Judging by the hot disputes about null
it should be some kind of the problem, but I have to admit I don’t see any. A lot of people complain about getting NullPointerException
(and alike) as a bad thing — for me it is great. It is the mechanism that stops my program and tells me “listen, the flow of your program is incorrect, I prevented bad things to happen, sit down and fix it”. I am grateful for that because I didn’t add any extra line to my program and yet the runtime keeps me safe. null
values are very useful if you start to treat them as normal citizens among your data, same way as you treat zeros when doing computations.
a = b + c;
Are you sweating because “b
” could be zero?
Some languages try to cover null
“problem” by introducing wrapper class for reference/pointer types (for example Scala with its Option
¹). However what kind of progress here is made with actually renaming null
to None
? I don’t see any ².
Since C# and extension methods I see things differently — to such extent that I started writing extensions instead of regular methods just to allow null
on the caller side (to this day I remember pride of C++ which guarantees “this
” cannot be null
).
In many cases you have to deal manually with null
, period, but in many, especially when the job is related to collections, null
comes naturally. Consider such task — having HTML node extract first “<p>
” node from its children, and then extract all “<span>
” nodes from its children. If you use XPath query you have one sequence of selectors. How come in classic programming you have to add so many guards against null
? Why not solve this without fear of null
?
var spans = ((node??emptyNode)
.Children
.Where(it => it.Name=="p")
.FirstOrDefault()??emptyNode)
.Children
.Where(it => it.Name=="span");
The only problem, or rather annoyance, is C# — not null
.
Kotlin addresses the issue directly but misses the point — instead of introducing coalesce
operator it allows subsequent call/access to fail (Kotlin “returns” null
):
var children = node?.Children;
If “node
” is null
, the variable ”children
” will be null
as well (Fantom and Groovy also took this approach). I am not saying coalesce
is superior in all cases, but adding a feature which moves you from null
domain into the same null
domain does not look like a solution to me.
Skila will use selective Null Object pattern ³ with short coalesce
operator. For every type you can define a constant null object
for coalescing. And then…
var spans = node->Children
.Where(it => it.Name=="p")
.FirstOrDefault()->Children
.Where(it => it.Name=="span");
“spans
” won’t be null! Arrow operator (“->
”) is hidden coalesce
call. Because Skila is statically typed checked, it verifies at compile time whether you can use the power of coalescing (it checks if the null object
is defined). And it does the right thing — it moves you from null
domain into not-null
domain.
This is probably not a complete blueprint — practice will be the best judge — but even upon completion it won’t be a magical wand. If you work on types that are collections, adding coalesce
is like adding a grease into the gears, in other cases you should rely on old “if
”, and for the rest — NPE comes to the rescue.
¹ I say ”yes” to Option, but not instead of null pointers, but as addition to them.
² Why Scala’s “Option” and Haskell’s “Maybe” types won’t save you from null (good read, but the example with Dictionary
is weak).
³ There might a be a little confusion what the true “Null Object” pattern is, so let’s say Kotlin, Fantom, Groovy use thin null object
(no body, everything falls into nulls
), while Skila will use fat objects, with real body defined by user. This will allow to transform null
collections into empty ones.