Functions: Anonymous Functions

Hack supports the ability to encapsulate an unnamed function and to use it, or save it for later use, in the context of an anonymous function object.

An anonymous function type has the following general syntax:

(function ( optional-comma-separated-parameter-list ): return-type )

For example:

function do_it((function (int): int) $process, int $value): int { ... }

class C {
  private (function (int): int) $af;
  ...
}

Function do_it takes two arguments, the first of which is an anonymous function taking one int argument and returning an int result. The instance property $af in class C has the same type.

There are two ways to create an anonymous function object: via a closure-creation expression or a lambda-creation expression. Consider the following example which shows both approaches:

<?hh // strict

namespace Hack\UserDocumentation\Functions\Anonymous\Examples\Basics;

function do_it((function(int): int) $process, int $value): int {
  return $process($value);
}

<<__EntryPoint>>
function main(): void {
  $closure = function(int $p): int {
    return $p * 2;
  };
  $result = $closure(5);
  echo "\$result = $result\n";

  $result = do_it($closure, 5);
  echo "do_it returned $result\n";
  $result = do_it(
    function(int $p): int {
      return $p * $p;
    },
    5,
  );
  echo "do_it returned $result\n";

  $lambda = (int $p): int ==> $p * 2;
  $result = $lambda(5);
  echo "\$result = $result\n";

  $result = do_it($lambda, 5);
  echo "do_it returned $result\n";
  $result = do_it((int $p): int ==> $p * $p, 5);
  echo "do_it returned $result\n";
}
Output
$result = 10
do_it returned 10
do_it returned 25
$result = 10
do_it returned 10
do_it returned 25

A closure-creation expression looks a lot like an anonymous-function type declaration. See the right-hand side of the assignment to $closure. Everything from function through } is an anonymous-function expression, often referred to as a closure. It gives each parameter a local name, and contains the body of the function, which, in this case, simply returns a value double that of the one passed in. The resulting object can then be used to call that function, as in $closure(5).

That same closure is also passed to function do_it, which calls it without knowing which function it is actually calling.

A lambda-creation expression involves the ==> operator and omits the keywords function and return. See the right-hand side of the assignment to $lambda. Everything from ( through 2 is an anonymous-function expression, often referred to as a lambda. It gives each parameter a local name, and contains the body of the function, which, in this case, simply returns a value double that of the one passed in. The resulting object can then be used to call that function, as in $lambda(5).

The ==> operator is right-associative, and lambda expressions can be chained together; for example:

<?hh // strict

namespace Hack\UserDocumentation\Functions\Anonymous\Examples\Chaining;

<<__EntryPoint>>
function main(): void {
  $lam1 = $x ==> $y ==> $x + $y;
  $lam2 = $lam1(10);
  $res = $lam2(7);
  echo "\$res = $res\n";

  $res = $lam1(10)(7);
  echo "\$res = $res\n";
}
Output
$res = 17
$res = 17

Note that all the parameter types and return types are absent in the lambda-creation expression; they can be omitted, in which case, they are inferred. For completeness, here's the same expression written with explicit type information:

$lam1 = (int $x): (function(int): int) ==> (int $y): int ==> $x + $y;

Consider the following example, in which an anonymous function accesses variables declared outside it:

<?hh // strict

namespace Hack\UserDocumentation\Functions\Anonymous\Examples\UseClause;

function get_process_1(int $val1): (function(int): int) {
  $val2 = $val1 * 3;
  $af = function(int $p): int use ($val1, $val2) {
    return $p + $val1 + $val2;
  };
  return $af;
}

function get_process_2(int $val1): (function(int): int) {
  $val2 = $val1 * 3;
  $af = (int $p): int ==> $p + $val1 + $val2;
  return $af;
}

<<__EntryPoint>>
function main(): void {
  $proc = get_process_1(2);
  $result = $proc(4);
  echo "\$result = $result\n";
  $result = get_process_2(2)(4);
  echo "\$result = $result\n";

  $result = get_process_1(3)(5);
  echo "\$result = $result\n";
  $result = get_process_2(3)(5);
  echo "\$result = $result\n";
}
Output
$result = 12
$result = 12
$result = 17
$result = 17

Function get_process_1 uses a closure-creation expression, but this time, it has a use clause, whose parentheses contain a comma-separated list of variable names that the anonymous function can reference.

Function get_process_2 uses a lambda-creation expression, which automatically has access to all variables in scope at the point where that anonymous function is defined. No use clause is needed.

Note the expression get_process_2(2)(4). As get_process_2 is a function, we can call that. And as it returns an anonymous function, we can call that.

Consider the following example, which calls an anonymous function stored in an instance property:

<?hh // strict

namespace Hack\UserDocumentation\Functions\Anonymous\Examples\Run;

class C {
  private (function(int): int) $af;
  public function __construct((function(int): int) $af) {
    $this->af = $af;
  }
  public function run(int $p): int {
    $tmp = $this->af;
    return $tmp($p);
  }
}

The assignment to the local variable $tmp and then calling through that in method run seems unnecessary. Why can't we simple replace these two statements with

return $this->af($p);

The problem is that a class is permitted to contain a property and a method having the same name! As such, $this->af() causes the checker to search for a method called af when none exists.

Like named functions, anonymous functions can have attributes, default argument values, and variable-argument lists, and they can be asynchronous.

If an anonymous function's parameter's type is omitted, that type is inferred, and if the function's return type is omitted, that is also inferred.

For both __FUNCTION__ and __METHOD__, an anonymous function name is {closure}.