6 ms·
> TypeScript has has union types i.e. "number | string" which are similar to algebraic data types. I understand that you said "similar", but there's actually a
by lomnakkus 11y ago
> TypeScript has has union types i.e. "number | string" which are similar to algebraic data types.
I understand that you said "similar", but there's actually a big difference that should be mentioned explicitly, namely that algebraic data type (ADT) sums always have "constructors" which you can use to disambiguate with. That means that you can meaningfully do the equivalent of "int | int" whereas for union types that would just be "int". (I'm sure you already know this, I'm just pointing this out for those who may not know or appreciate the subtleties.) Example:
data Maybe a = Nothing | Just a
In this example the "constructors" are Nothing and Just.
Of course, you can emulate first-order ADT sums using union types by just introducing artifical container classes and doing a union on those. While this works for simple cases, I believe (but cannot prove) that it's impossible to emulate GADTs using union types -- my intuition is that the presence of constructors to match on is an essential part of being able to "narrow" the types sufficiently to actually act upon what they "contain" (for each case).
However, and notwithstanding all of that... the use of union types in TypeScript is absolutely the best way to align with JavaScript since there's so much JS that just takes/returns values of type "whatever" (string | number | ...).
Btw, also agreed on the productivity boost. In the short term, it may not appear that you're getting faster, but once your application starts to grow beyond "trivial" you really start to notice the fact that you can refactor without fear.
- spicyj 11y agoFlow has pretty good support for tagged unions like this which act like ADTs: http://flowtype.org/blog/2015/07/03/Disjoint-Unions.html http://flowtype.org/blog/2015/07/03/Disjoint-Unions.html
- lomnakkus 11y agoThat's interesting! I haven't paid much attention to Flow since trying out the initial release and finding it a bit lacking. (But then, I'm used to GHC/Haskell, so everything lacking!) I truly feel that "disjoint unions" is one of those things you don't appreciate the true value of until you've used them for quite a while. The real eye-opener for me was implementing a state machine and finding that I could explicitly state exactly which bits of the state machine would propagate to $NEXT_STATE at every step... and have the compiler double-check for me that I got it right. EDIT: I accidentally a accidentally.
- fleshweasel 11y agoThis is great. Seems less cumbersome than TypeScript's user defined type guards for distinguishing cases of a union. The sentinel value concept is something you could at least steal when creating user defined type guards to make them simpler, at least.
- wereHamster 11y agoTypeScript union types and Flow tagged unions are nice to have, but to make them really useful you also have to provide a good way to pattern match on them. And there they both are still way behind Haskell.
- fpoling 11y agoThis is really nice with a very good much to common JS patterns. Among other things it types the usage of string literals making possible to typecheck and refactor code that contains if (foo === 'bar').
- fleshweasel 11y agoThanks for elaborating on the distinction. User defined type guards can get you some of the way there to distinguishing "int | int", but you don't get it for free-- the reality of being a superset of JavaScript. https://gist.github.com/RikkiGibson/74fa3dbfdb1b2d7ab86d https://gist.github.com/RikkiGibson/74fa3dbfdb1b2d7ab86d
- lomnakkus 11y agoOh, wow, that's pretty evil code! :)
- jesseschalken 11y agoADTs can be very easily replaced with visitors. type Maybe<T> = <V>(v:{just(x:T):V, nothing():V}) => V let just = <T>(x:T):Maybe<T> => <V>(v:{just(x:T):V}) => v.just(x) let nothing = <T>():Maybe<T> => <V>(v:{nothing():V}) => v.nothing() let values:Maybe<number>[] = [just(6), nothing()] values.forEach(maybe => { alert(maybe({ just: x => 'got: ' + x, nothing: () => 'nothing' })) })
- lomnakkus 11y agoEasy, but absolutely horrid on a large scale. (I mean, just try to enumerate the amount of redundancy you have in that short snippet you posted!)