Search code examples
hacklang

HackLang type for different objects


Let's say I have static connector that allows to proxy instances of different adapters:

$m = Connector::take('mcrouter');
$db = Connector::take('production_database');

Connector must init and handle connections during the runtime:

protected $connection;
abstract protected function openConnection($config);

Somewhere inside adapter:

$this->connection = $this->openConnection($config);

The connection is an object and could be an instance of Memcached, MySQLi etc. or NULL. So logically I want to do this:

protected ?object $connection;
abstract protected function openConnection($config):?object;

But at the same time connection is not really instance of "object", it is instance of Memcached for example, and the result is:

Catchable fatal error: Hack type error: Invalid assignment

The only solution works in this case is to not define the type at all. Is there are some trick for defining universal object?


Solution

  • The universal type in Hack is what you get when you omit a type annotation; it's compatible with everything. object is not a type known to the typechecker, and so it assumes you have a class object somewhere.

    The connection is an object and could be an instance of Memcached, MySQLi etc. or NULL.

    The right way to do this is define an interface that both of those objects implement, and use that interface as the type here.

    Having a generic object type gives no information to the typechecker; it still doesn't know what methods are valid to call on that object. The interface gives the typechecker that information.

    Note that "what methods are safe to call when" is something that's implicitly codified in your application -- the code knows through some external means when it's safe to call certain methods for Memcached, MySQLi, etc, otherwise your code wouldn't work! Hack's type system, and type systems in general, just force you to make this explicit.

    As an aside, you really shouldn't be getting your type errors as catchable fatals from HHVM; this is a last resort sort of check. Try running the hh_client checker directly, maybe even showing its result in your IDE; it will give you a much faster iteration cycle, and much more information than HHVM provides.