Callables: Introduction

The following is allowed in PHP:

<?php

namespace Hack\UserDocumentation\Callables\Intro\Examples\PHPCallable;

function callMe(callable $callback) {
  $callback();
}

function run() {
  $callback = function () {
    echo "Hello!";
  };
  callMe($callback);
}

run();

The Hack typechecker does not allow the callable type hint as it does not specify any information on parameter types or return types - however, Hack does offer a powerful alternative to the callable type hint through the function keyword:

<?hh

namespace Hack\UserDocumentation\Callables\Intro\Examples\HackCallableGood;

// Use the more expressive syntax instead of the callable typehint
// Using mixed here in case callMe ever got a callback that returned something
// else. But could have used string too.
function callMe((function(): mixed) $callback): mixed {
  $callback();
}

function run(): void {
  $callback = function () {
    echo "Hello!";
  };
  callMe($callback);
}

run();

Syntax

The syntax for Hack callables is:

(function (<param type 1>,..., <param type n>): <return type>)

Since a callable is really a function, the syntax above should look like a function signature without the function and parameter names.

e.g., This function takes a callable parameter: that takes a bool and int and returns a string:

function foo((function(bool, int): string) $callback) { ... }

Allowed Callables

There are four (4) types of callables allowed in Hack that will pass the typechecker. Note that objects with the __invoke() method are not recognized as callable by the Hack typechecker.

Closure

Closures work as you would expect.

<?hh

namespace Hack\UserDocumentation\Callables\Intro\Examples\Closure;

function my_math((function(int, bool): int) $callback, int $a, bool $b): ?int {
  if ($a < 0) {
    return null;
  }
  return $callback($a, $b);
}

function run(): void {
  $callback = function (int $a, bool $b): int {
    if ($b) {
      return $a + $a;
    }
    return $a * $a;
  };
  var_dump(my_math($callback, 3, true));
  var_dump(my_math($callback, 3, false));
  var_dump(my_math($callback, -1, false));
}

run();

Function

You can pass a named function to an entity expecting a callable by verifying that function with the special function called fun().

<?hh

namespace Hack\UserDocumentation\Callables\Intro\Examples\Func;

function add_or_mult(int $a, bool $b): int {
  if ($b) {
    return $a + $a;
  }
  return $a * $a;
}

function my_math((function(int, bool): int) $callback, int $a, bool $b): ?int {
  if ($a < 0) {
    return null;
  }
  return $callback($a, $b);
}

function run(): void {
  var_dump(
    my_math(fun(
      '\Hack\UserDocumentation\Callables\Intro\Examples\Func\add_or_mult'
      ),
      3, true)
  );
  var_dump(
    my_math(fun(
      '\Hack\UserDocumentation\Callables\Intro\Examples\Func\add_or_mult'
      ),
      3, false)
  );
  var_dump(
    my_math(fun(
      '\Hack\UserDocumentation\Callables\Intro\Examples\Func\add_or_mult'
      ),
      -1, false)
  );
}

run();

Instance Method

You can pass an instance method to an entity expecting a callable by verifying that method with inst_meth().

<?hh

namespace Hack\UserDocumentation\Callables\Intro\Examples\Instance;

class A {
  public function add_or_mult(int $a, bool $b): int {
    if ($b) {
      return $a + $a;
    }
    return $a * $a;
  }
}

function my_math((function(int, bool): int) $callback, int $a, bool $b): ?int {
  if ($a < 0) {
    return null;
  }
  return $callback($a, $b);
}

function run(): void {
  $a = new A();
  $callback = inst_meth($a, 'add_or_mult');
  var_dump(my_math($callback, 3, true));
  var_dump(my_math($callback, 3, false));
  var_dump(my_math($callback, -1, false));
}

run();

Static Method

You can pass a static method to an entity expecting a callable by verifying that method with class_meth().

<?hh

namespace Hack\UserDocumentation\Callables\Intro\Examples\Stat;

class A {
  public static function add_or_mult(int $a, bool $b): int {
    if ($b) {
      return $a + $a;
    }
    return $a * $a;
  }
}

function my_math((function(int, bool): int) $callback, int $a, bool $b): ?int {
  if ($a < 0) {
    return null;
  }
  return $callback($a, $b);
}

function run(): void {
  $callback = class_meth(A::class, 'add_or_mult');
  var_dump(my_math($callback, 3, true));
  var_dump(my_math($callback, 3, false));
  var_dump(my_math($callback, -1, false));
}

run();