Why nullable types?
31–40 of 119 posts
Re: Why nullable types?
#32Earlier quoted context omitted.
I generally agree with you but the one point they make in this article that really matters for Dart is it does not have pattern matching. Dart’s nominative type system makes much of standard FP much harder than you’d imagine. I know Rust is nominal too but Rust has a lot of complexity to support these things. It is very simple to add a Maybe and Either type to Dart (my code base has variants of these to deal with nul…
That's an interesting counter point, though another comment in a different post for this same article[1] pointed out that Dart is considering adding pattern matching too. Though I have trouble understanding how a nominal type system affects this? Does dart not have generics? That's all you need to support these patterns? I'm having trouble understanding how the type of type system plays a role here. Also, what's 'imp…
Dart is considering adding pattern matching (and real tuples!) and I think they will eventually get there. I just think that would have had to come first to make the case of optionals over nullable types more compelling.
I would call that example destructing in the normal sense and it is basically a form of pattern matching. To do that in an imperative language without pattern matching (i.e. what I am calling imperative destructuring), you end up with code like
if (maybeFoo.isSome) {
final foo = maybeFoo.asValue;
...
}
when you really want if let Some(foo) = maybeFoo {...}Re: Why nullable types?
#33Good 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…
> 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 [1]: https://hacka…
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.
Re: Why nullable types?
#34> it is entirely possible to live without null, and languages like Rust do. It seems as though there is this common misconception that Rust does not have a concept of null. Rust does have null pointers [1]! The reason many people do not see null often is because working with and dereferencing raw pointers is an unsafe operation 1: https://doc.rust-lang.org/std/ptr/fn.null.html
But most users would rarely if ever need to use that type. Option is how Rust deals with optional/nullable values.
Re: Why nullable types?
#35>There are two main solutions: Use an option or maybe type or Use a nullable type I don't get it. These are literally all exactly the same thing, all slightly varying in ergonomics and compiler support. They may differ slightly but to call them two different categories of solution is just creating a false dichotomy for yourself. > However, the type system is a little more flexible than with option types. The type sys…
String s = null;
But this is not legal in languages without them:
String s = None;
In those languages, in order to have a value of None, it must be of type Option (or whatever the syntax is), and in order to get a value of type String, you must assert its presence.
This is a fundamental difference with many ramifications. It really isn't just "the same but with better compile support".
Re: Why nullable types?
#36Earlier quoted context omitted.
> 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 [1]: https://hacka…
I 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 "I…
Then, you could simply return Result 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_err...
Re: Why nullable types?
#37>There are two main solutions: Use an option or maybe type or Use a nullable type I don't get it. These are literally all exactly the same thing, all slightly varying in ergonomics and compiler support. They may differ slightly but to call them two different categories of solution is just creating a false dichotomy for yourself. > However, the type system is a little more flexible than with option types. The type sys…
No, this is valid in languages with nullable types: String s = null; But this is not legal in languages without them: String s = None; In those languages, in order to have a value of None, it must be of type Option (or whatever the syntax is), and in order to get a value of type String, you must assert its presence. This is a fundamental difference with many ramifications. It really isn't just "the same but with bett…
Re: Why nullable types?
#38Earlier quoted context omitted.
I 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 "I…
In Rust, you would implement From and From 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 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…
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
Re: Why nullable types?
#39>There are two main solutions: Use an option or maybe type or Use a nullable type I don't get it. These are literally all exactly the same thing, all slightly varying in ergonomics and compiler support. They may differ slightly but to call them two different categories of solution is just creating a false dichotomy for yourself. > However, the type system is a little more flexible than with option types. The type sys…
In haskell, Either a (Either b c) is distinct from Either (Either a b) c. In typescript, a | (b | c) is identical to (a | b) | c. And further, x | x is identical to x in typescript.
The upshot is that Haskell (using Either instead of Maybe for clarity) lets you have Either (Either a ()) () as distinct from Either a (), but the equivalent typescript is (a | null) | null, which is equivalent to a | (null | null) which is further equivalent to a | null.
So nullable types are less powerful than option types. I'm not certain that they're worse, because dealing with nested option types can get confusing, and maybe making them impossible to express would encourage API design that requires less thinking, but unfortunately I think it's more likely that it would lead to API design with more footguns.
Re: Why nullable types?
#40> it is entirely possible to live without null, and languages like Rust do. It seems as though there is this common misconception that Rust does not have a concept of null. Rust does have null pointers [1]! The reason many people do not see null often is because working with and dereferencing raw pointers is an unsafe operation 1: https://doc.rust-lang.org/std/ptr/fn.null.html