Transcript - SlideBoom

Lambda Functions & Closures
A Sydney PHP Group Presentation
2nd October 2008
By Timothy Chandler
Lambda Functions
– Lambda Calculus
 Lambda functions originate from lambda calculus
which was introduced by Alonzo Church and Stephen
Cole Kleene in the 1930s.
 The lambda calculus can be thought of as an idealized,
minimalistic programming language. It is capable of
expressing any algorithm, and it is this fact that
makes the model of functional programming an
important one.
 The lambda calculus provides the model for functional
programming. Modern functional languages can be
viewed as embellishments to the lambda calculus.
Lambda Functions
– Implementations
 Implementing the lambda calculus on a computer involves
treating "functions" as “first-class objects”, which raises
implementation issues for stack-based programming
languages. This is known as the Funarg problem – More
on this later.
 Many languages implement lambda functions. These
include:
 Python
 C++
 C# (2 different implementations – Second one improved in C# v3.0)
 JavaScript
 ...and many more...
Lambda Functions
– Implementation Examples
 Python:
Python Lambda Function
func = lambda x: x ** s2
 C++:
C++ Lambda Function
s
std::for_each(c.begin(), c.end(), std::cout
<< _1 * _1 << std::endl);
Lambda Functions
– Implementation Examples
 C#:
C# Lambda Function
//Declare a delegate signature
delegate double MathDelegate(double i);
//Create a delegate instance
MathDelegate lambdaFunction = delegate(doublesi) { return Math.Pow(i, 2); };
/* Passing ' lambdaFunction ' function variable to another method, executing, and returning the
result of the function */
double Execute(MathDelegate lambdaFunction) { return lambdaFunction(100); }
 C# v3.0:
C# v3.0 Lambda Function
//Create an delegate instance
MathDelegate lambdaFunction = i => i * si;
Execute(lambdaFunction);
Lambda Functions
– Implementation Examples
 JavaScript:
JavaScript Lambda Function
var lambdaFunction=function(x)
{
return x*10;
}
document.write(lambdaFunction(100));
s
Lambda Functions
– The PHP Way
 PHP 5.3:
PHP 5.3 Lambda Function
<?php
$lambdaFunction=function($x)
{
s
return $x*10;
};
print $lambdaFunction(100);
?>
 Syntax:
Lambda Function Syntax
s vars) { body };
function & (parameters) use (lexical
Lambda Functions
– The PHP Way
 The goal of PHP’s Lambda function implementation is to
allow for the creation of quick throw-away functions.
 Don’t confuse with “create_function()”.
 These functions compile at “run-time”.
 These functions DO NOT compile at “compile-time”.
 Optcode caches CANNOT cache them.
 Bad practice.
Closures
 A closure is a function that is evaluated in an
environment containing one or more bound
variables. When called, the function can access these
variables.
 Closures provide a very useful tool for making lambda
functions more useful.
Closures
– The Funarg Problem
 Lambda functions MUST be first-class objects.
 Funarg, meaning “functional argument”, is a problem
in computer science where a “stack-based
programming language” has difficulty implementing
functions as “first-class objects”.
 The problem is when the body of a function refers to a
variable from the environment that it was created but
not the environment of the function call.
 Standard Solutions:
 Forbid such references.
 Create closures.
Closures
– The PHP Funarg Problem Solution
 PHP 5.3 introduces a new keyword ‘use’.
 Use this new keyword when creating a lambda function to define what
variables to import into the lambda functions scope – This creates a
Closure.
Closures
– The “use” Keyword
 Example:
Lambda Function Closure
$config=array('paths'=>array('examples'=>'c:/php/projects/examples/'));
$fileArray=array('example1.php','example2.php','exampleImage.jpg');
$setExamplesPath=function($file) use($config)
s
{
return $config['paths']['examples'].$file;
};
print_r(array_map($setExamplesPath,$fileArray) );
 Result:
Array
(
)
[0] => c:/php/projects/examples/example1.php
[1] => c:/php/projects/examples/example2.php
[2] => c:/php/projects/examples/exampleImage.jpg
Closures
– The “use” Keyword
 Example:
Lambda Function Closure – As an Anonymous Function
$config=array('paths'=>array('examples'=>'c:/php/projects/examples/'));
$fileArray=array('example1.php','example2.php','exampleImage.jpg');
print_r(array_map
(
function($file) use($config) s
{
return $config['paths']['examples'].$file;
},
$fileArray
));
 Result:
Array
(
)
[0] => c:/php/projects/examples/example1.php
[1] => c:/php/projects/examples/example2.php
[2] => c:/php/projects/examples/exampleImage.jpg
Closures
– “use” as reference or copy
 Variables passed into the “use” block are copied in by
default – This is the expected PHP behaviour.
 You can cause a variable to be imported by reference
the same way you do when defining referenced
parameters in function declarations.
 The PHP 5 pass by reference for objects rule still
applies.
Closures
– “use” by reference
 Example:
Referenced Variable Import
$config=array('paths'=>array('examples'=>'c:/php/projects/examples/'));
$fileArray=array('example1.php','example2.php','exampleImage.jpg');
$setExamplesPath=function($file) use(&$config)
{
return $config['paths']['examples'].$file;
};
s
print_r(array_map($setExamplesPath,$fileArray));
 Why?
 Able to directly affect the variable from within the lambda function.
 If used with a large array, can prevent massive overheads.
 Memory efficient.
Lifecycle
 A lambda function can be created at any point in your application, except in class
declarations.
 Example:
Lambda Function in Class Declaration
class foo
{
public $lambda=function()
{
return 'Hello World';
};
public function __construct()
{
print $this->lambda();
}
}
new foo();
s
 Throws Error:
Parse error: syntax error, unexpected T_FUNCTION in
D:\Development\www\php5.3\lambda\5.php on line 4
Lifecycle
 Lambda functions can live longer than whatever created them.
 Example:
Lifecycle Example 1
class foo
{
public $lambda=null;
public function __construct()
{
$this->lambda=function() {return 'Hello World';};
}
}
$foo=new foo();
var_dump($foo);
$lambda=$foo->lambda;
unset($foo);
var_dump($foo);
print $lambda();
s
 Result:
object(foo)#1 (1) { ["lambda"]=> object(Closure)#2 (0) { } }
NULL
Hello World
Lifecycle
 Imported variables can also live longer.
 Example:
Lifecycle Example 2
//Create prefix say function.
$say=function($prefix)
{
return function($suffix) use(&$prefix)
{
print $prefix.$suffix;
};
};
//Create suffix say function - will loose $prefix right?
$say=$say('Hello ');
//Wrong! - Execute new say concatenated function.
$say('World!'); //Outputs "Hello World!" <$prefix><$suffix>
s
 Result:
Hello World
Lifecycle – Objects
 Methods and properties used in a closure can live longer than the object.
 Example:
Lifecycle Example 3
class foo
{
public $bar="Bar\r\n";
public function __construct(){print "__construct()\r\n“;}
public function __destruct(){print "__destruct()\r\n“;}
public function getBarLambda()
{
return function(){return $this->bar;};
}
}
$foo=new foo();
$bar=$foo->getBarLambda();
print $bar();
unset($foo);
var_dump($foo);
print $bar();
unset($bar);
print $bar();
s
 Result:
__construct()
Bar
NULL
Bar
__destruct()
Fatal error Function name must be a string in D:\Development\www\php5.3\lambda\8.php on line 31
Lifecycle – Objects
 If a closure exists with a reference to an object’s
method or property, that object is not completely
destroyed when unset.
 __destruct() is NOT called until the closure is
destroyed.
 The unset object CANNOT be used in this situation as it
will be considered a null value by anything trying to
access it outside the closure environment.
Object Orientation
 Lambda Functions are Closures because they
automatically get bound to the scope of the class that
they are created in.
 $this is not always needed in the scope.
 Removing $this can save on memory.
 You can block this behaviour by declaring the Lambda
Function as static.
Object Orientation
 Example:
Static Lambda Functions
class foo
{
public function getLambda()
{
return function(){var_dump($this);};
}
public function getStaticLambda()
s
{
return static function(){var_dump($this);};
}
}
$foo=new foo();
$lambda=$foo->getLambda();
$staticLambda=$foo->getStaticLambda();
$lambda();
$staticLambda();
 Result:
object(foo)#1 (0) { }
NULL
Object Orientation
 PHP 5.3 introduces a new magic method.
 Invokable objects are now possible through the use of
the __invoke() magic method.
 Essentially makes the object a closure.
Object Orientation
 Example:
Invokable Objects
class foo
{
public function __invoke()
{
print 'Hello World';
}
}
$foo=new foo;
$foo();
 Result:
Hello World
s
Questions?
Thank you.
References
http://en.wikipedia.org/wiki/Lambda_calculus
http://en.wikipedia.org/wiki/Closure_(computer_science)
http://en.wikipedia.org/wiki/Funarg_problem
http://wiki.php.net/rfc/closures