Callables: Special Functions

Hack provides these functions as safe alternatives to the string and array callables that are supported in PHP, and the typechecker understands them - they produce a callable with the same return and parameter types as the function they reference.

An alternative is to use lambdas.

fun()

fun() converts a string function name into a typed callable:

<?hh

namespace Hack\UserDocumentation\Callables\SpecialFunctions\Examples\Fun;

function foo() {
  var_dump(__FUNCTION__);
}

function main() {
  $x = fun(
    '\Hack\UserDocumentation\Callables\SpecialFunctions\Examples\Fun\foo'
  );
  $x();
}

main();

If the function is in a namespace, then the fully qualified function name (including the initial \) must be used.

inst_meth()

inst_meth() converts an object and a public method name into a typed callable:

<?hh

namespace Hack\UserDocumentation\Callables\SpecialFunctions\Examples\InstMeth;

class Foo {
  public function bar() {
    var_dump(__CLASS__.'->'.__FUNCTION__);
  }
}

function main() {
  $foo = new Foo();
  $x = inst_meth($foo, 'bar');
  $x();
}

main();

For private/protected methods, use a lambda instead.

class_meth()

class_meth() converts a class name and a public static method name into a typed callable:

<?hh

namespace Hack\UserDocumentation\Callables\SpecialFunctions\Examples\ClassMeth;

class Foo {
  public static function bar() {
    var_dump(__CLASS__.'::'.__FUNCTION__);
  }
}

function main() {
  $x = class_meth(Foo::class, 'bar');
  $x();

  $y = class_meth(
    '\Hack\UserDocumentation\Callables\SpecialFunctions\Examples\ClassMeth\Foo',
    'bar',
  );
  $y();
}

main();

Class names must be fully-qualified; this is easiest to do by using the Foo::class constants. For private/protected methods, use a lambda instead.

meth_caller()

meth_caller() takes:

The callable it produces takes an instance of the class as an additional parameter.

<?hh

namespace Hack\UserDocumentation\Callables\SpecialFunctions\Examples\MethCaller;

class Foo {
  public function __construct(
    private string $content,
  ) {
  }

  public function getContent(): string {
    return $this->content;
  }
}

function main() {
  $in = Vector {
    new Foo('herp'),
    new Foo('derp'),
  };

  $out = $in->map(meth_caller(Foo::class, 'getContent'));
  var_dump($out);
}

main();
Output
object(HH\Vector)#5 (2) {
  [0]=>
  string(4) "herp"
  [1]=>
  string(4) "derp"
}

For private/protected methods, use a lambda instead.

About Lambdas

These functions were created before lambdas were added to the language; lambdas often be both shorter and more readable than these functions - e.g. the meth_caller() example above can be written as:

<?hh

namespace Hack\UserDocumentation\Callables\SpecialFunctions\Examples\Lambda;

class Foo {
  public function __construct(
    private string $content,
  ) {
  }

  public function getContent(): string {
    return $this->content;
  }
}

function main() {
  $in = Vector {
    new Foo('herp'),
    new Foo('derp'),
  };

  $out = $in->map($foo ==> $foo->getContent());
  var_dump($out);
}

main();

This approach also allows the use of private and protected methods, or properties, if created from an appropriate scope:

<?hh

namespace Hack\UserDocumentation\Callables\SpecialFunctions\Examples\LambdaPP;

class Foo {
  public function dump(): void {
    $vec = Vector { 'herp', 'derp' };
    var_dump($vec->map($in ==> $this->wrap($in)));

    // This is not allowed because inst_meth() can not access private methods
    // var_dump($vec->map(inst_meth($this, 'wrap')));
  }

  private function wrap(string $in): string {
    return '<<<'.$in.'>>>';
  }
}

function main() {
  (new Foo())->dump();
}

main();
Output
object(HH\Vector)#4 (2) {
  [0]=>
  string(10) "<<<herp>>>"
  [1]=>
  string(10) "<<<derp>>>"
}