ASPN ActiveState Programmer Network
ActiveState
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups


Recent Messages
List Archives
About the List
List Leaders
Subscription Options

View Subscriptions
Help

View by Topic
ActiveState
.NET Framework
Open Source
Perl
PHP
Python
Tcl
Web Services
XML & XSLT

View by Category
Database
General
SOAP
System Administration
Tools
User Interfaces
Web Programming
XML Programming


MyASPN >> Mail Archive >> php-dev
php-dev
Re: [PHP-DEV] [PATCH] [RFC] Closures and lambda functions in PHP
by Christian Seiler other posts by this author
Jun 18 2008 1:16PM messages near this date
RE: [PHP-DEV] [PATCH] [RFC] Closures and lambda functions in PHP | Re: [PHP-DEV] [PATCH] [RFC] Closures and lambda functions in PHP
Hi Andi, Hi Stanislav,

> > - You mention "global" and "static" as examples of how we do things
> > today. They are actually not good examples because the binding by
> > reference which they do has been a real pain over the years. This
> > is why we introduced the $GLOBALS[] array so that you could also
> > assign by reference ($GLOBALS["foo"] =& $var). Now that I think of
> > this example I'd actually prefer to see $LEXICALS[] or something
> > similar to access variables then go with the broken global/static
> > behavior. This will bite us and people will complain... In general,
> > I always recommend to people to keep away from "global" and go with
> > "$GLOBALS[]".

>  The problem here might be that if we do something like $LEX[$foo] in
>  runtime, we don't know which parts of parent's scope we need to
>  preserve. While I like the syntax, it may not work this way.

Yes, that's the point. 'lexical $foo' does two things (instead of global
simply doing one thing):

  a) At compile time (!) remember the name of the variable specified
     in an internal list assigned to the function. Example:

     function () {
       lexical $data, $i;
     }

     This will cause op_array-> lexical_names to be the list "data", "i".

  b) At run time, the lexical keyword creates a reference to the
     lexical variables that are stored in op_array-> lexical_variables
     (just as global does with global scope)

The op_array-> lexical_variables itself is filled in the new opcode which
is executed upon *assignment* (read: creation) of the closure. It's
essentially a for loop that goes through op_array-> lexical_names and
adds a reference from the current symbol table to the
op_array-> lexical_variables table.

So, to make an example (with line numbers for reference):

1   $data = "foo";
2   $i = 4;
3   $func = function () {
4     lexical $data, $i;
5     return array ($data, $i);
6   };
7
8   $func ();

Step 1 (Line 4 at compile time): op_array-> lexical_names is set to
"data", "i".

Step 2 (Line 3 at run time): The ZEND_DECLARE_LAMBDA_FUNC opcode is
executed, it creates a copy of the op_array to store in the return
value, in the copy it initializes the hash table
op_array-> lexical_variables and then creates two new variables in
op_array-> lexical_variables which are references to the current scope
varialbes $data and $i:

  +---------------+                  +-------------------------+
  | lex_variables |                  | EG(active_symbol_table) |
  +---------------+      ref         +-------------------------+
  | data    ------|------------------|->  data                  |
  | i       ------|------------------|->  i                     |
  |               |                  |   func                  |
  |               |                  |   ...                   |
  +---------------+                  +-------------------------+

Step 3 (Line 8 at run time): The closure is executed.

Step 4 (Line 4 at run time): The lexical keyword retrieves the $data and
$i variables from op_array-> lexical_variables and adds a reference to
them:

  +-------------------------+      +---------------+      +-------------+
  | EG(active_symbol_table) |      | lex_variables |      | parent s.t. |
  +-------------------------+      +---------------+      +-------------+
  | data            --------|------|->  data     ---|------|-> data      |
  | i               --------|------|->  i        ---|------|-> i         |
  |                         |      |               |      |   func      |
  |                         |      |               |      |   ...       |
  +-------------------------+      +---------------+      +-------------+

Btw: The grammar for lexical_variable contains only T_VARIABLE (and not
${...} etc.) on purpose - to be sure the name can be extracted at
compile time.

(Just as a clarification how the patch internally works.)

Frankly, I don't really see a problem with using references. It fits
into what's already there in PHP and it assures that closures have the
necessary properties to make them useful.

>  Which brings me to another point - how bad would it be if closure's
>  lifetime would be limited to parent's lifetime? Of course, this would
>  limit some tricks, but would allow some other - like this direct
>  access to parent's scope.

That "trick" would actually completely destroy the concept of closures:
The idea behind closures is that the lexical scope during *creation* of
the closure is saved. If you say "I want direct access via $LEXICALS"
then the lexical scope during the *execution* of the closure will be
used (yeah sure, the scope will be the scope during the creation of the
closure but the *state* of that scope will be the scope during execution
and not creation - "unbinding" variables after defining the closure (and
therefore for example loops) will not be possible at that point).

Furthermore, the idea that the closure lives longer than the scope in
which it was declared is one of the other most basic ideas behind
closures. Also, consider this code:

function foo () {
   $lambda = function () { echo "Hello World!\n"; };
   $lambda ();
   return $lambda;
}

$lambda = foo ();
$lambda (); # What should happen here?

That would be a *major* WTF in my eyes... You return something that was
perfectly valid inside the function WITHOUT CHANGE TO IT and just
because you leave the function it becomes invalid?

Personally, I don't like the idea of dumping two essential concepts of
closures just because using variable references may seem a bit of a pain
in some way.

> > - Minor implementation suggestion: I am not sure we need those
> > flags for closures and have those if() statements before function
> > calls. We took the same approach with other obfuscated
> > functions/methods/variables. If the developer *really* wants to
> > cheat the engine and assemble an obfuscated name then he can. It's
> > like doing the following in C: ((fun(*)()) 0x454544)(). I say, be
> > my guest. It just simplifies implementation a bit. No biggy but
> > consistent with the rest of PHP.

Personally, I do like to catch all possible errors, even if they don't
matter that much. But if you think this is superflous, I can remove it.

Granted, if called directly without being a closure, all lexical
variables will be NULL, so it doesn't really represent a problem.

> > - Please check eval(). I assume it will bind to global scope but
> > let's just make sure what happens esp. when it's called from within
> > a method...

Hmm, closures inside eval() will bind variables to the scope in which
eval() was called. But closures defined inside eval will NOT be class
methods, even if eval() is called within a class.

But I do find that behaviour consistent with what PHP currently does
with normal functions and variables: If eval()'d or include()'d inside a
function, variables will the "global scope" of eval() or the included
file will actually be the local function scope whereas defined functions
inside will automatically become global functions.

Of course, this behaviour should be documented but I don't see a reason
to try and change it.

> > - In PHP 5, object storage is resources done right. I don't think
> > we should be using the resource infrastructure for this
> > implementation and would prefer to use the object one. It's better.
> > I suggest to take a look at it.

Hmm, seems like a good idea. If nobody objects in the next few days,
I'll rewrite my patch to use objects instead of resources. What class
name do you suggest?

Regards,
Christian

PS: Somebody made me aware of a segfault in my code when destroying the
closure variable while still inside the closure. I'll fix that.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Thread:
Christian Seiler
Wez Furlong
Dmitry Stogov
Stanislav Malyshev
Alexander Wagner
Dmitry Stogov
Andi Gutmans
Christian Seiler
Lukas Kahwe Smith
Sebastian Bergmann
Marcus Boerger
Markus Fischer
Troels Knak-Nielsen
Christian Seiler
Dmitry Stogov
Larry Garfield
Christian Seiler
Dmitry Stogov
Christian Seiler
Lars Strojny
Stanislav Malyshev
Marcus Boerger
Lars Strojny
Troels Knak-Nielsen
Larry Garfield
Marcus Boerger
Dmitry Stogov
Andi Gutmans
Alexander Wagner
Andi Gutmans
Alexander Wagner
Alexander Wagner
Christian Seiler
Alexander Wagner
Lars Strojny
Dmitry Stogov
Marcus Boerger
Lars Strojny
Dmitry Stogov
Alexey Zakhlestin
Federico Lebron
Dmitry Stogov
Rodrigo Saboya
lenar
Larry Garfield
Stanislav Malyshev
Marcus Boerger
Alexander Wagner
Lars Strojny
Larry Garfield
Robert Cummings
Rodrigo Saboya
Alexander Wagner
Christian Seiler
Chris Stockton
Alexander Wagner
Troels Knak-Nielsen
Andi Gutmans
Marcus Boerger
Christian Seiler
Lukas Kahwe Smith
Gwynne Raskind
Andi Gutmans
Christian Seiler
Stanislav Malyshev
Kalle Sommer Nielsen
Troels Knak-Nielsen
Lars Strojny
Alexander Wagner
Stanislav Malyshev
Alexander Wagner
Andi Gutmans
Marcus Boerger
Kalle Sommer Nielsen
Troels Knak-Nielsen
Stanislav Malyshev
Alexey Zakhlestin
Chris Stockton
Alexey Zakhlestin
Gwynne Raskind
Stanislav Malyshev
Christian Seiler
Gwynne Raskind
Stanislav Malyshev
Richard Quadling
Christopher Jones
Marcus Boerger
Steph Fox
Christian Seiler
Marcus Boerger
Stanislav Malyshev
Lars Strojny
Christian Seiler
Stanislav Malyshev
Marcus Boerger
Marcus Boerger
Andrei Zmievski
Stanislav Malyshev
Stanislav Malyshev
Alexey Zakhlestin
Chris Stockton
Christian Seiler
Christian Seiler
Larry Garfield
Edward Z. Yang
Christian Seiler
Larry Garfield
Christian Seiler
Nathan Nobbe
Christian Seiler
Alexey Zakhlestin
Larry Garfield
Philip Olson

Privacy Policy | Email Opt-out | Feedback | Syndication
© ActiveState Software Inc. All rights reserved