Remix.run Logo
adrian_b a day ago

The null reference was invented by Hoare as a means to implement optional types, which works regardless of their binary representation.

Optional types were a very valuable invention and the fact that null values have been handled incorrectly in many programming languages or environments is not Hoare's fault.

tialaramex a day ago | parent [-]

Having "Optional types" only makes sense if your type system is powerful enough.

There are two ways this might happen, both will solve the Billion Dollar Problem but I think one is the clear winner. The first way is explicit optionality, often retro-fitted to languages for example in C# the difference between the types Goose and Goose? are that (in a suitable C# project enabling this rule) the first one is always a Goose and the second might be null instead.

The second way is if you have Sum types you can just add "or it's null" to the type.

I think sum types are better because they pass my "three purposes" rule where I can think of not one (Option<T> replaces optionality) or two (Result<T,E> for error handling) but at least three (ControlFlow<B, C> reifies control flow) distinct problems I don't need separate solutions for any more if I have this feature.

If your type system is too weak you suffer the Billion Dollar problem with Hoare's idea and perhaps if this "feature" had never been invented we'd have all migrated to languages with a better type system decades ago.

adrian_b a day ago | parent [-]

I agree that for the correct use of both Optional types and Sum types (a.k.a. Union types) a type system that is powerful enough is essential. Moreover, a convenient syntax is also important.

In my opinion, besides being passed as arguments of functions whose parameters are declared as having the corresponding Optional or Sum type, there is only one other permissible use of values of such types.

Variables of an Optional type shall be allowed in the Boolean expression of an "if" or equivalent conditional statement/expression, while variables of a Sum type shall be allowed in an expression that tests which is the current type in a select/case/switch or whatever is the name used for a conditional statement or expression with multiple branches.

Then in the statements or expressions that are guarded by testing the Optional- or Sum-type variable, that variable shall be used as having the corresponding non-optional type or the type among those possible for a Sum type that has been determined by the test.

This syntax ensures that such variables will not be misused, while also avoiding the excessive and unhelpful verbosity that exists in some languages.

tialaramex 18 hours ago | parent [-]

I disagree. Only true is true and only false is false, Some(1234) isn't true and None isn't false, for the same reason the string "A" isn't the ASCII byte 'A' and the 8-bit unsigned integer 10 isn't the 64-bit floating point number 10.0

When you muddle these things it makes life very slightly easier, very briefly and then you introduce impossible to find bugs and ruin your life. People tend to imagine that OK, maybe C went too far with coercions, but I can handle smaller things, that'll be fine, and in my experience they're wrong.

I like the fact that in Rust I'm expected to write if opt.is_none() rather than just treat it as if it was a boolean when it isn't. The resulting machine code isn't different, so we're talking about communicating our intent to human programmers, such as our future selves or our colleagues and I'm certain opt.is_none() communicates that intent better than coercing it to a boolean.

I don't like the other idea you propose, but it's less of a footgun and so I don't mind that e.g. C# programmers often write this way. I wouldn't choose it myself in many cases, but I don't write "No, fix this" in reviews of such code.