9 ms·
At last! This all seem extremely cool. I went straight from hacking Scala and Haskell as a hobbyist to doing (mostly) front-end JS job, and I've always found
by slashnull 12y ago
At last!
This all seem extremely cool.
I went straight from hacking Scala and Haskell as a hobbyist to doing (mostly) front-end JS job, and I've always found that my code, and a lot of good libraries I read, naturally emulate something close to Hindley-Milner typing, by using objects as tuples/records and arrays as (hopefully well-typed) lists, as well as the natural flexibility of objects as a poor substitute for Either types.
I'm definitely pleased to see that the designers of this library have also realized that strongly-typed javascript was just a few annotations and a type inference algorithm away.
I'm just wondering why are nullable types inmplemented as such and not as a natural consequence of full sum types, which are inexplicably absent.
- slashnull 12y agoWow! I missed the part about sum types! My god this is perfect
- inglor 12y agoStrongly typed JS is actually pretty hard - probably not by Haskell and Scala standards - but if you take promises for example the signature of `then` is: Promise<A,E> -> ((A -> (Promise<B,F> | B)),(E -> (Promise<C,G> | C))) -> Promise<B|C,E|G|C> That is - a promise's then - takes the promise (as this) and executes either a `.then` fulfillment handler or a catch handler. If the `fulfill` handler executes the value is unwrapped and either a new value, or a Promise over a new value and its own type of error is returned. Now, if the `reject` handler is executed the error is unwrapped and either a new value, or a promise over a new value or a new error is returned. This is quite simple and easy to use because it behaves like try/catch in the dynamic type system of JS with recursive unwrapping - however it is challenging to reason about when you're starting to type code and you want to actually have correct type information with promises. Static languages generally approach these problems with pattern matching on the type - in JS that's not common nor is it feasible at runtime - you just expect a value of a certain type. When I implemented promises in another language (swift) this was a lot of fun to work through and not very trivial - if their compiler cna do this I'd be very impressed. Promises are just one example. Anyway - this looks cool. I definitely agree that full sum types would've made more sense - having explicit nullables is usually a smell (like in C#).
- slashnull 12y agoI don't want to say what I'm about to say, but... I don't care about the type signature of promises. I actually don't care about the type of anything which has a complicated type; I think it's just incredibly winning that I will now be able to describe the shape of the raw data circulating in my code. I've always found really annoying, in Haskell, to see incredibly complex type idioms emerging to allow stuff that doesn't really deserve it. And yes, I'm talking about monad transformers. Don't get me wrong, I think that Haskell and the typing techniques and idioms it has fostered are a tremendous achievement, but right now, I'm more focused on bringing my web code, which right now is unfortunately a jungle of implicitly-typed garbage, closer to a safe and predictable better-typed form. I tried to do that in Haskell in the back-end, but everytime I tried, I lost mind-boggling amounts of time dealing with the monadic stack of the framework I tried. As the saying goes, I'm not clever enough to use dynamic typing, and bugs happen. Unfortunately, I'm also not clever enough to use real strong typing, and nothing compiles, let alone gets done. Hence why I'm immensely thankful to see facebook embracing gradual typing in a way that lets me leverage my knowledge of algebraic typing.
- inglor 12y agoYou make some good points here, I've also experienced the same with Haskell web frameworks. I think we can agree that the most important things to annotate are points of interaction. If I'm part of a 5 developer team working on a code base, or I'm using a library someone made - I want the functions I'm calling to be very explicit about what they take and return and I want the functions I provide others to be very clear on what they take and return. The problem is that even something people use every day like a Promise or an event handler creates very complex types (like the example above). I think any viable solution that expects to be type safe needs to be able to express that. If your code does not expose any callbacks, or anything async I'd say it's simply not very typical JS code. The alternative of course is to be _less_ safe about our type. We could say that we treat a promise as: Promise<A> -> A -> Promise<B> -> Promise<B> (just a `bind` from Haskell) - this would let us maintain _some_ type safety which is better than nothing.
- 12y ago
- ufo 12y agoYou can't really use sum types as they are found in Haskell in Javascript. With Haskell option types you do a pattern matching and create a new binding for the non-null value but in Javascript you don't do that - you keep using the same object that you tested against null. case mx of Just x -> f(x) vs if (x != null){ f(x) } What you can do in a Javascript-like language is use union and intersection types. However, they can get a bit complicated (specially if you allow unions of non-primitive types) and the extra flexibility can confuse the type inference a bit so I can understand them restricting things to the common case of handling null.
- sonyandy 12y agoWhat about function nothing() { return { match: function(cases) { return cases.nothing(); } }; } function just(x) { return { match: function(cases) { return cases.just(x); } }; } just(1).match({ just: function(x) { return x + 1; } nothing: function() { return 0; } });
- jahewson 12y agoRight, but now you're not using null. The question was how to justify the use of sum types for null... not whether or not they could be emulated in some non-idiomatic manner.
- masklinn 12y ago> I'm just wondering why are nullable types inmplemented as such and not as a natural consequence of full sum types, which are inexplicably absent. Haskell-style sum types are a generic type with multiple values describing the alternatives. Since the goal of Flow is to typecheck existing JS semantics, the addition of wrapper types and objects to support such sum types makes very little sense while the addition of "anonymous" union types makes a lot of sense. Dialyzer took the exact same path (except even more so as its type unions can contain values) as it tried to encode Erlang's existing semantics.