I think the most likely outcome is that PHP will adopt some of HHVM's additional features, but remain a separate project. IMO that's a great outcome, since we'll both likely drive the other to be better.
Do you think Hack is a good language to start a new project in, compared to non-PHP languages? Are you using Hack for things besides the main web page?
Yeah, I think Hack is a good language to start a new project in. For as much flak as PHP gets, there are actually a lot of good things about the language. The fast development cycle -- edit php script, refresh -- is something amazing that you don't get in a lot of statically typed languages, which usually have a compilation step. The crazy dynamic things you can do also occasionally have their place, though it's certainly easy to shoot yourself in the foot.
On the other hand, a lot of the time you want the safety that strong static typing can give you. Even just the null propagation checking can immediately find tons and tons of silly little bugs without even running the code, and ensure that the code stays consistent as a "mini unit test" if you will.
Hack hits the sweet spot of both. Wiring the Hack typechecker into vim was really revolutionary for me -- having both the immediate feedback of the type system for all the silly bugs that I was writing, along with the fast reload/test cycle from PHP, is great.
Once you have type annotations, it wouldn't be too much of a stretch to enforce them statically with a separate tool. You could even go as far as rejecting first-party code if you can't statically determine every single value's type. Pylint's underlying astroid library has a bunch of inference tools you could perhaps build on top of.
FB already had a PyLint-like tool earlier that could do some static analysis, namely pfff (also open source and written in OCaml), but it did not provide a full-on static type system like Hack. (Background: I used pfff when I was in bootcamp at FB itself. This was however prior to hack, I worked solely on C++, Java, and a bit of Python at FB after bootcamp).
I am sure if FB started off with Python, a similar solution could have been found, but if you're looking for a tool that exists _right_ now, Hack is actually quite decent.
Creating a static type system, implement local type inference, as well as working out "gradual typing" and associated problems (all while being able to do type-checking at speeds developer _expect_) is not a trivial problem.
For that matter, can you call a typed function from an untyped one? Or, worse, a typed method? If the typing is purely static, there's no way to know the method you're calling is actually typed, so there's no actual guarantee that it receives the types it's declared unless your entire program is typed. It doesn't seem like a very strong guarantee if both the caller and the callee have to opt into the typing.
If you're looking for a tool that exists right now, you either have an existing codebase and can't port it to Hack if it's not already PHP anyway (for the same reason Facebook couldn't port away from PHP), or you're starting from nothing and could just use a statically-typed language in the first place.
I don't know if I'd even be excited about the prospect of optional static typing in Python. (It hasn't gotten me interested in Dart, for example.) I'd kinda rather see the effort poured into something that could do static duck-typed analysis/inference, e.g. balk if I pass an argument that could be a non-string into a function that tries to call `.startswith` on it. (Ah, but maybe it could theoretically be a string or None, and I only know it isn't None for reasons the type system can't see, and now I hate the type system.)
I didn't say it was a trivial problem. I just don't feel excited by the solution.
Another mode (you specify the mode per file/compilation unit) is "strict". In strict mode, you can not code any un-typed code (note, the standard library is typed with hack).
(There is a bit more nuance here, but you can read that in the documentation.)
So the idea is to eventually migrate most of the code to strict, but code that relies on legacy can remain partial and you can write new code without waiting for a re-write to finish.
See http://docs.hhvm.com/manual/en/hack.modes.strict.php http://docs.hhvm.com/manual/en/hack.modes.partial.php
"Shapes" are also a neat feature specifically for parts where static typing can be frustrating for dealing with HTTP requests specifically: http://docs.hhvm.com/manual/en/hack.shapes.php
(FWIW I don't see myself using Hack, but I'm not a web developer. I'd say the ML family languages are my favourite, but for what I do day-to-day it's not really an option.)
I'm intrigued, because it's such a good idea (especially when null's originator claimed it was a "billion dollar mistake"), though Java doesn't have it. I'm wondering if there's some subtle problem with it...?
Also, how do you make your vim typechecker fast enough? Usually, even syntax colouring is local to ensure adequate performance - and a typechecker with inference/propagation would be very non-local.
and an Optional<T> class (references to which can still be null for maximum hilarity): http://download.java.net/jdk8/docs/api/java/util/Optional.ht...
I'm stoked. And disappointed the "elvis" operator didn't make it in.
I hope some day Java breaks backwards compatibility and eliminates null entirely. Then again, that's already happened with the proliferation of other JVM languages. But that doesn't me at my day job, where we have a large legacy code base... which would need to be ported to a backwards-incompatible version of non-null Java anyway. Hm.
Well, with Java 8 I can at least start to grow null-safe code within our codebase.
You seriously think that? That's how we do haskell web development. Both yesod and snap do this out of the box. That's how every java developer I know works.
Edit: I misread what you wrote. I thought you were saying there was something fundamentally different about the type checker, but more that reload experience is different then what you get with Yesod.
How big is your code base, and is it all in one huge file or something? Our stuff is reloaded and ready before I've alt+tabbed back and hit refresh.
How extensively is Facebook using Hack at this point? Is it in production?
What has been the biggest learning/unlearning you've needed to do going from PHP to Hack?
The biggest learning step for our engineering teams was to treat type errors from Hack as actual logic errors. We have a collection of "linters" that provide advice on code style and other nice-to-have factors. Some people (quite reasonably) initially thought of Hack errors as lint-like stuff that it was safe to ignore, when in fact they indicate real logical inconsistencies in code.
Interesting, thanks!
One followup: what has it been like from an ops perspective? Similar to PHP, or is there a better frame of reference?
HHVM is its own web server (although it supports FastCGI for easier use with existing infrastructure like nginx), and that's how we use it in production. It's hard to compare memory usage, except at scale, where it benefits from not having a whole bunch of interpreters (in different processes) running at the same time and some other benefits by using more appropriate types to store values through type inference.
On a webserver, that goes further since HHVM is single-process/multi-thread, whereas PHP (in its typical setup) is multi-process/single-thread. This cuts HHVM's memory overhead much further.
Yes, PHP can run multi-threaded, but it still has a number of instability issues in that mode.
TypeScript added classes, interfaces, modules, and arrow functions.
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?
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.
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());What's your motivation, aside from modernizing Facebook's code base? What language niche does Hack serve which is not served by other languages? Why Facebook at all (aside from the rarity of finding a company to pay you to write a compiler :))?
This lets you choose the pace and extent to which you want to adopt the safety of static typing, while preserving your dynamically typed code -- and without sacrificing the rapid turnaround of PHP.
That's a unique combination, in my experience.
Typed Racket also includes more types, like ": Integer [more precisely: Negative-Fixnum]" (from docs), as well as polymorphic data structures and higher-order functions. I don't know if this is a "fundamental difference" but it might mean Facebook's type checker can optimize in certain ways that Racket's cannot.
The Typed Racket type checker is much slower than Hack, though.
Hack does seem to have polymorphic data structures and higher-order functions.
Would you consider adding even more expressive features like algebraic types or hygienic macros to the language?
Also how likely do you think it is that typical PHP will eventually be overtaken by HHVM? I'm hoping it will!
public string $x = '';
instead of
public $x:string = '';
It seems inconsistent to me, probably because I've used AS3/Haxe.
function foobar() : int { .. }
instead of function int foobar() { .. }Property types are declared before the property name, while function return types are declared after (and with a colon).
Very strange.
That syntax in hack seems more "functional" to me. That said, I must admit that I kind of like the C syntax, because of its oddity.
The incosistency drives me a bit crazy.
Do you expect Hack to be stable without large breaking changes going forward?
The documentation doesn't say much about the scheduling for asynchronous tasks. Can async functions be used to batch requests to e.g. caches and databases? Can Awaitable be used to interface with code that expects chainable Future or Promise-style interfaces?
Can HHVM/Hack use standard PHP extensions, or how much work is it to port an extension?
Is there a Hack plugin for IntelliJ?
Does the Hack project have a mailing list or forum?
Our internal plumbing is different, so the extension APIs aren't the same, thus PHP extensions can't normally be used with HHVM. Paul has a source-compatability transformer which works for simple extensions (no fancy stuff allowed), but we recommend anyone with an extension consider porting the code.
If your extension is one of those "This was written in PHP but we wanted it to be faster so we turned it into C" types, you should consider going back to PHP for it. In practice, we find that HHVM can JIT PHP code into running about as fast as (sometimes faster than) C/C++ extension code.
If you do need to dip into C/C++ (because you're calling an external library), the API we've designed is a LOT easier to work with than PHP's. And I say that with the authority of having written THE book on writing PHP extensions: http://amazon.com/dp/067232704X .
I'm working on proper documentation for extensions as we speak, but for now you can find some very basic info at https://github.com/facebook/hhvm/wiki/Extension-API .
(I'm not a php developer so excuse my ignorance)
It seems you only have 1 method 'await', to check if the async function has completed its job (it self, if I'm understanding is a blocking operation in which ever thread/worker its called).
So is there a way to run something more similar to
$i = 1;
while(doingSomeThing)
{
$b = "";
if(completed gen_foo())
{
$b += await gen_foo();
do_critical_task($b);
}
else
{
do_something(do_item[i]);
}
$i++;
}
Because as I see it now, what ever I run asynchronously I.E.: gen_foo(get_user_data); //async
$x = do_something_for_a_while(); //do something while gen foo processes
do_new_thing($x, await gen_foo()); //do something with rendezvous result
I'm forced to time out my async calls so that they'll rendezvous in the same place won't I?Again if I'm completely off the mark please let me know.
The way it works is quite different from your example:
async function gen_foo(): Awaitable<string> {
echo "until we get to an await, eager execution\n";
// ...
await gen_bar();
// this code is only executed after await
return 'result';
}
$x = gen_foo(); // x is a handle, suspended at the point of its first 'await'
await $x; // ... and now it's resumed
The benefit comes from being able to batch these async functions together: list($x, $y, $z) = await genva(
gen_foo(),
gen_bar(),
gen_baz();
); // genva creates a wait handle for awaiting its args
// ... and when we get here $x, $y, and $z are all assignedCan I embed it? Or extend it via compiled binaries written in C/C++?
As the author of pretty much THE book on writing PHP Extensions ( http://amazon.com/dp/067232704X ), I can say with a fair bit of authority that writing HHVM extensions is SO SO SO much easier.
Are you planning a Windows build ?
do you think Hack could replace all PHP stacks (PHP cloud stacks , not talking about shared host wordpress toys) in a 5 years time frame ?
What do you think about the current state of PHP?
what would you tell PHP core devs about that?
Does an async frameworks like React runs with Hack?
What can you answer to people that are concerned that it is Facebook who is developping this?
Yes. auroraeosrose is working on it (the same person who did the port for php) https://github.com/auroraeosrose/hhvm/tree/win32_start
[1] - http://hacklang.org/manual/en/hack.annotations.summary.php
It's complicated.
If you use partial mode, which doesn't enforce that every function is fully typed, then it's trivial to break the type system by just using an untyped function. In order to ease conversion, we just assume the programmer knows what they are doing with untyped functions and let anything pass.
If all of your code is in strict mode, then we believe the type system to be sound. We haven't done any formal proof of this of course, and there have been plenty of bugs in the past. But that's the goal.
Some types are enforced at runtime -- just like PHP5 enforces class type annotations on parameters. However, at least for now, the runtime doesn't do a lot of the clever things with the type system that the static typechecker does. It's much more conservative with using the type information, and doesn't do things like check generics at all. (At least right now! We probably will change this in the future.) This means that we can play a little more fast and loose with the type system in the static parts; we want it to be sound, but it doesn't have to be right now; it's not going to cause a JIT crash or anything.
There are more details on the modes here: http://hacklang.org/manual/en/hack.modes.php
For example, in Java you'd write: int add(int x, int y) { ... }
And in Go you'd write: func add(x int, y int) int { ... }
But it looks like in Hack you'd write: function add(int x, int y): int { ... }
Both Java and Go are consistent, either the type comes before or after the name, no matter which context you're dealing with, be it return type or argument type. Hack seems to mix the two styles, which seems like it would make code harder to read as things get more complex, like when functions accept other functions as arguments.
Am I reading the documentation right? And, if so, can you shed any light on the decision making process that went into this decision?
When I look at this from the docs, it seems unsound:
"Hack treats traits as a stand-alone entity during the type checking process. In other words, it ensures type consistency within the trait (i.e., as a black box, so to speak), but does not "copy and paste" the code into all of the classes that use the trait and check for type consistency there. The reason this is done comes down to performance."
Is there something I'm missing that does make this sound?
Feel free to play around with the type system in the interactive code editor on http://hacklang.org/.
What I thought the quoted passage was saying was that consistency between a class and a trait used by the class was not checked. That's clearly not the case, though. I don't understand what the performance point is, though, since I don't think the copy-and-paste style would produce different answers.
Finally, the online editor doesn't seem to honor // strict -- it doesn't produce an error when some methods aren't annotated.
How do you protect from type errors when a value crosses from the untyped to typed fragment? Do you use contracts?
We don't (statically) protect against type errors when you cross from untyped into typed code. If you call an untyped function, we assume the programmer knows what they are doing -- just like PHP does. You might get a runtime exception if you are calling a method on null, for example.
This is actually a pretty important part of the conversion process. You don't have to convert all your code all at once. Typed code can be verified statically; untyped code is assumed to work just as before.
Parameter and return type annotations are enforced at runtime by HHVM (just like you can add a class type annotations to a parameter in PHP). This will protect against some inconsistencies -- again, just as if you weren't using Hack.
If I write a typed map : (a -> b) -> [a] -> [b] and then it's called with an untyped function as input, but it returns a something that's not a b. Do you place some kind of barrier around it so that that's stopped immediately?
https://raw.github.com/strangeloop/StrangeLoop2013/master/sl...
The idea of bolting a static checker onto a dynamic language and using an unresolved top type to make it work is cool. The presentation refers to your system as SoA gradual typing. Are there any papers or presentations which explain that approach and how it works in more detail? Particularly in how it might differ from gradual typing?
Adding lambdas makes PHP more Ruby-like and generics and type checking are straight out of Java. I'm still unconvinced in how this makes programming websites more efficient or bug-free than existing languages. Can you please elaborate on that?
In the mean time, the open source release of Hack includes integration with both Emacs and Vim.
Feel free to send us a pull request: https://github.com/facebook/hhvm/tree/master/hphp/hack/edito...
I program in php every day and miss static typing (as opt-in mechanism). Sadly i'm on windows and use oracle, so hhvm is not applicable.
Deleted comment