4 ms·
> [...] made the final convincing argument as to why no serious typed language should have null. Null is a perfectly valid and very useful feature, if modeled
by tadfisher 2y ago
> [...] made the final convincing argument as to why no serious typed language should have null.
Null is a perfectly valid and very useful feature, if modeled in the type system. A nullable reference of type, say, "T?", would accept values accepted by T or the special value "null", which is equivalent to a union type of "T | Void" (or whatever type is assigned to "null" in your language).
The billion-dollar-mistake is not the existence of null references, but the inability to model nullability in the type system, thus making references implicitly nullable. You can ban the concept of null from your language, but you're inevitably going to add lots of syntax sugar or helper functions to unwrap "Optional<T>" or "Maybe t" values, so making nullability a "type modifier" has equivalent behavior and, IMO, better ergonomics.
- Nullabillity 2y ago> Null is a perfectly valid and very useful feature, if modeled in the type system. No it isn't, because union types leak through generics in surprising ways. For example: // value is null if not loaded yet struct AsyncSnapshot<T> { value: T? } // returns null if user is not found fn getUser(id: Id<User>): User? fn runInBackground<T>(f: () -> T): Stream<AsyncSnapshot<T>> If you run `runInBackground({ getUser(id) })`, `AsyncSnapshot<T?>`'s `value` gets the type `T??` which is typically coalesced to `T?` and you can't tell whether it's `null` because there is no user, or because it's still loading. Sum types like `Option<T>` do not coalesce, `Option<Option<T>>` is distinct from `Option<T>`. Better yet, you can declare your own clearer sum types, so you can have `MaybeLoaded<MaybeExists<User>>` rather than have to remember which layer is which.
- tadfisher 2y agoThat doesn't make it not useful, but I agree with your conclusion.