Null has no type, but Maybe has by Mark Seemann
In C#, null has no type, but most variables can be null; you can't really trust the type system. A Maybe, on the other hand, always has a type, which means that Maybe is a saner approach to the question of values that may or may not be present.
A few days ago, I was looking at some C# code that, reduced to essentials, looked like this:
string foo = null;
var isNullAString = foo is string;
What is the value of
isNullAString after execution?
foo is declared as a string, I thought that the answer clearly had to be
true. Much to my surprise, it turns out that it's
Wondering if I was exceptionally bad at predicting the type of null values, I created a Twitter poll. 235 votes later, the outcome was this:
Forty-four percent of respondents (some 103 people) were as wrong as I was! At one point, while the poll was still open and some 100 people had responded, the distribution was even fifty-fifty. Ultimately, I believe that the final results are artificially skewed toward
false, because people could try the code first, before answering, and there's evidence that at least one person did that.
In short, that a null string isn't a string doesn't make much sense to a lot of people.
It's not a bug, though. It's explicitly stated in section 7.10.10 of the C# language specification:
"If E is [...] the null literal, of if the type of E is a reference type or a nullable type and the value of E is null, the result is false."The specification doesn't offer much of an explanation, but Eric Lippert shares some information on the topic.
It still doesn't make any sense to me...
Apparently, the rules of C#'s type system is: a variable is guaranteed to be of a certain type, except when it isn't. Once again,
null throws a wrench into any attempt to reason sanely about code.
The .NET Rocks! episode about less is more sparked a ton of comments; most of them in defence of
null. People don't seem to understand just how malicious null references are. That null has no type is yet another example.
I think that the main reason that people defend null is that they have a hard time imagining other ways of modelling situations where a value may or may not be present. Even when introduced to the Maybe monad, most people remain unconvinced, because it's difficult to understand how Maybe is better than null.
The difference is clear: only values explicitly declared as Maybes can be Maybes, and Maybe values always have a type!
In F#, Maybe is called
option, and it's always typed. The logical equivalent of the above type check would be this in F#:
let foo : string option = None
let isNoneAStringOption = foo :? string option
Only, this doesn't even compile!
If you try this in F#, the compiler will complain:
"error FS0016: The type 'string option' does not have any proper subtypes and cannot be used as the source of a type test or runtime coercion."That expression doesn't even make sense in F#. Of course
string option, because it's the only thing it can be!
You'll have to upcast
obj in order to be able to perform the type check:
let foo : string option = None
let isNoneAStringOption = box foo :? string option
As expected, this evaluates to
true. Of course
true, even when it's
None! What else could it possibly be?
In Haskell, it doesn't even make sense to ask such a question, because there's no type hierarchy. In Haskell, you can't upcast a value to its base type, because there's no inheritance.
In short, null values invalidate all rules and guarantees that the C# type system attempts to make. It's truly a toxic language 'feature'.