4 ms·
The answer is simple: because other languages don't need it - they have different features to deal with it. The author even mentions it: pattern matching. Just
by valenterry 5y ago
The answer is simple: because other languages don't need it - they have different features to deal with it. The author even mentions it: pattern matching.
Just that he picks a language that doesn't support union-types. But that doesn't mean that flow typing would be necessary here - it means that the language(s) should support union-types and extend their pattern matching accordingly.
In fact, I would say that flow typing is almost like a workaround for missing pattern matching.
- lambdasquirrel 5y agoYeah, the same code would look much clearer in a language with union types. Heck even Swift cribbed them. switch resp { case Result(val, meta): doStuff(val) case Error(msg, code): logger.main.debug(msg) }
- dragonwriter 5y agoYou mean sum types, not union types (TypeScript has union types, your example uses sum types.)
- marcosdumay 5y agoAn union type is the C attempt to implement something like sum types, that other languages extend a bit to literally implement sum types. Those two words live on different contexts, as unions are about a memory usage pattern, and sum types are about conceptual software design, but they often go together on the same language feature.
- munificent 5y ago> An union type is the C attempt to implement something like sum types Sum types, unions (in the C sense), and union types are three distinct concepts. Wikipedia's article confusingly merges the latter two concepts together but they're different thing. TypeScript[1] and Scala[2] have union types, but they have nothing like C's notion of "unsafely reuse the same bits in memory to be interpreted as different things". [1]: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types https://www.typescriptlang.org/docs/handbook/2/everyday-type... [2]: https://dotty.epfl.ch/docs/reference/new-types/union-types.html https://dotty.epfl.ch/docs/reference/new-types/union-types.h...
- lambdasquirrel 5y agoI did mean sum types. Thank you for the correction.
- parhamn 5y ago> In fact, I would say that flow typing is almost like a workaround for missing pattern matching. If flow typing is 'type narrowing' (the article kinda dragged on pulling in unrelated concepts so it lost me), then if anything, it is at least as good as pattern-matching/switch-blocks. At least in Typescript. That is because it works in switch statements and non-switch statements and provides the same guarantees. I don't get this argument. Consider something like: data: {pages: number} | null = f() if (!data){ return 0 } return data.pages + 5 Sure you can switch on the 'data' as well (exhaustive type-switches are a thing in Typescript too), but thats a syntactic preference. It surely is nice to get the same guarantees without _needing_ a switch statement.
- deleted 5y ago[deleted]
- valenterry 5y ago> I don't get this argument. Maybe it's because you focus on typescript here. But typescript is in a position that other languages are not: it has to make things work within an existing ecosystem, in particular with an untyped language. Under these constraints, flow typing might be the best solution. But that doesn't mean it's generally "at least as good as pattern-matching/switch-blocks". Btw, pattern matching and switch-blocks are very different things - maybe it make it easier for you to understand to dive into a language that has native pattern matching and no switch-blocks at all because from what you write it seems to me that you have not been exposed to pattern matching much yet.
- masklinn 5y ago> It surely is nice to get the same guarantees without _needing_ a switch statement. Is it? let data: Some(usize) = f(); match data { Some(pages) => pages + 5, None => 0 } Seems pretty nice. Or if you want a more structurally similar code with a guard (and removing the useless bits): let Some(pages) = f() else { return 0 }; pages + 5
- parhamn 5y ago
- _old_dude_ 5y agoYou need flow typing when you do pattern matching on generalized abstract data type (GADT). Here is an example in Java sealed interface Foo<T> permits Impl {} // union type record Impl() implements Foo<String> {} // product type <T> T m(Foo<T> foo) { return switch(foo) { case Impl impl -> "hello"; // flow typing T=String }; }
- valenterry 5y agoBy that definition, many (maybe most) languages then already support flow typing.
- whimsicalism 5y agothat's not the same as what is defined in the article, and it is supported by plenty of languages
- alduin32 5y agoI don't think pattern matching and union-types makes narrow typing useless. Rust has both pattern matching and union types but still implements some specific forms of control-flow based typing. For example, consider this Rust snippet : pub fn positive1(x: isize) -> Option<usize> { if x > 0 { Some(x as usize) } else { None } } Unless I'm mistaken, without narrow typing, this cast would be impossible, and there would be no way to avoid the redundant runtime check caused by try_into().unwrap(), that is not even optimized unless you trick the compiler.
- dragonwriter 5y ago> Unless I'm mistaken, without narrow typing, this cast would be impossible same sized integer casts in Rusts are no-ops [0]; the conditional isn't type narrowing, it just avoids the cases where the cast would not preserve the same semantic value. [0] https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions https://doc.rust-lang.org/reference/expressions/operator-exp...
- alduin32 5y agoRight. Thank your for the correction.