3 ms·
Good article, but there is no "choice". You need both and the article even explains why! You need union types if you have subtyping to avoid wrapping/unwrappin
by valenterry 6y ago
Good article, but there is no "choice". You need both and the article even explains why!
You need union types if you have subtyping to avoid wrapping/unwrapping all the time.
And you also need option/maybe types for the cases where you must not lose information at runtime. These types can implemented by union types even, by simply "tagging" (= wrapping) the runtime values with a class).
A good language gives you both out of the box.
- creata 6y ago> You need union types if you have subtyping to avoid wrapping/unwrapping all the time. In my experience, the option types are always more concise. That's mostly because having option types encourages people to build out nice helper functions like these[0] and these[1]. Haskell's do notation and Rust's Try notation also save a lot of space. [0]: https://doc.rust-lang.org/std/option/enum.Option.html https://doc.rust-lang.org/std/option/enum.Option.html [1]: https://hackage.haskell.org/package/base-4.14.0.0/docs/Control-Monad.html https://hackage.haskell.org/package/base-4.14.0.0/docs/Contr...
- valenterry 6y agoI was actually not referring to specifically option types but generally sum types. E.g. inside a function I have a result of (union) type "Integer | ErrorA" in one branch and "Integer | ErrorB" in another branch. ErrorA and ErrorB are completely unrelated types (coming from thirdparty libraries, can't change them). With union types I don't have to do anything and the return type for my function will be inferred as "Integer | ErrorA | ErrorB". Or if you prefer to work with a result type / either type, then it will become "Either Integer (ErrorA | ErrorB)". In most languages I'd have to make it "Either Integer (Either ErrorA ErrorB)" and also change the function code by lifting accordingly. That's verbose and many times I don't want to do that.
- Cyph0n 6y agoIn Rust, you would implement From<ErrorA> and From<ErrorB> for a custom Error enum defined in your application/library. This will allow you to convert any ErrorA or ErrorB into Error. Then, you could simply return Result<Integer, Error> from your function, and the conversion would happen for you behind the scenes. Of course, unless you track the original error types, you will lose some information. But it’s a very clean way to handle multiple error types within your library imo. An example of how this works: https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/wrap_error.html https://doc.rust-lang.org/rust-by-example/error/multiple_err...
- valenterry 6y ago> This will allow you to convert any ErrorA or ErrorB into Error. I chose my example for cases where I want to keep track of the different error cases. But even if I don't, I still have a _lot_ of overhead for converting them. Unfortunately Rust isn't a language that offers a solution to this problem so far. But there is an open RFC for it: https://github.com/rust-lang/rfcs/issues/294 https://github.com/rust-lang/rfcs/issues/294
- Cyph0n 6y agoI see. Yeah, this approach breaks down a bit if the error conversion logic is non-trivial.
- valenterry 6y agoActually, I'm wrong here, it's tagged union types not plain ones. But some commentary in there explains it better than me, so still helpful.