What's the difference between closure parameters and the 'use' keyword?

Seralize picture Seralize · May 21, 2012 · Viewed 7.2k times · Source

This has got me very confused and I can't seem to find an answer to this question. A clear and simple clarification would be nice.

Answer

goat picture goat · May 21, 2012

The use statement captures the variable at the time the closure function is created.

Regular function arguments capture the value when the function is called.

Note that I differentiated between variable and value there.

function makeAnAdder($leftNum) {
    // Notice that *each time* this makeAnAdder function gets called, we 
    // create and then return a brand new closure function.
    $closureFunc = function($rightNum) use ($leftNum) {
        return $leftNum + $rightNum;
    };

    return $closureFunc;
}

$add5to = makeAnAdder(5);
$add7to = makeAnAdder(7);

echo $add5to(10); // 15
echo $add7to(1); // 8

If there were a way to inspect the, um, "source code" of the $add5to function, it would look like this:

function($rightNum) {
    return 5 + $rightNum;
}

I guess you could kinda say the value of $leftNum got remembered by the closure function.

I want to further emphasize that the use statement allows you to maintain a reference to a variable, and not just a copy of the value that the variable had at some point. To clarify my idea: think of a variable as being a little box, which can contain a single value at any instant in time, and that value can be changed. And, you can make another variable point to that box, so that you can update the value in the box, or read the current value in it.

Normally, a local variable that is created within a function ceases to exist after the function returns. But, a closure function can maintain a reference to that variable, and cause that local variable to live on even after the function returns - and this is the true power of closure functions. It lets you mimic certain behaviors of a class (instance variables), with just a tiny bit of code.

Here's a more advanced example, that might take some deep thinking to understand the fine details of the behavior.

function makeBankAccount() {
    // Each time this makeBankAccount func is called, a new, totally
    // independent local variable named $balance is created.
    $balance = 0;

    // Also, on each call we create 2 new closure functions, $modifyBalance, and $getBalance
    // which will hold a reference to the $balance variable even after makeBankAccount returns.
    $modifyBalance = function($amount) use (&$balance) {
        $balance += $amount;
    };

    $getBalance = function() use (&$balance) {
        return $balance;
    };

    // return both closure functions.
    return ['modifyBalance' => $modifyBalance, 'getBalance' => $getBalance];
}

// Let's prove that bank1 works by adding 5 to the balance by using the first
// function, then using the other function to get the balance
// from the same internal variable.
$bank1 = makeBankAccount();
$bank1['modifyBalance'](5);
echo $bank1['getBalance'](); // 5 - it works.

// Now let's make another bank to prove that it has it's own independent internal $balance variable.
$bank2 = makeBankAccount();
$bank2['modifyBalance'](10);
echo $bank2['getBalance'](); // 10 - as expected. It would have printed 15 if bank2 shared a variable with bank1.

// Let's test bank1 one more time to be sure that bank2 didn't mess with it.
echo $bank1['getBalance'](); // 5 - still 5, as expected.

You may have noticed I used the reference operator & in this example. If you're not yet familiar with them yet, just know that References are known to be hard to understand. Although, I hope this post mostly makes sense by itself.