[PHP-DEV] PATCH: anonymous functions in PHP

6 views
Skip to first unread message

Wez Furlong

unread,
Mar 18, 2007, 7:44:54 PM3/18/07
to
We've been daydreaming about the ability to do something like this in
PHP:

$data = array("zoo", "orange", "car", "lemon", "apple");
usort($data, function($a, $b) { return strcmp($a, $b); });
var_dump($data); # data is sorted alphabetically

In the past, people have been told to use the travesty that is
create_function() to emulate this kind of behavior, well, it turns
out to be a minor patch to the parser to pull that into the core.

You can find my prototype patch at http://pastebin.ca/400871 (against
PHP_5_2)

The way it works is by making the expression:

function() {}

evaluate to the (generated) name of the anonymous function.

$foo = function() {};

sets $foo to a string like "__zend_anon_1", which can then be passed
around as a callback name, just like the way that create_function()
works, except that you don't need to use crazy quoting to declare any
kind of moderately complex function.

There's one minor flaw in my implementation for ZTS enabled systems
(just need to move the anon function counter into CG() to solve that.

So, the question is, do we want this in PHP?

--Wez.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Jan Lehnardt

unread,
Mar 18, 2007, 7:57:33 PM3/18/07
to
On Mon 19 Mar 2007, at 19 Mar 00:41, Wez Furlong wrote:
> So, the question is, do we want this in PHP?

yes, please.

Jan
--

Wez Furlong

unread,
Mar 18, 2007, 8:34:15 PM3/18/07
to
I found another flaw; when used in a loop it keeps trying to declare
the same function over and over. I think this is because the
ZEND_DECLARE_FUNCTION opcode is emitted as part of the arg list
building op sequence.

I'm poking to find an elegant way to fix that.

--Wez.

On Mar 18, 2007, at 7:41 PM, Wez Furlong wrote:

> You can find my prototype patch at http://pastebin.ca/400871
> (against PHP_5_2)

> There's one minor flaw in my implementation for ZTS enabled systems

> (just need to move the anon function counter into CG() to solve that.

--

Jim Wilson

unread,
Mar 18, 2007, 8:49:02 PM3/18/07
to
------=_Part_69570_26017896.1174265129361
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

> > So, the question is, do we want this in PHP?
>
> yes, please.

Anonymous function declaration is one of the things I've always loved in
Ruby and JavaScript - I for one would _love to see this_ in php.

-- Jim R. Wilson (jimbojw)

------=_Part_69570_26017896.1174265129361--

Gwynne

unread,
Mar 18, 2007, 9:30:52 PM3/18/07
to
On Mar 18, 2007, at 8:48 PM, Wez Furlong wrote:
> Updated patch at http://pastebin.ca/400952
> Not 100% sure if my hack in zend_compile.c is righteous, but it
> doesn't seem too far wrong.

74. + if (!memcmp(opline-
>op2.u.constant.value.str.val, "__zend_anon_", sizeof
("__zend_anon_")-1)) {

Pardon my nitpicking, but shouldn't this be:

74. + if (!memcmp(opline-
>op2.u.constant.value.str.val, "__zend_anon_", strlen
("__zend_anon_")-1)) {

Also, a strong +1 for this patch, I'd love to see this support in PHP.

-- Gwynne, Daughter of the Code
"This whole world is an asylum for the incurable."

Wez Furlong

unread,
Mar 18, 2007, 9:34:21 PM3/18/07
to
Your nitpicking happens to be wrong ;-)

sizeof("string constant") is the "same" as strlen("string constant")
+1, but is resolved at compile time, so we use sizeof("string
constant")-1 to get a compile time evaluated strlen(). This trick is
used throughout the PHP internals.

--Wez.

Stanislav Malyshev

unread,
Mar 18, 2007, 10:10:23 PM3/18/07
to
> $data = array("zoo", "orange", "car", "lemon", "apple");
> usort($data, function($a, $b) { return strcmp($a, $b); });
> var_dump($data); # data is sorted alphabetically

What happens if you do this:

$data = array("zoo", "orange", "car", "lemon", "apple");

$rev = 1;
usort($data, function($a, $b) { return $rev?strcmp($a, $b):!strcmp($a,

$b); });
var_dump($data); # data is sorted alphabetically

This works in Javascript (probably Ruby too), but quite hard to make
work in PHP because $rev is in different scope.
Moreover, would it mean that this:
$f = function($a, $b) { return $rev?strcmp($a, $b):!strcmp($a, $b); }
would work too? Keeping right value of $rev?
--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

Gwynne

unread,
Mar 18, 2007, 10:17:07 PM3/18/07
to
On Mar 18, 2007, at 9:30 PM, Wez Furlong wrote:
> Your nitpicking happens to be wrong ;-)
>
> sizeof("string constant") is the "same" as strlen("string constant")
> +1, but is resolved at compile time, so we use sizeof("string
> constant")-1 to get a compile time evaluated strlen(). This trick
> is used throughout the PHP internals.

Ah. I've never seen it used that way before; I apologize for my
ignorance :). In my experience, sizeof() on a character constant
would evaluate as sizeof( const char * const ).


> On Mar 18, 2007, at 9:27 PM, Gwynne wrote:
>>> Updated patch at http://pastebin.ca/400952
>>> Not 100% sure if my hack in zend_compile.c is righteous, but it
>>> doesn't seem too far wrong.
>>
>> 74. + if (!memcmp(opline-
>> >op2.u.constant.value.str.val, "__zend_anon_", sizeof
>> ("__zend_anon_")-1)) {
>>
>> Pardon my nitpicking, but shouldn't this be:
>>
>> 74. + if (!memcmp(opline-
>> >op2.u.constant.value.str.val, "__zend_anon_", strlen
>> ("__zend_anon_")-1)) {
>>
>> Also, a strong +1 for this patch, I'd love to see this support in
>> PHP.

-- Gwynne, Daughter of the Code
"This whole world is an asylum for the incurable."

--

Wez Furlong

unread,
Mar 18, 2007, 10:22:31 PM3/18/07
to
I didn't make it do anything fancy with scoping; it would make the
implementation more complicated, and wouldn't fit so well with the
way that scoping works in PHP, in that you need to explicitly
reference the global scope to "break out" of your function scope.

It would be cool if the lexical scope was inherited, but maybe not
cool enough to warrant making it work :)

--Wez.


On Mar 18, 2007, at 10:06 PM, Stanislav Malyshev wrote:

>> $data = array("zoo", "orange", "car", "lemon", "apple");
>> usort($data, function($a, $b) { return strcmp($a, $b); });
>> var_dump($data); # data is sorted alphabetically
>
> What happens if you do this:
>
> $data = array("zoo", "orange", "car", "lemon", "apple");
> $rev = 1;
> usort($data, function($a, $b) { return $rev?strcmp($a, $b):!strcmp
> ($a, $b); });
> var_dump($data); # data is sorted alphabetically
>
> This works in Javascript (probably Ruby too), but quite hard to
> make work in PHP because $rev is in different scope.
> Moreover, would it mean that this:
> $f = function($a, $b) { return $rev?strcmp($a, $b):!strcmp($a, $b); }
> would work too? Keeping right value of $rev?
> --
> Stanislav Malyshev, Zend Products Engineer
> st...@zend.com http://www.zend.com/
>

--

Stanislav Malyshev

unread,
Mar 19, 2007, 1:47:28 AM3/19/07
to
> I didn't make it do anything fancy with scoping; it would make the
> implementation more complicated, and wouldn't fit so well with the way
> that scoping works in PHP, in that you need to explicitly reference the
> global scope to "break out" of your function scope.
>
> It would be cool if the lexical scope was inherited, but maybe not cool
> enough to warrant making it work :)

Well, making it work makes this thing closure. Otherwise it's just a
nice way to save a couple of keystrokes :) Not to diminish your work,
but there's a danger people would think it is closure because it looks
like one (i.e., in other languages closures look exactly this way, e.g.
Javascript).

Marcus Boerger

unread,
Mar 19, 2007, 3:52:58 AM3/19/07
to
Hello Gwynne,

Monday, March 19, 2007, 3:13:28 AM, you wrote:

> On Mar 18, 2007, at 9:30 PM, Wez Furlong wrote:
>> Your nitpicking happens to be wrong ;-)
>>
>> sizeof("string constant") is the "same" as strlen("string constant")
>> +1, but is resolved at compile time, so we use sizeof("string
>> constant")-1 to get a compile time evaluated strlen(). This trick
>> is used throughout the PHP internals.

> Ah. I've never seen it used that way before; I apologize for my
> ignorance :). In my experience, sizeof() on a character constant
> would evaluate as sizeof( const char * const ).

Actually it is not a "const char *" here. Instead the language generates
a "const char[]" which works asexpected.

Best regards,
Marcus

Marcus Boerger

unread,
Mar 19, 2007, 3:58:56 AM3/19/07
to
Hello Wez,

interesting solution. Nice work:-)

Monday, March 19, 2007, 1:48:31 AM, you wrote:

> Updated patch at http://pastebin.ca/400952
> Not 100% sure if my hack in zend_compile.c is righteous, but it
> doesn't seem too far wrong.

> --Wez.

David Zülke

unread,
Mar 19, 2007, 4:42:47 AM3/19/07
to
Am 19.03.2007 um 00:41 schrieb Wez Furlong:

> We've been daydreaming about the ability to do something like this
> in PHP:
>

> $data = array("zoo", "orange", "car", "lemon", "apple");
> usort($data, function($a, $b) { return strcmp($a, $b); });
> var_dump($data); # data is sorted alphabetically
>

> So, the question is, do we want this in PHP?

Oh yes. Please. Please. Please :)


David

Antony Dovgal

unread,
Mar 19, 2007, 5:22:52 AM3/19/07
to
On 03/19/2007 02:41 AM, Wez Furlong wrote:
> We've been daydreaming about the ability to do something like this in
> PHP:

I don't have any objections, the only requirement from me is that it should be covered by tests as much as possible.
So I would like to encourage people to write tests to support the patch.
If all the people saying "yes, please" write a test case and send it to the list, the coverage would be pretty good, I guess.

Oh, and of course we can't include it into 5.2, it's only for HEAD (and maybe for 5.3, that's up to Ilia).

--
Wbr,
Antony Dovgal

Christian Schneider

unread,
Mar 19, 2007, 5:24:06 AM3/19/07
to
Stanislav Malyshev wrote:
> Well, making it work makes this thing closure. Otherwise it's just a
> nice way to save a couple of keystrokes :) Not to diminish your work,
> but there's a danger people would think it is closure because it looks
> like one (i.e., in other languages closures look exactly this way, e.g.
> Javascript).

Wez' proposal sounds very intriguing. At the same time I wouldn't try to
add closures to PHP as while they might be a mighty concept they are
also complicated and have a high WTF factor IMHO. When I first started
doing more complex things in Javascript I was bitten more than once by
closures: Sometimes they did what I expected and sometimes they didn't
(e.g. when using 'this').

But I agree that it would be great to being able to pass context to the
generated function, preferably not via a global variable.

One possible solution would be to generate the function as a method of
the current object and expose this so something like
usort($data, function($a, $b) { return $this->cmp($a, $b); });
could be used.

- Chris

Sebastian Bergmann

unread,
Mar 19, 2007, 5:43:16 AM3/19/07
to
Wez Furlong wrote:
> So, the question is, do we want this in PHP?

Yes, please. (It might even make my userland implementation of of CLOS-
style generic functions easier.)

--
Sebastian Bergmann http://sebastian-bergmann.de/
GnuPG Key: 0xB85B5D69 / 27A7 2B14 09E4 98CD 6277 0E5B 6867 C514 B85B 5D69

Jacob Santos

unread,
Mar 19, 2007, 9:06:52 AM3/19/07
to
Hey, thanks, I was researching this exact same thing. You make it appear much
easier than it is. You've just saved me many months, weeks, and hours trying to
figure out how to mess with the internals of the Zend Engine. For that, I am
most grateful.

Also, I would love to see this in PHP, at the very least it would negate the
argument of those who say PHP doesn't support anonymous functions.

Jacob Santos

> Updated patch at http://pastebin.ca/400952
> Not 100% sure if my hack in zend_compile.c is righteous, but it doesn't
> seem too far wrong.
>
> --Wez.

--

Jon Parise

unread,
Mar 19, 2007, 11:19:41 AM3/19/07
to
On Sun, Mar 18, 2007 at 08:48:31PM -0400, Wez Furlong wrote:

> Updated patch at http://pastebin.ca/400952

This is interesting. I'm not sure I'll ever use it (I prefer named
functions), but it's a purely optional and folks seem to want
something like this.

One comment on the patch itself:

static unsigned int anon_count;

I think you should provide an initial value (0?) for this static
counter. Also, you should protect against wrap-around (which, while
improbably, is possible).

Alternatively, you could change the anonymous function naming scheme
to something like __zend_anon_FILE_LINE_COLUMN, but that could be an
unnecessary waste of string memory.

--
Jon Parise (jon of php.net) :: The PHP Project (http://www.php.net/)

Sean Coates

unread,
Mar 19, 2007, 2:34:47 PM3/19/07
to
> Well, making it work makes this thing closure. Otherwise it's just a
> nice way to save a couple of keystrokes :) Not to diminish your work,
> but there's a danger people would think it is closure because it looks
> like one (i.e., in other languages closures look exactly this way, e.g.
> Javascript).

I think this is the key to this whole discussion.

I doubt PHP will ever be able to fully support JavaScript- and
Lisp-style closures.

To take Stas' example to another level:

function foo()
{
$bar = function () { echo "bar"; }
return $bar;
}
$bar = foo();
$bar(); // does this work?

According to our current current syntax:
function foo()
{
$bar = create_function('', 'echo "bar";');
return $bar;
}
$bar = foo();
$bar(); // this does work

I don't know Lisp very well at all, but in JavaScript, functions are
general containers, and this sort of thing works fine, albeit VERY
differently. PHP's scoping rules are already completely different from
JavaScript's (there's no way to access the parent scope unless it
happens to be the global scope).

I strongly prefer Wez's syntax for anonymous function declaration. It
would help on many levels, from readability to syntax-highlighting, to
optimization (maybe...).

On optimization, the question becomes "how does Wez's proposal tokenize?"

--------Old:
T_OPEN_TAG : <?php
T_VARIABLE : $bar
T_WHITESPACE :
=
T_WHITESPACE :
T_STRING : create_function
(
T_CONSTANT_ENCAPSED_STRING : ''
,
T_WHITESPACE :
T_CONSTANT_ENCAPSED_STRING : 'echo "bar";'
)
;
--------New:
T_OPEN_TAG : <?php
T_VARIABLE : $bar
T_WHITESPACE :
=
T_WHITESPACE :
T_FUNCTION : function
(
)
T_WHITESPACE :
{
T_WHITESPACE :
T_ECHO : echo
T_WHITESPACE :
T_CONSTANT_ENCAPSED_STRING : "bar"
;
T_WHITESPACE :
}
;

If the answer is "New", then this could be compiled at.. well,
compile-time, not at execute time. That could be even more interesting.

(sorry for the long post.. most of it's code (-; )

S

Stanislav Malyshev

unread,
Mar 19, 2007, 2:51:38 PM3/19/07
to
> If the answer is "New", then this could be compiled at.. well,
> compile-time, not at execute time. That could be even more interesting.

If it would create anonymous function compile-time, it would be a big
advantage to Wez's patch because then this function could be cached.
Thinking about this, maybe it is the reason enough to do this even if
it's not real closure.


--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Sean Coates

unread,
Mar 19, 2007, 3:28:49 PM3/19/07
to
> If it would create anonymous function compile-time, it would be a big
> advantage to Wez's patch because then this function could be cached.
> Thinking about this, maybe it is the reason enough to do this even if
> it's not real closure.

On mulling this over a bit more, other than quick one-off callbacks
(which would definitely benefit from avoiding compile on every request),
one of the key reasons to use create_function() is actually to create
dynamic functions:

$fancyVer = create_function('', 'return "PHP " . phpversion();');
// could be optimized as:
$fancyVer = create_function('', 'return "PHP ' . phpversion() .'";');

(the latter only calls phpversion() at declaration, not each time the
lambda runs)

Since phpversion() is available globally, this isn't a problem. But what
happens if we want to use a variable, instead?

$ver = phpversion();
$fancyVer = create_function('', "return 'PHP $ver';");
// this currently works ^

How would this be rewritten, though?
$ver = phpversion();
$fancyVer = function () { return "PHP $ver"; };

where would $ver come from? the parent scope? the lambda's local scope?
what if it's defined in both places?

S

Wez Furlong

unread,
Mar 19, 2007, 3:37:12 PM3/19/07
to
I've been thinking about this on and off today too.
Something along the lines of the following is more in the PHP spirit:

$ver = phpversion();
$fancyVer = function () { lexical $ver; return "PHP $ver"; };

Where "lexical" is a keyword that means "inherit this variable from
the current lexical scope". I'm not suggesting that this is a good
name for the keyword, it's just something that springs to mind.

So, given some way to explicitly reference the scope where the
function was "defined", what happens when you call $fancyVer after
that scope has gone away:

function doSomething() {
$ver = phpversion();
return function () { lexical $ver; return "PHP $ver"; };
}
$func = doSomething();
$func(); # the doSomething() scope (hash table) doesn't exist any more

This could perhaps be solved by taking a reference to $ver when the
function is bound, but I don't know enough about the ZE to understand
the implications of that; it would probably require a bit more state
tracking per zend_function so that we know that we need to do that
step during binding.

--Wez.

Sean Coates

unread,
Mar 19, 2007, 3:49:27 PM3/19/07
to
If we can solve the scoping problem (perhaps via references as you
mentioned), then lexical (or another keyword, to be debated endlessly
for months, whose name-debate will delay the implementation of this
functionality, but I digress...) seems like a good solution to grabbing
scope, and fits the "PHP Way", IMO.

> So, given some way to explicitly reference the scope where the function
> was "defined", what happens when you call $fancyVer after that scope has
> gone away:

This was my next question (-:

> This could perhaps be solved by taking a reference to $ver when the
> function is bound, but I don't know enough about the ZE to understand
> the implications of that; it would probably require a bit more state
> tracking per zend_function so that we know that we need to do that step
> during binding.

JavaScript (and I suspect other Lisp-like languages) solves this by
making the function an actual closure—the defined function maintains
access to the parent scope, even after the parent's hash table (or
however it works in JS) would have normally been destroyed.

I think the key thing to remember here is that JS is fundamentally
different from PHP. Functions are objects in JS, and they always have
access to variables from all parent scopes. I don't think PHP can (or
should) ever implement this.

I also don't know what I'm talking about when it comes to ZE internals,
so if I'm way off base, feel free to put me in line (-:

(I maintain that JS' wonky (though useful) scoping rules should never be
assimilated in PHP.)

Stanislav Malyshev

unread,
Mar 19, 2007, 4:18:56 PM3/19/07
to
> I've been thinking about this on and off today too.
> Something along the lines of the following is more in the PHP spirit:
>
> $ver = phpversion();
> $fancyVer = function () { lexical $ver; return "PHP $ver"; };
>
> Where "lexical" is a keyword that means "inherit this variable from the
> current lexical scope". I'm not suggesting that this is a good name for
> the keyword, it's just something that springs to mind.

How this is going to work? Variables are not interpreted by the compiler
now...

> So, given some way to explicitly reference the scope where the function
> was "defined", what happens when you call $fancyVer after that scope has
> gone away:

Exactly! That's why it is hard to do closures in PHP :)

> This could perhaps be solved by taking a reference to $ver when the
> function is bound, but I don't know enough about the ZE to understand
> the implications of that; it would probably require a bit more state

$ver would not even exist when we compile it - $ver appears in run-time
and we want function to be created in compile-time!

--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Richard Lynch

unread,
Mar 19, 2007, 4:25:31 PM3/19/07
to

On Sun, March 18, 2007 6:41 pm, Wez Furlong wrote:
> We've been daydreaming about the ability to do something like this in
> PHP:
>
> $data = array("zoo", "orange", "car", "lemon", "apple");
> usort($data, function($a, $b) { return strcmp($a, $b); });
> var_dump($data); # data is sorted alphabetically

I'd LOVE it if there was SOME difference between this and a normal
'function' definition...

I guess we're kind of stuck with 'create_function' being the mess that
it is.

But perhaps something like 'temp_function' or 'local_function' or
'lexical_closure' or something similar. Even 'horse' [*] would be
fine by me.

I think it muddies things too much to have it just be 'function' with
no name after it.

+1

* [sings] "In the desert, on a horse with no name"

--
Some people have a "gift" link here.
Know what I want?
I want you to buy a CD from some starving artist.
http://cdbaby.com/browse/from/lynch
Yeah, I get a buck. So?

Richard Lynch

unread,
Mar 19, 2007, 4:27:21 PM3/19/07
to
On Sun, March 18, 2007 7:30 pm, Wez Furlong wrote:
> I found another flaw; when used in a loop it keeps trying to declare
> the same function over and over. I think this is because the
> ZEND_DECLARE_FUNCTION opcode is emitted as part of the arg list
> building op sequence.
>
> I'm poking to find an elegant way to fix that.

Would the function body/arglist be allowed to change based on the loop
data? [shudder]

It's starting to sound like Lisp :-)

Though I guess if you need anonymous functions, you probably need them
with varying bodies as well.

Robert Cummings

unread,
Mar 19, 2007, 4:37:52 PM3/19/07
to
On Mon, 2007-03-19 at 13:15 -0700, Stanislav Malyshev wrote:
> > I've been thinking about this on and off today too.
> > Something along the lines of the following is more in the PHP spirit:
> >
> > $ver = phpversion();
> > $fancyVer = function () { lexical $ver; return "PHP $ver"; };
> >
> > Where "lexical" is a keyword that means "inherit this variable from the
> > current lexical scope". I'm not suggesting that this is a good name for
> > the keyword, it's just something that springs to mind.
>
> How this is going to work? Variables are not interpreted by the compiler
> now...
>
> > So, given some way to explicitly reference the scope where the function
> > was "defined", what happens when you call $fancyVer after that scope has
> > gone away:
>
> Exactly! That's why it is hard to do closures in PHP :)

What about just having a function that allows retrieving variables from
the parent scope?

mixed seek_var( $name [, $levels=1, [ $startLevel=0 ] ] )

Returns the the value of the variable with name $name in the
current of parent lexical scopes. By default the seek will
only search for the variable in the current and immediate
parent scopes but this can be tailored to meet any particular
need including searching to the top by setting $levels to -1.
Additionally by setting $startLevel greater than 0, the
search can be confined to scopes outside of the current
lexical scope. If the request variable is not found then
E_NOTICE is generated and null is returned.

Cheers,
Rob.
--
.------------------------------------------------------------.
| InterJinn Application Framework - http://www.interjinn.com |
:------------------------------------------------------------:
| An application and templating framework for PHP. Boasting |
| a powerful, scalable system for accessing system services |
| such as forms, properties, sessions, and caches. InterJinn |
| also provides an extremely flexible architecture for |
| creating re-usable components quickly and easily. |
`------------------------------------------------------------'

Robert Cummings

unread,
Mar 19, 2007, 4:38:50 PM3/19/07
to
On Mon, 2007-03-19 at 15:20 -0500, Richard Lynch wrote:
> On Sun, March 18, 2007 6:41 pm, Wez Furlong wrote:
> > We've been daydreaming about the ability to do something like this in
> > PHP:
> >
> > $data = array("zoo", "orange", "car", "lemon", "apple");
> > usort($data, function($a, $b) { return strcmp($a, $b); });
> > var_dump($data); # data is sorted alphabetically
>
> I'd LOVE it if there was SOME difference between this and a normal
> 'function' definition...
>
> I guess we're kind of stuck with 'create_function' being the mess that
> it is.
>
> But perhaps something like 'temp_function' or 'local_function' or
> 'lexical_closure' or something similar. Even 'horse' [*] would be
> fine by me.
>
> I think it muddies things too much to have it just be 'function' with
> no name after it.
>
> +1

A function with a name is no longer anonymous ;)

Wez Furlong

unread,
Mar 19, 2007, 4:45:21 PM3/19/07
to
On Mar 19, 2007, at 4:15 PM, Stanislav Malyshev wrote:

> How this is going to work? Variables are not interpreted by the
> compiler now...
>

Well, the compiler would make a list of variables names to import and
store those in the zend_function structure. Then at the time the
function is bound (in response to a DECLARE_FUNCTION opcode), the
variable reference could be fixed up in the "same" way that global is
handled.

--Wez.

Richard Lynch

unread,
Mar 19, 2007, 5:02:28 PM3/19/07
to
On Mon, March 19, 2007 3:35 pm, Robert Cummings wrote:
> On Mon, 2007-03-19 at 15:20 -0500, Richard Lynch wrote:
>> On Sun, March 18, 2007 6:41 pm, Wez Furlong wrote:
>> > We've been daydreaming about the ability to do something like this
>> in
>> > PHP:
>> >
>> > $data = array("zoo", "orange", "car", "lemon", "apple");
>> > usort($data, function($a, $b) { return strcmp($a, $b); });
>> > var_dump($data); # data is sorted alphabetically
>>
>> I'd LOVE it if there was SOME difference between this and a normal
>> 'function' definition...
>>
>> I guess we're kind of stuck with 'create_function' being the mess
>> that
>> it is.
>>
>> But perhaps something like 'temp_function' or 'local_function' or
>> 'lexical_closure' or something similar. Even 'horse' [*] would be
>> fine by me.
>>
>> I think it muddies things too much to have it just be 'function'
>> with
>> no name after it.
>>
>> +1
>
> A function with a name is no longer anonymous ;)

I am not suggesting that there be a name.

I am suggesting that the reserved keyword for an anonymous function
should not be 'function', the same as a normal function.

--
Some people have a "gift" link here.
Know what I want?
I want you to buy a CD from some starving artist.
http://cdbaby.com/browse/from/lynch
Yeah, I get a buck. So?

--

Stanislav Malyshev

unread,
Mar 19, 2007, 5:03:24 PM3/19/07
to
> What about just having a function that allows retrieving variables from
> the parent scope?
>
> mixed seek_var( $name [, $levels=1, [ $startLevel=0 ] ] )

How you are going to know where "parent scope" is? It can even be not
existing anymore, or can be separated by any number of parameter
passing. Remember that the function is defined at compile-time, but
invoked at run-time by some other function.

--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Stanislav Malyshev

unread,
Mar 19, 2007, 5:09:08 PM3/19/07
to
> Well, the compiler would make a list of variables names to import and
> store those in the zend_function structure. Then at the time the
> function is bound (in response to a DECLARE_FUNCTION opcode), the
> variable reference could be fixed up in the "same" way that global is
> handled.

Global is not handled this way. Global creates reference at runtime
(i.e. when function is *executed*) to runtime variable in global space.
However, binding to compile scope can't work as globals do - since
binding should happen at "definition run-time" - e.g., when the function
definition is used. Adding binding capabilities to DECLARE_FUNCTION
might work though, but it is not clear what to do in case this is
declared in a loop (then it can't really be done compile-time unless we
clone this function each time). It must be also considered how these
values are added to the function symbol table when the function runs -
e.g. are they references or copies?

Robert Cummings

unread,
Mar 19, 2007, 5:16:55 PM3/19/07
to
On Mon, 2007-03-19 at 13:59 -0700, Stanislav Malyshev wrote:
> > What about just having a function that allows retrieving variables from
> > the parent scope?
> >
> > mixed seek_var( $name [, $levels=1, [ $startLevel=0 ] ] )
>
> How you are going to know where "parent scope" is? It can even be not
> existing anymore, or can be separated by any number of parameter
> passing. Remember that the function is defined at compile-time, but
> invoked at run-time by some other function.

I guess I was primarily thinking in the context of the anonymous
function being defined in your previous example. As such the parent
scope is known (or at least can be expected), unless (unknown to me)
within the internal C code the scope can change other than the hierarchy
one would see visually while writing PHP code. From your example:

On Sun, 2007-03-18 at 19:06 -0700, Stanislav Malyshev wrote:
>
> What happens if you do this:

> $data = array("zoo", "orange", "car", "lemon", "apple");

> $rev = 1;
> usort($data, function($a, $b) { return $rev?strcmp($a, $b):!

strcmp($a,
$b); });
> var_dump($data); # data is sorted alphabetically
>

We would have:

<?php

$data = array("zoo", "orange", "car", "lemon", "apple");

$rev = 1;
usort($data, function($a, $b) { return seek_var( 'rev' )?strcmp($a,
$b):!strcmp($a,

$b); });
var_dump($data); # data is sorted alphabetically

?>

Can we not expect that the lexical scope of the $rev is the immediate
parent of the anonymous function at runtime? Obviously this could also
work for normally defined functions if there's an expectation of the
calling environment having some specific variable defined.

Cheers,
Rob.
--
.------------------------------------------------------------.
| InterJinn Application Framework - http://www.interjinn.com |
:------------------------------------------------------------:
| An application and templating framework for PHP. Boasting |
| a powerful, scalable system for accessing system services |
| such as forms, properties, sessions, and caches. InterJinn |
| also provides an extremely flexible architecture for |
| creating re-usable components quickly and easily. |
`------------------------------------------------------------'

--

Stanislav Malyshev

unread,
Mar 19, 2007, 5:20:47 PM3/19/07
to
>
> I guess I was primarily thinking in the context of the anonymous
> function being defined in your previous example. As such the parent
> scope is known (or at least can be expected), unless (unknown to me)

It is known in compile-time. But functions are not called in
compile-time. And in run-time, inside usort() - which could in other
case be some kind of foo_sort() passing its second parameter to a dozen
of other functions, one of which uses it to invoke comparator function -
you can not know where the scope in which usort was called is with
regard to the scope of the executing anonymous function. And if you took
this function name and saved it in the variable - it could be that the
scope it was defined in does not exist anymore.


--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Robert Cummings

unread,
Mar 19, 2007, 5:28:54 PM3/19/07
to
On Mon, 2007-03-19 at 14:17 -0700, Stanislav Malyshev wrote:
> >
> > I guess I was primarily thinking in the context of the anonymous
> > function being defined in your previous example. As such the parent
> > scope is known (or at least can be expected), unless (unknown to me)
>
> It is known in compile-time. But functions are not called in
> compile-time. And in run-time, inside usort() - which could in other
> case be some kind of foo_sort() passing its second parameter to a dozen
> of other functions, one of which uses it to invoke comparator function -
> you can not know where the scope in which usort was called is with
> regard to the scope of the executing anonymous function. And if you took
> this function name and saved it in the variable - it could be that the
> scope it was defined in does not exist anymore.

I'm not arguing the preservation of the exact value of $rev when the
anonymous function was created (as would be the case with a closure).
I'm thinking of the variable being whatever is defined in the parent
regardless. As in my proposal, the seek could search all the way up to
the top in which case the $rev would be found if it had been defined as
expected. At any rate, I guess this is diverging from the real
discussion of anonymous functions :)

Cheers,
Rob.
--
.------------------------------------------------------------.
| InterJinn Application Framework - http://www.interjinn.com |
:------------------------------------------------------------:
| An application and templating framework for PHP. Boasting |
| a powerful, scalable system for accessing system services |
| such as forms, properties, sessions, and caches. InterJinn |
| also provides an extremely flexible architecture for |
| creating re-usable components quickly and easily. |
`------------------------------------------------------------'

--

Richard Lynch

unread,
Mar 19, 2007, 5:29:54 PM3/19/07
to
I think the anonymous name having metadata about the __FILE__ __LINE__
__COLUMN__ would be pretty nifty for error messages and debuggers...

I'm a bit tired of seeing "Error: blah blah in Unknown on line: 0"
personally. :-)

This presumes somebody would take the effort to de-construct that
metadata for a better error message, but better to have it available
and possible to be done someday than "impossible"

--
Some people have a "gift" link here.
Know what I want?
I want you to buy a CD from some starving artist.
http://cdbaby.com/browse/from/lynch
Yeah, I get a buck. So?

--

Wez Furlong

unread,
Mar 19, 2007, 5:35:34 PM3/19/07
to
My implementation preserves this information.

--Wez.

Richard Lynch

unread,
Mar 19, 2007, 5:38:33 PM3/19/07
to
On Mon, March 19, 2007 2:33 pm, Wez Furlong wrote:
> I've been thinking about this on and off today too.
> Something along the lines of the following is more in the PHP spirit:
>
> $ver = phpversion();
> $fancyVer = function () { lexical $ver; return "PHP $ver"; };
>
> Where "lexical" is a keyword that means "inherit this variable from
> the current lexical scope". I'm not suggesting that this is a good
> name for the keyword, it's just something that springs to mind.

I believe "Environment" is what it was called back in CLOS when we
schlepped it around as an extra extra argument to functions that
needed data from outside function scope...

> So, given some way to explicitly reference the scope where the
> function was "defined", what happens when you call $fancyVer after
> that scope has gone away:
>

> function doSomething() {
> $ver = phpversion();
> return function () { lexical $ver; return "PHP $ver"; };
> }
> $func = doSomething();
> $func(); # the doSomething() scope (hash table) doesn't exist any more
>

> This could perhaps be solved by taking a reference to $ver when the
> function is bound, but I don't know enough about the ZE to understand
> the implications of that; it would probably require a bit more state

> tracking per zend_function so that we know that we need to do that
> step during binding.

This gets incredibly complex not only to figure out what to do in the
PHP source, but for scripters to figure out what the heck it does...

If it's making YOUR head spin, what will it do to the poor unwashed
masses?...

Obviously, an anonymous function is going to be inherently complex --
but keep it as simple as it can be.

I suspect that you could get away with JUST using global and/or static
as they exist now, not introduce yet another scoping keyword, and
anything that *NEEDS* to be done with an anonymous function can be
done.

Does anybody really *NEED* a variable whose scope is non-global and
captured at the time of the func definition, carried over beyond the
scope of that until it's executed?...

Somehow I think you're just complicating it "because you can" rather
than because anybody really NEEDS this.

You have to have a pretty esoteric function to need that kind of scope
control, no?

Stanislav Malyshev

unread,
Mar 19, 2007, 5:39:48 PM3/19/07
to
> I think the anonymous name having metadata about the __FILE__ __LINE__
> __COLUMN__ would be pretty nifty for error messages and debuggers...
>
> I'm a bit tired of seeing "Error: blah blah in Unknown on line: 0"
> personally. :-)

I'd say if it's compiled then the compiler would put there file and line
numbers too, just as for the regular function.


--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Robert Cummings

unread,
Mar 19, 2007, 5:42:38 PM3/19/07
to
On Mon, 2007-03-19 at 14:34 -0700, Stanislav Malyshev wrote:
> > regardless. As in my proposal, the seek could search all the way up to
> > the top in which case the $rev would be found if it had been defined as
>
> The top of *what*? Anonymous function could be called from global scope too.

Then it has 1 level to travel to the top since it's own scope isn't the
global scope.

Cheers,
Rob.
--
.------------------------------------------------------------.
| InterJinn Application Framework - http://www.interjinn.com |
:------------------------------------------------------------:
| An application and templating framework for PHP. Boasting |
| a powerful, scalable system for accessing system services |
| such as forms, properties, sessions, and caches. InterJinn |
| also provides an extremely flexible architecture for |
| creating re-usable components quickly and easily. |
`------------------------------------------------------------'

--

Sean Coates

unread,
Mar 19, 2007, 5:57:13 PM3/19/07
to
> I'm not arguing the preservation of the exact value of $rev when the
> anonymous function was created (as would be the case with a closure).
> I'm thinking of the variable being whatever is defined in the parent
> regardless. As in my proposal, the seek could search all the way up to
> the top in which case the $rev would be found if it had been defined as
> expected. At any rate, I guess this is diverging from the real
> discussion of anonymous functions :)

If you've ever tried to hack on the source to anything that you didn't
write that also uses globals throughout (Gallery2, for example), you
know how hard it is to track down a single layer of scope that can be
defined [somewhere else].

Tracking this up the chain like a bubbled exception sounds like a
nightmare for debugging/comprehension, and IMO it's too magical.

One layer of scope might be useful: $_PARENT ?
More than that, and my head starts to hurt.

S

Stanislav Malyshev

unread,
Mar 19, 2007, 5:58:36 PM3/19/07
to
> This gets incredibly complex not only to figure out what to do in the
> PHP source, but for scripters to figure out what the heck it does...

Welcome to the world of closures :) But in most cases people use it to
do pretty basic stuff (unless they are CS majors with too much time on
their hands ;) - like creating "dynamic anonymous functions" - basically
resulting in the same as what create_function does, and some currying -
which, for those who doesn't know it, is technique of partially
instantiating a multi-argument function - i.e. making something like:

$addtwo = function ($a) { return $a+2; }
$six = $addtwo(4);

or more frequently something like this:

$var = "foo";
$compare_to_var = function($s) { return (str_compare($var, $s)<0); }
$strings_less_than_var = array_filter($array, $compare_to_var);

which is really powerful technique for dynamic language, though can
easily lead one to lose all track of what is happening if overused. For
more confusion see http://en.wikipedia.org/wiki/Currying :)

> If it's making YOUR head spin, what will it do to the poor unwashed
> masses?...

Unwashed masses better not do it :) Actually many "washed" masses have
hard time to understand these things too.

> I suspect that you could get away with JUST using global and/or static
> as they exist now, not introduce yet another scoping keyword, and
> anything that *NEEDS* to be done with an anonymous function can be
> done.

I guess this depends on definition of "NEEDS".

> You have to have a pretty esoteric function to need that kind of scope
> control, no?

Depends on your background. Some people consider LISP intuitive language
:) And if you work with Javascript, you probably would be doing it daily
- most AJAX toolkits rely on heavy usage of anonymous functions. Not
sure it needs to be in PHP though. It might be cool thing, but it's
definitely becomes very confusing very fast.


--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Andi Gutmans

unread,
Mar 19, 2007, 6:03:50 PM3/19/07
to
We could compile the anonymous function at compile-time but leave
placeholders which need to be "fixed-up" when at run-time the actual
closure is created. For this purpose we could introduce a new magical
variables $_SCOPE["var"] which references the current $var during fixup
time.
So here's an example:

$var =3D php_version();
$fancyVer =3D function () { return "PHP $_SCOPE['ver']; };

So what this does is compile the "anonymous function" at compile-time,
but when this line is executed (note, this line of code should also be
reached during execution), then we do the fix-up for $_SCOPE variables.
Regular variables remain what they would in create_function(). Fix-up
time would be faster than compiling the code.

Thoughts?
Andi

Stanislav Malyshev

unread,
Mar 19, 2007, 6:07:29 PM3/19/07
to
> So what this does is compile the "anonymous function" at compile-time,
> but when this line is executed (note, this line of code should also be
> reached during execution), then we do the fix-up for $_SCOPE variables.

Yes, this is basically what Wez was proposing I guess. This would
require new hashtable for _SCOPE (and/or adding _SCOPE elements on
execution of anon-func line) in the function run-time data.

> Thoughts?

This might work, still not sure if we really want that in PHP. On one
side, it's cool, on other side closures can develop into a very messy
code :)


--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Lukas Kahwe Smith

unread,
Mar 19, 2007, 6:12:05 PM3/19/07
to
Stanislav Malyshev wrote:

>> Thoughts?
>
> This might work, still not sure if we really want that in PHP. On one
> side, it's cool, on other side closures can develop into a very messy
> code :)

Yeah well .. for the single task of callbacks they are great and prevent
a lot of messiness .. I think its messy to pollute the namespace for
something which is not intended for reuse .. maybe we need to
artificially limit its uses just like for "goto".

regards,
Lukas

Andi Gutmans

unread,
Mar 19, 2007, 6:26:33 PM3/19/07
to
Why would this pollute less? It would still pollute the namespace.

> -----Original Message-----
> From: Lukas Kahwe Smith [mailto:m...@pooteeweet.org]=20
> Sent: Monday, March 19, 2007 3:08 PM
> To: Stas Malyshev
> Cc: Andi Gutmans; Robert Cummings; Wez Furlong; Sean Coates;=20
> inte...@lists.php.net
> Subject: Re: [PHP-DEV] PATCH: anonymous functions in PHP
>=20
> Stanislav Malyshev wrote:
>=20
> >> Thoughts?
> >=20
> > This might work, still not sure if we really want that in=20
> PHP. On one=20
> > side, it's cool, on other side closures can develop into a=20
> very messy=20
> > code :)
>=20
> Yeah well .. for the single task of callbacks they are great=20
> and prevent a lot of messiness .. I think its messy to=20
> pollute the namespace for something which is not intended for=20
> reuse .. maybe we need to artificially limit its uses just=20
> like for "goto".
>=20
> regards,
> Lukas
>=20

Richard Lynch

unread,
Mar 19, 2007, 6:29:52 PM3/19/07
to
On Mon, March 19, 2007 4:55 pm, Stanislav Malyshev wrote:
>> You have to have a pretty esoteric function to need that kind of
>> scope
>> control, no?
>
> Depends on your background. Some people consider LISP intuitive
> language
> :) And if you work with Javascript, you probably would be doing it
> daily
> - most AJAX toolkits rely on heavy usage of anonymous functions. Not
> sure it needs to be in PHP though. It might be cool thing, but it's
> definitely becomes very confusing very fast.

I almost finished my MS in CS with Artificial Intelligence
concentration at DePaul, so I *was* one of those CS students with too
much time on my hands.

Then I worked at an AI think tank lab at Northwestern University for
most of a decade using Lisp.

I gotta say that while it was Really Nifty to know all about those
esoteric closures, curries, and whatnot I have since forgot, mostly, I
didn't need these things on a daily basis even at that job.

So, yes, somebody somewhere "needs" this, but is it the kind of thing
that belongs in PHP?

I think not.

By "this" I mean the ability to have the lexical closure carry its
scope around with itself.

I *WANT* to have a simple clean anonymous function syntax.

Can't you just take the body of "create_function" with the syntax of
the patch, and marry those two?

How does the current create_function handle this same scope issue?

--
Some people have a "gift" link here.
Know what I want?
I want you to buy a CD from some starving artist.
http://cdbaby.com/browse/from/lynch
Yeah, I get a buck. So?

--

Stanislav Malyshev

unread,
Mar 19, 2007, 6:37:08 PM3/19/07
to
> Can't you just take the body of "create_function" with the syntax of
> the patch, and marry those two?

Well, that'd be a bit hard since the whole difference is that
create_function is runtime (thus having access to run-time values) while
anon-func we are trying to do here is compile-time (at least it makes a
lot of sense to do it compile-time). We could of course just leave the
scope thing alone and say "want access to other scope? tough luck, use
create_function/eval or globals".

> How does the current create_function handle this same scope issue?

It doesn't AFAIK :) Since it's a close relative of eval, it can import
values of variables by inserting ones into function string, but nothing
more (i.e. you couldn't put an object there, and couldn't refer to a
variable - only insert it's value).

--
Stanislav Malyshev, Zend Products Engineer
st...@zend.com http://www.zend.com/

--

Sean Coates

unread,
Mar 19, 2007, 6:39:16 PM3/19/07
to
> Can't you just take the body of "create_function" with the syntax of
> the patch, and marry those two?

I already explained why this can't work, in this very thread. At least
not without hacking { and } to work (mostly) like " in this context.

> How does the current create_function handle this same scope issue?

create_function() takes a string. This string is interpolated at
runtime. There's an obvious distinction between local variables (they
look like \$something within the string, and parent vars: they look like
'. $var .' )

S

Richard Lynch

unread,
Mar 19, 2007, 6:51:34 PM3/19/07
to
On Mon, March 19, 2007 5:33 pm, Stanislav Malyshev wrote:
>> Can't you just take the body of "create_function" with the syntax of
>> the patch, and marry those two?
>
> Well, that'd be a bit hard since the whole difference is that
> create_function is runtime (thus having access to run-time values)
> while
> anon-func we are trying to do here is compile-time (at least it makes
> a
> lot of sense to do it compile-time). We could of course just leave the
> scope thing alone and say "want access to other scope? tough luck, use
> create_function/eval or globals".

I thought the original driving purpose was to not have the gnarly
quoting mess of create_function.

Yes, in the implementation, it turned out to be a compile-time closure.

Does that mean we really WANT to go there, just because it falls out
easily, or does that mean we should step back and look at the original
goal and see if maybe we've gone off-track?

>> How does the current create_function handle this same scope issue?
>

> It doesn't AFAIK :) Since it's a close relative of eval, it can import
> values of variables by inserting ones into function string, but
> nothing
> more (i.e. you couldn't put an object there, and couldn't refer to a
> variable - only insert it's value).

I'd be perfectly happy if PHP's "anonymous" functions had the nice
syntax and no funky weird stuff about closure, a la
eval/create_function, personally.

I don't need the closures, but when I need a quickie anon function in
PHP, I just want something less ugly than create_function.

That's just my personal preference, of course.

--
Some people have a "gift" link here.
Know what I want?
I want you to buy a CD from some starving artist.
http://cdbaby.com/browse/from/lynch
Yeah, I get a buck. So?

--

Robert Cummings

unread,
Mar 19, 2007, 7:08:10 PM3/19/07
to
On Mon, 2007-03-19 at 17:53 -0400, Sean Coates wrote:
> > I'm not arguing the preservation of the exact value of $rev when the
> > anonymous function was created (as would be the case with a closure).
> > I'm thinking of the variable being whatever is defined in the parent
> > regardless. As in my proposal, the seek could search all the way up to
> > the top in which case the $rev would be found if it had been defined as
> > expected. At any rate, I guess this is diverging from the real
> > discussion of anonymous functions :)
>
> If you've ever tried to hack on the source to anything that you didn't
> write that also uses globals throughout (Gallery2, for example), you
> know how hard it is to track down a single layer of scope that can be
> defined [somewhere else].
>
> Tracking this up the chain like a bubbled exception sounds like a
> nightmare for debugging/comprehension, and IMO it's too magical.
>
> One layer of scope might be useful: $_PARENT ?
> More than that, and my head starts to hurt.

Well I did originally suggest defaulting to the parent scope with
options for advanced users to climb higher :) Just because there are
unwashed masses as Richard Lynch likes to call it, doesn't mean we all
need to suffer their limitations.

Cheers,
Rob.
--
.------------------------------------------------------------.
| InterJinn Application Framework - http://www.interjinn.com |
:------------------------------------------------------------:
| An application and templating framework for PHP. Boasting |
| a powerful, scalable system for accessing system services |
| such as forms, properties, sessions, and caches. InterJinn |
| also provides an extremely flexible architecture for |
| creating re-usable components quickly and easily. |
`------------------------------------------------------------'

--

Christian Schneider

unread,
Mar 19, 2007, 7:09:52 PM3/19/07
to
Richard Lynch wrote:
> I'd be perfectly happy if PHP's "anonymous" functions had the nice
> syntax and no funky weird stuff about closure, a la
> eval/create_function, personally.

While the discussion about closures and how to emulate them was
interesting I think Richard hits the nail on the head.

1. Keep it simple as in Wez' patch.
2. Maybe make it return a callback array (object, method) if used with
class to be able to use $this.
3. Maybe add $_SCOPE a la Andi's proposal if you really think it's
worthwhile.

My two cents,
- Chris

Reply all
Reply to author
Forward
0 new messages