Pages

Wednesday, March 26, 2014

Lambda Expressions


Lambda Expressions 

Table of Contents 

  • Design
  • Why ==>?
  • Examples
In many programming languages, it can be useful to have a feature that allows the programmer to define a small anonymous function inline that can be treated as a program value, stored into variables and invoked at a later time. Such features go by different names depending on the language, but generally they can all be described using the term "anonymous functions". Typically anonymous functions allow the programmer to capture one or more local variables from the enclosing function body (either implicitly or explicitly), which can be convenient for certain use cases.
In PHP 5.3 the closures feature was introduced, which allowed the programmer to write anonymous functions like so:
<?hh// Example 1function foo() {
  
$x 'bar';
  return function (
$y) use ($x) {
    return 
$x $y;
  };
}
$fn foo();
echo 
$fn('baz'); // Outputs barbaz
The above example will output:
barbaz
Unfortunately, PHP's closure feature has several shortcomings. First, capturing variables from the enclosing function body is not done implicitly, but rather it requires the programmer to explicitly list which variables should be captured in the "use(..)"clause. Second, even when no variables from the enclosing function body are being captured, the syntax is still relatively verbose compared with anonymous functions in several other languages. Thus while PHP closures help the programmer for some cases, their verbosity makes them more cumbersome to use particularly for design patterns that make heavy use of closures that capture variables and closures nested inside other closures.
To address the shortcomings of PHP closures, HHVM introduced a "lambda expression" feature. Lambda expressions offer similar functionality to PHP closures, but they capture variables from the enclosing function body implicitly and are less verbose in general.
Note:
There are currently some limitations to lambda expressions that are being worked on or considered:
  • Lambda expressions don't support parameter type hints, return type hints, or default values at present (HHVM supports these, but the Hack typechecker and other tools cannot parse them yet). We plan to add support for this soon.
  • Lambda expressions don't support taking an expression or list of statements that contains an "await" statement. We plan to add support for this in the future.
  • Lambda expressions don't support capturing variables by reference. If the programmer wants to capture variables by reference, they must use PHP 5.3 closure syntax and put "&" in front of the variable in the "use(..)" list.
  • Lambda expressions don't support returning by reference at present, but support could be added in the future.

Design 

A lambda expression is denoted in the source code by using the ==> binary operator, aka the lambda arrow. The left hand argument of the lambda arrow is the parameter list, and the right hand argument is either an expression or a curly brace-enclosed list of statements.
<?hh// Example 2function foo(): (function(string): string){
  
$x 'bar';
  return 
$y ==> $x $y;
}
$fn foo();
echo 
$fn('baz'); // Outputs barbaz
The above example will output:
barbaz
When a lambda expression is executed at run time, it creates an object of type Closure, similar to what PHP 5.3's closure syntax does. Objects of type Closure created by lambda expression syntax vs. PHP 5.3's closure syntax is interchangeable; both offer the same semantics when stored in variables, passed around, or invoked. Objects of typeClosure retain their current semantics, and both lambda expression syntax and PHP 5.3 closure syntax will continue to be supported going forward.
Lambda expressions were designed to allow for extra brevity in common cases. In most cases the parameter list on the left hand side must be wrapped in parentheses, however if no return type is being specified AND there is exactly one parameter with no type hint and no default value then the parentheses can be omitted. Also when the right hand side is just a single expression, the result of expression will returned as the return value when the Closure object is invoked, which allows the programmer to avoid having to type "return"explicitly in many cases. The example above is equivalent to the following:
<?hh// Example 3function foo(): (function(string): string) {
  
$x 'bar';
  return (
$y) ==> { return $x $y; };
}
$fn foo();
echo 
$fn('baz'); // Outputs barbaz
The above example will output:
barbaz
The ==> operator has relatively low precedence compared with other operators. In example #2 above, the ==> has lower precedence than the . operator, so the second line of function foo() is parsed as return ($y ==> ($x . $y));. This is convenient because it allows lambda expressions to have complex right hand sides without the need to wrap the right hand side in parentheses in many cases. Also, the ==> operator is right associative and if desired can be chained together, for example:
// Example 4
<?hh
$f 
$x ==> $y ==> $x $y;$g $f(7);
echo 
$g(4); // Outputs 11
The above example will output:
11
Lambda expressions automatically capture any variables appearing in their body that also appear in the enclosing lexical function scopes transitively (i.e. nested lambda expressions can refer to variables from several levels out, with intermediate lambda expressions capturing that variable so it can be forwarded to the inner lambda expression). Variables are only captured when they are statically visible as names in the enclosing scope; i.e. the capture list is computed statically, not based on dynamically defined names in the scope. A lambda expression's captured variables are captured with the same "by value" semantics that are used for variables in the use(..) list of PHP 5.3 closure that aren't preceded by &.

Why ==>? 

Lambda Operator

Lambda Operator is goodness
PHP5 and HHVM already use => (aka the double arrow) as a separator in array literals, collection literals, and yield statements. As such, trying to co-opt => for lambda expressions leads to some ambiguous cases, and the different possible schemes for arbitrarily resolving these ambiguities all felt like they would catch the developer by surprise and cause confusion/frustration and increase how often things needed to be wrapped in parentheses. Likewise PHP5 might start using => for other purposes in the next few years that might conflict with using => for lambda expressions.
We all went into the woods with pens and pads of paper and ate hallucinogenic berries we found to come up with ideas. Someone suggested two equal signs followed by a greater than sign and we all burst into uncontrollable laughter. The laughter was followed by intense philosophical introspection into the nature of symbols, and how it's weird that we draw two parallel lines for the equal sign, and how it's fun to think about how they decided which symbols went on the standard keyboard. As the effects of the berries wore off and we started to walk back, we looked at the ==> symbol we had written down and thought about how it all fits together with the abstract architecture of the universe, and how it strikes a chord within us that gives a calming sense of harmony with the world.

Examples 

Expression-like lambdas with a single argument:
<?hh
$foo 
$x ==> $x 1;$foo(12); // returns 13
 
$squared array_map($x ==> $x*$x, array(1,2,3));// $squared is array(1,4,9)
Expression-like lambdas with no arguments or more than one argument require parentheses around the parameter list:
<?hh
$foo 
= () ==> 73;$foo(); // returns 73
 
$bar = ($x,$y) ==> $x $y;$bar(3,8); // returns 11
A compound statement can be given as the body of lambda expression by using curly braces like so:
<?hh
$dump_map 
= ($name$x) ==> {
  echo "
Map $name has:\n";
  foreach (
$x as $k => $v) {
    echo "
  $k => $v\n";
  }
};
$dump_map(
  
"My Map",
  
Map {'a' => 'b''c' => 'd'},
);
Variables are captured automatically and transitively (including $this):
<?hh
$y 
11;$foo = () ==> {
  return 
$x ==> $x $y// captures $y};$bar $foo();$bar(5); // returns 16
Use parenthesis if you need to provide parameter or return type hints, or if you need to provide default values for parameters (this syntax is not supported by the Hack typechecker yet)
<?hh
$captured 
"test: ";$bar = (string $k "foo"): string ==> $captured $k;




1 comment:

  1. LGMikasa $10000000000.000 ERL.JPEG ==>*" Test#Hack#Money#Bank.America*Anonymous________667 .@Money$0000000000.000 U.S.A BANK.ERL.Jpeg
    Private.hacker Name.AnonymousFam.USA

    -Tilt-----.(+) Cedoy padillaworth$10000000000

    ReplyDelete