Great article. Go may have generics, but because old Go code doesn't, and the standard library doesn't, it still feels like the language doesn't sometimes.
Basically learning the lessons of Java and C# all over again. Yes, there are features you can defer implementing until later, but their absence infects everything until you do. I still see cases where people have to drop down into ADO.Net code for C#, and the fact that you still see DBNull.Value instead of just a simple null value, much less a proper Option type is infuriating.
Like I write a lot of Powershell code and their stuff still returns DBNull.Value when returning a null value from the DB, when nullable value-types were introduced almost 20 years ago.
The documentation for DBNull [1] seems to have some idea that DBNull represents something entirely different.
> Do not confuse the notion of null in an object-oriented programming language with a DBNull object. In an object-oriented programming language, null means the absence of a reference to an object. DBNull represents an uninitialized variant or nonexistent database column.
For all the explanation though, I couldn't fathom what its talking about.
I think it's exactly the same issue as null in Lisp and Lua — you sometimes want to differentiate between null as in "I returned no value", and null as in "I returned the fact that there is no value". Or null vs false vs empty list in the context of Lisp.
This distinction becomes very clear (and sometimes very annoying) when you realize that in Lua, setting a table key to null completely removes it, so there's no way to store the concept of a missing value unless you define a special value (like DBNull). A slot being null literally signifies its absence.
There is no difference between "no value" and "the fact that there is no value". "The fact that" is just rhetorical verbiage.
There is an ambiguity in a polymorphic container between a present entry indicating a null value, and a null return indicating there is no entry.
E.g. hash table being used to represent global variables. We'd like to diagnose it when a nonexistent variable is accessed, while allowing access to variables that exist, but have a null value.
This is because the variables are polymorphic and can hold anything.
When we are dealing with a typed situation (all entries are expected to be of a certain type, and null is not a member of that type's domain) then there is no ambiguity: if a null appears in the search for a value that must be a string, it doesn't matter whether "not found" is being represented by an explicit entry with a null value, or absence of an entry.
This rhetorical verbiage matters a lot in a language like Lua where you can pack an array full of dbnull sentinel types but filling it with nil results in an empty array.
I don't know lua. But the behavior you describe seems like a quirk of lua tables. Instead of having `table.get(key)` return a sentinel value, you can replace it with `table.has_key(key)` and `table.get(key)`.
I'm not sure about the ergonomics of the trade in lua. But in C# the ergonomics of `DBNull` are terrible. If it were just replaced with `null` everywhere, everything would just be better.
> Yes, there are features you can defer implementing until later, but their absence infects everything until you do. I still see cases where people have to drop down into ADO.Net code for C#, and the fact that you still see DBNull.Value instead of just a simple null value, much less a proper Option type is infuriating.
This DBNull.Value may be a problem for C#, but idiomatic Go has largely been untouched by the introduction of generics.
Like I write a lot of Powershell code and their stuff still returns DBNull.Value when returning a null value from the DB, when nullable value-types were introduced almost 20 years ago.