Showing posts with label lambda. Show all posts
Showing posts with label lambda. Show all posts

Saturday, April 25, 2015

Closures - PHP vs Javascript

In my previous post Exploring Lambda Functions and Closures in PHP, I gave you example of function that can calculate factorial of given number:

$factorial = function( $n ) use ( &$factorial ) {
    if( $n == 1 ) return 1;
    return $factorial( $n - 1 ) * $n;
};

echo $factorial( 5 ); // 120

In that post I simply said it is important to pass $factorial by reference otherwise it won't work. Let's explore why it is so in this post.

Closures are pretty common in Javascript so let's create a simple closure in it and see how it behaves:

var addOne = function(num) {
    return function() { 
        console.log(num++); 
    };
};

var f = addOne(1);

f(); // 1
f(); // 2
f(); // 3

As can be seen, each time we call the function, it increments the number. Let's now convert the same in PHP:

$addOne = function($num) {
    return function() { 
        echo $num++; 
    };
};

$f = $addOne(1);

$f();
$f();
$f();

This would result in error:

Notice: Undefined variable: num

Why is it so ? Because variable scope is different in PHP and Javascript. In PHP, all variables go in local scope by default unless you explicitly declare them in global scope. However in Javascript all variables go in global scope unless you explicitly declare them in local scope using var keyword. Since we are talking about functions and closures here, we can say that in PHP a variable declared in a function can only be available to that function where it is declared. On the other hand, in Javascript a variable can not only be available to function where it is declared but also to inner functions of that parent function. Let's see an example:

function test() {
  $variable = 'value';

  return function() {
    echo $variable;
  };
}

$f = test();
$f(); // Notice: Undefined variable: variable

So PHP gave error as inner function didn't had access to outer variable $variable. We could have made this variable available to inner function either by creating closure using use keyword or declaring that variable in global scope. Le't see same example in Javascript:

function test() {
  var variable = 'value';

  return function() {
    console.log(variable);
  };
}

var f = test();
f(); // value

In Javascript though, the inner function was able to get variable from outer scope eg parent function in this example.

This is reason why Javascript inner function shown above didn't give error whereas PHP's did. Therefore to have access to outer variable in PHP, we need to create a closure by using the use keyword. Of course in Javascript, you don't need something like use keyword because as I mentioned in Javascript a variable of a parent function is available to all its inner functions as well. So we need to use the use keyword in PHP due to difference of variable scoping in PHP and Javascript.

So let's fix that error we received earlier in PHP:

$addOne = function($num) {
    return function() use ($num) { 
        echo $num++; 
    };
};

$f = $addOne(1);

$f(); // 1
$f(); // 1
$f(); // 1

Now we have created the closure and error is gone and $num variable is now available to inner function but there is a problem, each time we call the function it says 1 unlike Javascript example we saw earlier which said 1, 2 and 3. That's because unlike Javascript, PHP creates a new local variable inside inner function eg passes it by value not by reference. Conceptually, PHP sees this code like this:

$addOne = function($num) {
    return function() use ($num) { 
        $num = 1;  // so PHP kind of creates a new variable and assigns to 1 each time you call this
        echo $num++; 
    };
};

So to get similar output as in Javascript, we need to pass the variable using reference which can be done by using the & symbol:

$addOne = function($num) {
    return function() use (&$num) { // passed by reference 
        echo $num++; 
    };
};

$f = $addOne(1);

$f();
$f();
$f();

and now we get the output 1, 2, and 3.


And that's also reason why you needed to pass variable by reference for the factorial example I had given in my previous post. Hope this post clears the confusion of closures and scoping especially for someone who comes from Javascript to PHP.

Sunday, April 19, 2015

Exploring Lambda Functions and Closures in PHP

What is a Lambda Function

A lambda function (also known as anonymous function) is a function that has:

  1. No Name
  2. Can be assigned to a variable
  3. Can be passed as argument to other functions or methods of a class

In the past, this was possible with create_function like:

$multiply = create_function('$a, $b','return $a * $b;');
echo $multiply(5, 5); // 25

But it was a nuisance if you needed to write longer anonymous functions using create_function because it was difficult to write function body that way inside quotes and then escaping quotes and stuff...

PHP 5.3 introduced true support of lambda/anonymous functions similar to how you see in Javascript, so you could write above code like:

$multiply = function ($a, $b) {
    return $a * $b;
};

echo $multiply(5, 5); // 25

As can be seen, above anonymous function has no name and has been assigned to variable $multiply. You can also pass it other functions:

function countEmails($number) {
    echo 'you have ' . $number . ' emails!';
}

$multiply = function ($a, $b) {
    return $a * $b;
};

echo countEmails($multiply(2, 5)); // you have 10 emails!

You could have also written above like this if you wanted:

function countEmails($multiply, $a, $b) {
    echo 'you have ' . $multiply($a, $b) . ' emails!';
}

$multiply = function ($a, $b) {
    return $a * $b;
};

echo countEmails($multiply, 2, 5); // you have 10 emails!

And even like this:

function countEmails($multiply, $a, $b) {
    echo 'you have ' . $multiply($a, $b) . ' emails!';
}

echo countEmails(function ($a, $b) {
    return $a * $b;
}, 2, 5); // you have 10 emails!

You can also assign it to array:

$array['multiply'] = function ($a, $b) {
     return $a * $b;
};

echo $array['multiply'](2, 10); // 20

And even an object:

$obj = new StdClass();

$obj->multiply = function ($a, $b) {
     return $a * $b;
};

But caveat here is that instead of calling it like echo $obj->multiply(2, 10), you would instead need to do:

$multiply = $obj->multiply;
echo $multiply(2, 10); // 20

Now let's create little program which says Hello, World using anonymous function:

$message = 'Hello, World!';

$sayHello = function () {
    echo $message;
};

$sayHello();

Oops, it results in error:

Notice: Undefined variable: message

It means anonymous function does not have access to $message inside it. This is exactly when we need a closure to have access to that $message variable.

What is a Closure

A closure is a lambda/anonymous function that is aware of its surrounding context through the use of use keyword. It can access a variable outside of the scope where it is defined. Consider last $sayHello anonymous function and we convert it into closure through the use of use keyword and gain access to the $message variable:

$message = 'Hello, World!';

$sayHello = function () use ($message) {
    echo $message;
};

$sayHello(); // Hello, World!

Thanks to closure (use keyword), we are now able to access $message variable!


If you need to change the value of passed outside variable, you need to use reference of course:

$message = 'Hello, World!';

$sayHello = function () use (&$message) {
    echo $message;
    $message = 'Hello World Again!';
};

$sayHello(); // Hello, World!
$sayHello(); // Hello World Again!

You can also pass regular arguments to closure:

$message = 'World!';

$sayHello = function ($arg) use ($message) {
    echo $arg . ' ' . $message;
};

$sayHello('Hello'); // Hello, World!

You can also create a recursive function using closure easily, here is example of how you can create a factorial function:

$factorial = function( $n ) use ( &$factorial ) {
    if( $n == 1 ) return 1;
    return $factorial( $n - 1 ) * $n;
};

echo $factorial( 5 );

Notice that $factorial is passed by reference using & otherwise it won't work.


The difference between an anonymous function and closure is that closure has ability to access a variable outside of its scope by using the use keyword. So that is the subtle difference between an anonymous function and a closure. In fact, both are of them are instances of Closure class internally:

// anonymous function
$multiply = function ($a, $b) {
    return $a * $b;
};

$message = 'Hello, World';

// closure
$sayHello = function () use ($message) {
    echo $message;
};

var_dump ($multiply instanceof Closure); // true
var_dump ($sayHello instanceof Closure); // true

Type Hinting

We know that you can already type hint few things:

  • Object
  • Array
  • Interface
  • callable

But you can also type hint Closure as shown below:

function multiply($number, Closure $closure)
{
    return $closure($number);
}

$closure = function($x){ return $x * 2; };
echo multiply(10, $closure);

Here we have made multiply function to require type of Closure as second parameter.

Use Cases

Anonymous functions and closures can be used in variety of situations.

As Callbacks

You can use them in your custom functions as callback or some of the PHP's built-in functions such as array_map(), array_reduce(), array_filter(), array_walk(), etc. Let's take example of array_walk(). If you look at it's definition, here is how it looks:

bool array_walk ( array &$array , callable $callback [, mixed $userdata = NULL ] )

Notice the second parameter callable $callback. It means it expects something to be callable function. Anytime you see such parameter in some function definition, it means you can apply anonymous functions to it. Here is example:

$myArray = array(1, 2, 3, 4, 5);

// multiply each array value with 2    
array_walk($myArray, function(&$value, $index){
    $value *= 2;
});

print_r($myArray);

Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 8
    [4] => 10
)

In Routing

You might have seen closures being used in routing in framework like Laravel, Slim, Silex, etc:

$app = new \Slim\Slim($options);

$app->get('/', function () use ($app) {
    $app->render('home', array('content' => 'Hello, World!!!'));
});

Here $app is imported from outside scope into the scope of closure so that it can be used to render the view $app->render(...)


Accessing Private Members of a Class

We can use bind() or bindTo methods of Closure class to access private data of some class, for example:

class MyClass {
    private $variable = 'I am private variable!';
}

$closure = function() {
    return $this->variable;
};

$result = Closure::bind($closure, new MyClass(), 'MyClass');
echo $result(); // I am private variable!

There you see, we were able to get value of private variable $variable.

Similarly, it is also possible to add new behaviour to a class without actuallly modifying it direclty. Pretty cool hun ?


Lazy-loading a Class

Another ^cool^ thing you can do with closure is to lazy-load a class. For example:

class MyClass {
    public function __construct() {
        echo 'I am initialized!';
    }
}

$getMyClass = function() {
    $myClass = new MyClass();
    return $myClass;
};

If you run above code, you might expect to see I am initialized! message because one might think we are creating an instance of MyClass. That's not true though, because code inside $getMyClass anonymous function is not run until you actually call it:

$myClass = $getMyClass();

And now you would see the message I am initialized!. So this is pretty neat trick to defer some piece of code and use it only when you need it.

Popular Posts