Implicit Evaluation with PHP

30 January 2007

PHP as a Lambda Language

PHP is an imperative language which supports object-oriented programming. However, it supports a number of paradigms closer to what you would expect Lisp or Scheme to allow. I’ve discussed parts of this before, but its time to put together a definitive primer.

Scheme has one of the shorts languages definitions of any language, ever. Without getting into details of it (like macros or scoping) let’s consider the basics of the language:

I would be wrong if I claimed these were all the basics of the language, but it is a large enough palette to create most other functionality.

Largely because of the blurred line between lambda and any other built-in operations, coupled with the non-imperative paradigm caused inline functions to become a symbol of the language. A function might return a function as the result of its being called. For instance,

(define adder (lambda (a) (lambda (b) (+ a b))))

While a simple arithmetic operation is hardly indicative of efficiency, by returning a function designed by its inputs, certain efficiencies become possible that are otherwise not.

PHP is an imperative language. As imperative languages go, PHP is incredibly loosely typed. Concepts like callback functions are hardly unique to PHP, however, PHP loses much of their formality. In C, you have a function pointer. Scheme does not have pointers, as a pointer is a variable, however all variables are in effect pointers behind the scenes. In PHP, references exist but lacking functions as a data type, they are useless. Instead, PHP supports variable variables and variable functions, which I call implicit evaluation. However, PHP also provides create_function, which is closer to real lambda functions. While implicit evaluation gives you convenient ways to access and determine code paths at run time, create_function lets you create code at run time.

create_function does not give you real functions. They are not capable of recursion. They are best described as C’s function pointers. If you lose the reference to one of these functions, it cannot be invoked.

function adder ($left) {
$fA = create_function(’$x’,'return (’ . $left . ‘ + $x);’);
return $fA;
}
$f = adder(5);
echo $f(10);

Like the above Scheme example, this code creates an optimized function according to its inputs. The Scheme code, however, does not have a discrete evaluation layer like the PHP does, which is why PHP’s lambda functions cannot be recursive. They are unaware of the outside scope or even that it is a function. Although it has a name (var_dump($f); gives string(9) "lambda_1") it does not exist in the function namespace and it cannot be invoked without some form of the the original result of create_function.

That’s not to say it’s useless. It can be quite necessary to maintain an object-oriented structure to your programs. Lambda-like functions like array_map are well suited to the results of create_function. All of PHP.net’s examples show the callback as a variable function, but it is more elegant to use an anonymous function.

$cube = create_function('$i', 'return $i * $i * $i;');
$a = array(1, 2, 3, 4, 5);
$b = array_map($cube, $a);
print_r($b);

Create_function avoids polluting the namespace, breaking the object-oriented paradigm and avoids the awkward syntax of object-oriented callbacks. It also enabled non-obvious uses of PHP functions like array_map or usort.

PHP is not Scheme or even a LISP-like language. It’s just not. But many of the features these languages afford you is possible in PHP. Tail recursion is possible through implicit evaluation. Anonymous functions are possible through create_function. Primitive list-processing is built into the language. While features like register_globals do dilute the language, it remains much more capable then its shallow shell would suggest.

7 Comments currently posted.

Larry says:

Very interesting, I had no idea you could do that in PHP. Please write more about this secret functional side of the language.

AlmostAlive says:

It should also be pointed out that create_function() is not garbage collected. Create a lot of functions in a script and you are going to use a lot of memory.

metapundit says:

AlmostAlive:
>It should also be pointed out that create_function() is not garbage collected. Create a lot of functions in a script and you are going to use a lot of memory.

Bingo. I recently blogged about a way around this in part - I find that the main thing I’m using create_function for is to pass to array_map and friends. Usually there is a function that already does almost what I want and I’m using the “anonymous” function as a wrapper to hold some arguments. For this common case I implemented partial function application and am using an object (which is GC) instead of an “anonymous” function which is not… Check it out http://metapundit.net/sections/blog/partial_function_application_in_php

Erling says:

>Although it has a name (var_dump($f); gives string(9) “lambda_1″) it does not exist in the function namespace and it cannot be invoked without some form of the the original result of create_function.

This is not strictly true — the name of the function just starts with a null byte:

create_function(”, ‘print “Hello world”;’);
$b=”\0lambda_1″;
$b();

Ick!

Peter Goodman says:

You might be interested with my experiments with implementing closures/lambdas into PHP and the problems/solutions I used to do it.

http://ioreader.com/2007/05/03/php-closures/
http://ioreader.com/2007/05/06/php-closures-update/

The update article is more recent and has lambda syntax improvements. One thing I wanted to avoid was using eval, and I also wanted to allow proper function scoping without depending on global variables. I accomplished this with the next worst thing: using a singleton stack for scope and compiling each lambda into normal php.

Peter Goodman says:

One thing I forgot to mention is why I didn’t use create_function(). For doing lambda-like functions, this seems like the obvious solution, but in bulk it becomes *very* slow. It’s a function I generally try to stay away from.

Peter Goodman says:

I just thought up another way to achieve lambda-like functions in php. I drew up a quick mock up using create_function and variable composition at http://ioreader.com/2007/06/18/hacking-variable-composition-another-approach-to-php-lambdas/

Here’s how it turns out:

${lambda(’$a’, ‘echo $a;’)}(’hello world’);

and that outputs ‘hello world’. :)

Post a comment on this entry: