A question about the type inference mechanism: in PHP, while it is possible to define object interfaces which classes may be defined against, by the dynamic nature of the language, functions don't necessarily need that interface specification to accept an object conforming to it, explicitely or implicitely. OCaml provides something "similar" with its object system, but much more powerful with static inference of an object (super)type from its usage. will Hack be able to infer an object interface as well from its usage in a function?
There seem to be 2 questions here:
1) Is Hack type inference total? Answer no: you must annotate parameters and return types.
It would be pretty much impossible to implement total type-inference without loosing separate compilation in Hack.
PHP projects are not organized around a module system, which means that you have "spaghetti" dependencies, and even better, cyclic dependencies all over the place.
So trying to implement total type-inference would be a bad idea, you would not be able to separate the code in independent entities and the checker would not scale.
2) Does Hack support structural sub-typing? Answer: No, but not for obvious reasons.
Fun fact, the first version of the type-checker was implementing structural sub-typing. And it was not scaling, for subtle reasons.
Hack allows covariant return types, so if we implemented structural sub-typing we would have to "dive" into the return types of each method to see if they are compatible. But in turn, these objects could have covariant return types etc ... The process of checking that was too inefficient. Caching is a bad idea (or at least a non trivial idea to implement), because of constraints and type-variables.
Since disallowing covariant return types was not an option (it was crucial to make a lot of code work), we had to kill structural sub-typing.
I hope this answers your question. As a big OCaml fan myself, I like the features you just mentioned (Well, Hack is written in OCaml), but they really didn't seem to be a good fit due to the nature of the language and the kind of checking speed we were shooting for.
Thanks! I was mostly thinking about point 2, and I understand your motivations in going in a different direction after trying it. Very good and enlightening answer!
We don't do any type inference across function boundaries, so we largely dodge the issue that I think you are getting at. (Please elaborate if I misunderstand!) We rely on interface and class definitions in order to know what methods are available, and even though the runtime resolves everything at runtime so you can call any method that happens to exist at that time ("duck typing"), we enforce statically that methods do exist where we can. So for example, the following code will work at runtime since the method `g` does exist, our static typechecker will reject it since it does not exist on type `I`.
<?hh // strict
interface I { public function f(): void; }
class C implements I {
public function f(): void {}
public function g(): void {}
}
function f(I $i): void {
$i->g();
}
f(new C());
Thank you! You brought an interesting point with the example you gave, it is one of the things I was thinking about. Thank you for your work, and for making it available!
A question about the type inference mechanism: in PHP, while it is possible to define object interfaces which classes may be defined against, by the dynamic nature of the language, functions don't necessarily need that interface specification to accept an object conforming to it, explicitely or implicitely. OCaml provides something "similar" with its object system, but much more powerful with static inference of an object (super)type from its usage. will Hack be able to infer an object interface as well from its usage in a function?