php closures and lambda functions rfc

There is a discussion going on in php land about introducing closures and lambda functions, there was even a discussion on haskell-cafe about it (chx strikes again ;). About time, I would say. Having this functionality is a bonus. Having it implemented badly or half-arsed is going to do more damage than help. This is a short summary of what do I understand from the rfc and what do I think about it.

Anonymous functions aka lambda functions

The proposed syntax is

function & (parameters) { body }

No automatic variable capture. That is no variables from the parent scope are visible in the lambda function. So far so good, this is consistent with the default behaviour of php functions - you need to use global (in the process of becoming deprecated) or $GLOBALS to gain access to the global variables.

Closures and the newly proposed lexical keyword

That is something I'm not sure about. Although it is consistent with the current usage of global, with the combination of lambda functions it seems like a redundant syntax noise. The only explanation for me is to keep the runtime simpler, by requiring the explicit imports. Fair enough, although this verbosity does bug me a lot. It imports the variable by reference, similar to global. The consistency is a good thing. But consider the following trivial example:

for( $i = 1;  $i < 10;  $i++ ) 
  $a[] = function() { lexical $i; print $i;};
....
//What will it print?
$a[9]();  

As I understand the proposal and php (I might be wrong) the result of all of the $a[...]() functions will be 10. Which is definitely not what is intended - we can't refer to the same code in different contexts. But this is one of the most common uses for closures - capture the current state for later use! Erm. Yeah, sure, you could possibly work around it with explicit copy, but why? You lose update, true, but when do you actually need update? Which case is more 'natural'?

Can't one do something like:

$i = 1;
//highly illegal, i'll get locked up for this
$lambda = function ( $i &= $i ) { $i = 10; };

when you want to be able to update the parent variable? True &= is ugly. It will make the closures patch far more intrusive. It is true that the same trick - using defaults for arguments in a lambda will work the other way around. The code based on the rfc.

$i = 1;
for( $i = 1; $i < 10; $i++ ) {
  $lambdas['ref'][] = function () { lexical $i; print $i; };
  $lambdas['copy'][] = function ( $i = $i ) { print $i; };
}
//prints 9
$lambdas['ref'][3]();
//prints 3
$lambdas['copy'][3]();

But what is the more common use, especially existing exaples from other languages, and what is more natural in php? Least surprise?

The proposed interaction with classes reinforces my feeling of a half-baked proposal. Why automatically import $this, while not doing it for other variables. IMHO using references rather than copies is not thought through properly. Why do you need to make the $this automatic? Why do you need to use the class/object part of the scope, and make the anonymous function essentially an anonymous method, while not importing the whole scope and prune only the used parts at compile or run time? Why not something like:


class someclass {
   function one( ) {
       return function () { lexical $this; ... }
   }
   function another( ) {
       //errrhhm - this might cause problems... 
       //since the inner $this is a copy, not a reference...
       return function ( $this = $this ) { ... }
   }
}

Will it cause too much trouble to alpha convert a lambda, and leave it as simple as possible? Turning

$i = 1;
$x = function( $y ) { lexical $i; ... }

at compile time into the equivalent

$i = 1;
function lambda_xxxxgenname( $y, $i = $i ) { lexical $i; }

. Unfortunately this implies copy semantics of lexical and will have unexpected effects with variable function arguments use.

The problem probably lies with how do you do it in a way so that it doesn't change existing php semantics. I don't grok the php internals enough to actually do it, but I feel these questions are important.

OK, it is an RFC, now it is the time to discuss. It is obvious that there will be a need for a compromise, since changing too much will make a majour mess in an already delicate runtime.

As a side note, proper lambdas will help clean up the phptemplate trickery in drupal. Now, it does some hairy acrobatic acts to achieve somewhat similar functionality.

update: added the side by side comparison code for the copy versus reference(using the current rfc) semantics (not sure about the default arguments part)

And a challenge - can you define the Y combinator in php with lambdas & closures?

the patch is now officially

the patch is now officially in php 5.3 and head

I decided to have a go at

I decided to have a go at this, the first go is here

Powered by Drupal, an open source content management system