
The Logical Disaster of Null - matthewwarren
https://rob.conery.io/2018/05/01/the-logical-disaster-of-null/
======
jasode
_> I’m not sure Null should have a place in programming. _

The essay is conflating 2 different concepts of "null."

The Tony Hoare quote is about null _references_ which is about _aliases_ to
computer memory. (E.g. the dreaded NPE or null pointer exception.) He's not
talking about _logical_ nulls such as "tri-state booleans" or "missing data".

The logic-variant of Null usage for "unknown" "missing" "invalid" values is
unavoidable in programming. This is why that null concept shows up repeatedly
in different forms such as NaN in floating point, NULL in SQL language, etc.
If you invented a theoretical language without logical Nulls, the users of
your language would _reinvent nulls_ using worse techniques such as homemade
structs with an extra boolean "HasValue" field. E.g.:

    
    
      struct NullableInteger {
        int x;
        bool hasvalue;
      }
    

The "hasvalue" field becomes a "null by convention". Other programmers might
not even code a verbose extra boolean variable and instead use (dangerous)
sentinel values such as INT_MAX "32767" or negative value such as "-1" as the
"pseudo null" to represent missing data. A lot of old COBOL programs had 99999
as some out-of-range value to represent missing data. We need nulls in
programming languages because they are useful to model real-world (lack of)
information. All those other clunky techniques will reinvent the same kinds of
"null" programming errors!

The orthogonal aspect is making the _type system_ more powerfully _aware_ of
nulls so that the compiler sees that possible null conditions were not
checked. A compiler error forces the programmer to put in the _explicit_
defensive code to _handle the possibility of null values_.

~~~
Zak
I find it weird that people keep making languages with static typing where all
types can also be null instead of using an option type. Doing so nullifies a
significant amount of the correctness checking static typing provides.

It makes some sense for interoperation when living inside .NET or the JVM, but
this was a known problem when _those_ systems were designed.

~~~
davidgay
1\. It's hard to support circular data structures in an imperative language
without null pointers.

2\. Restricting a language to non-circular data structures will not be
popular.

3\. Having a nullable and non-nullable version of every pointer or reference
type is not usually super popular either.

Phrased differently: nullable types are actually a fairly reasonable
compromise from a set of not fully appealing choices.

~~~
skybrian
Having nullable and non-nullable versions of every type is fine if you have
decent syntax for it. Take Kotlin for example:

[https://kotlinlang.org/docs/reference/null-
safety.html](https://kotlinlang.org/docs/reference/null-safety.html)

~~~
chaosite
Well, not really. What Kotlin has is exactly an Optional type. It's just a
different syntax for it.

~~~
Zarel
There's one difference between nullable/non-nullable types and Optional type,
which is nesting, right?

I'd assume that while Kotlin's String? is equivalent to Optional<String>,
Kotlin has no equivalent to Optional<Optional<String>>.

Which, personally, I think I'd prefer Kotlin's approach, because that means
you can do `maybeString = "exists"`, which is more readable than `maybeString
= Optional::Exists("exists")`

~~~
chaosite
Sure. In Haskell you join them, but just not allowing it perhaps better.

Also, you can still nest them in Kotlin, since you can have a nullable type
inside generics.

------
paulhodge
I have a pet peeve whenever people bring up the old "Billion dollar mistake"
quote. Calling it a mistake means that it could have been avoided. But we
couldn't have avoided null pointer errors (or some other name for the same
thing) any more than humans could have avoided the bronze age. In the 60s and
70s we didn't have the "technology" to avoid null errors. By technology I mean
production-ready languages with a static typing layer sophisticated enough to
implement a Maybe/Optional type. Those languages weren't production ready
until maybe around the 80s. And most of those compilers were themselves
implemented in unsafe-null languages like C.

But anyway, that's history, now we have nice languages that have safe nulls,
so the interesting questions moving forward are: why are people still creating
new languages that have unsafe nulls (looking at you Go), and why are people
still choosing to use languages with unsafe nulls?

~~~
alkonaut
There were better languages but Unix won, and with that - C. A mistake we
(whoever “we” are) repeated with JavaScript - we let the language win that was
“there”, not the one we really needed.

Worse is better. Ease of deployment over ease of development.

As for the Q: why design languages with null (that is - incomplete or flawed
type systems) _now_? I have o idea. I think the reasoning is that “worse is
better” succeeded for JS, php, C, so it’s a viable path.

“Maybe an X” and “Definitely an X” are more different than string and number.
If a language pretends string and number are distinct but at the same time has
no distiction for “maybe X” - then it doesn’t have a very good type system.

Note that it doesn’t necessarily need to avoid null values for this. Non-
nullables is mostly equivalent although less elegant. That is, “String s”
means a string or null, while “String! s” means a non-null string (example
from a C# vNext syntax).

~~~
ItsMe000001
> we let the language win that was “there”, not the one we really needed.

Different explanation of history: Something "wins" because it is _precisely_
what is needed.

~~~
alkonaut
It does - but I think sometimes somethinb should have “won” that solved the
short term problem 95% as well, but solves the problems over the next 4
decades better The Q is why individuals and companies would pay short term for
gains to others.

------
skywhopper
I disagree with the author here, and while I admit that Null failures are a
pain, it's not true to say they don't exist in other languages. "Maybe" and
"Option" etc may force you to deal with them before they will compile, but
that's not the same thing as saying the concept of Null does not exist in
those languages. I'm sure there are some languages out there without any
concept of Null, but they would have their own shortcomings for the lack of
it.

It's also wrong to suggest that Null has no place in "logic". Boolean logic is
one type of logic, but it's not the only type.

Finally, the examples of how Null works in various languages are really poor.
"Why doesn't Ruby coerce nil into zero?" Because it doesn't coerce any types
implicitly. Why should nil be the exception? How is that expectation
"logical"?

~~~
flipgimble
I think you misunderstood something about the languages mentioned like Swift
or Haskell. They do have a way to represent the concept of "nothing", or
"missing" using an enumeration (called Optional in Swift, Maybe in Haskell).

What they don't have is the global concept of Null that sits at the root of
the type hierarchy. This means every type in a language like C# or Javascript
has to have Null as one of its members. If you want to define some
operators/functions on the members of the type, have have to always consider
Null as a member. Like the author listed: How do you compare Null, how do you
negate Null? Those questions shouldn't even be asked because Null is not
negatable or comparable. Maybe the problem is that Null shouldn't have been
admitted as member of a type that you consider comparable or negatable.

So in Swift/Haskell, when you define a type you don't consider "nothing" as
one of its members.

The mental model in Null-using-languages is that a Type is a sort of blueprint
that can be stamped out to make instances of that type. From that point of
view its natural to consider you may be missing an instance.

In language like Swift or Haskell the concept of a Type is closer to a set in
math; in this case the set of all possible values of that type. This is why I
said above that Null has to be considered as a member of a type. I’m using
math language to re-interpret the type-as-blueprint world view, to show why it
leads to illogical results.

Seriously, Swift demoting of the concept of “nothing” to just another enum is
alone worth the price of admission. This has cascading consequences that lead
to safer code. You also get simpler code as you strive to resolve the
uncertainty of a missing value as early as possible in your code. Swift is the
most impressive language I've seen in a while, but think its origins at Apple
have overshadowed the its incredible technical value.

~~~
yxhuvud
Have you used any language that has a type system powerful enough to support
arbitrary sumtypes (and also has null as a separate type)? If not then I can
recommend playing around some with Crystal.

IMO optimals are just a crutch to make up for a too weak type system.

~~~
annabellish
Given a sufficiently powerful type system, you should still err towards an
Option type, because you don't want to have to make two types for everything
you want to represent - the real type, and the real type but also null. Having
an Option type lets you compose that behaviour, even if, yes, it's as simple
to define as `type Maybe x = Just x | Nothing`.

~~~
RX14
No, optional really isn't needed in crystal. You can just add `| Nil` to any
type wherever you use it. We ever have a short cut in the type syntax of the
language `?` to add null to a type union. So in practice at the type layer all
you're doing is replacing Maybe Type with Type?. But now you have a strictly
more powerful construct which behaves like options in some ways (you can call
try on any union with nil because all types including nil implement the try
method) but supports flow typing for null checking, removing all of the extra
dereferencing syntax for option types.

~~~
flipgimble
This is also how Swift implements it support for optional. So far I don’t see
a difference in expressive power between the two languages. A user never has
to actually type out Optional, when adding ? is enough. Swift also has
optional chaining and nil coalescing operators as syntactic sugar. Under the
hood there is still an Optional type for the type checker to work with.

I think this is becoming a trend in modern language design. However as this
comments section demonstrates it’s hard to understand its benefits or why it’s
an important improvement, if your only experience is from C/C++/C#/Java etc

~~~
RX14
Does swift have flow typing? The expressive power of sum types with nil is
only exposed with flow typing. Also I don't think swift supports arbitrary sum
types it just has a "fake" sum type syntax that only works with null. In
crystal you can have an `Int32 | String` just the same as you can have an
Int32?

~~~
AnimalMuppet
I've seen this come up a couple of times in this discussion, so ELI5: What is
"type flowing"?

~~~
RX14
Flow typing is where the type of a variable changes based on control flow.

Consider:

    
    
        x : Int32 | String
        if x.is_a? Int32
          # typeof(x) == Int32
        else
          # typeof(x) == String
        end

~~~
AnimalMuppet
Thanks!

------
chrisoverzero
> No one seems to know why C# behaves the way it does.

Operators are lifted[0] over nullability in C#. Every value type T can be
converted implicitly to Nullable<T>. When `10 * null` is typechecked, both
sides of * are typed as Nullable<T>. The * operator then acts like the pseudo-
Haskell

    
    
      (*) <$> 10 <*> null
    

or, I guess

    
    
      liftA2 (*) (Just 10) Nothing
    

The semantics of Nullable<T> are similar to those of an optional type, with
some implicit mapping and lifting. In that context, null acts less like null.
(Thanks to convenient conversions.) Note that the following doesn't throw:

    
    
      int? value = null;
      value.HasValue // == false
    

[0]:
[https://blogs.msdn.microsoft.com/ericlippert/2007/06/27/what...](https://blogs.msdn.microsoft.com/ericlippert/2007/06/27/what-
exactly-does-lifted-mean/)

~~~
juliangoldsmith
I came here to write this. What C# does in this case makes sense if you take
the time to pick apart what it's actually doing.

------
hamstercat
If anyone wants to see for themselves what life is without null, TypeScript
has an option to type-check for possible null/undefined values with
strictNullChecks[0]. It's a game changer. The con: you have to check every
variable that could be null/undefined before using it. The pro: you have to
check every variable that could be null/undefined before using it :)

[0]
[https://basarat.gitbooks.io/typescript/docs/options/strictNu...](https://basarat.gitbooks.io/typescript/docs/options/strictNullChecks.html)

~~~
mushishi
Our stack is Typescript and Kotlin, and I find handling of null simple and
safe, especially in Kotlin there is decent supporting functionality
[https://kotlinlang.org/docs/reference/null-
safety.html](https://kotlinlang.org/docs/reference/null-safety.html)

With Java we used @NotNull annotations and IDE support to give warnings.
Luckily we are converting our legacy code base to Kotlin.

With conditional type conditions Typescript will have better support than
earlier:

    
    
        type NonNullable<T> = Diff<T, null | undefined>;  // Remove null and undefined from T

Source and the examples in:
[https://github.com/Microsoft/TypeScript/pull/21316](https://github.com/Microsoft/TypeScript/pull/21316)

------
hprotagonist
_These laws apply to logical expressions about things that exist. In other
words: you can’t apply these laws to the unknown, which also includes the
future. This is where we arrive at the edge of the rabbit hole: null
represents nothingness /unknownness, so what the hell is it doing in a
deterministic system of 1s and 0s?

Computer systems are purely logical, but the applications that we write, if
they embrace Nulls, are apparently not. Null is neither true nor false, though
it can be coerced through a truthy operation, so it violates Identity and
Contradiction. It also violates Excluded Middle for the same reason. So why is
it even there?_

And suddenly a bunch of type theorists just winced. It is possible to have
trivalent logic that's coherent. SQL does this -- and it makes sense to do it
in that application.

~~~
mjburgess
I really don't like throwing the word "pseudo-intellectual" around because its
mostly used to mean "I think I'm smarter than you".

However, this person begins their article poisoning the well, saying,

> I find that the people I talk to about this instantly flip on the
> condescension switch and try to mansplain this shit to me as if.

Proceeds to speak in that very same condescending tone, presenting a smarter-
than-thou trundle down from the mountain. All the while including some
nonesense about logic.

This person clearly has a chip on their shoulder: tacking on things about
community-regulation in an article about Null, like all technical public
engagement authors do. Let's signal "what good behaviour we expect" in the
midst of a book pitch and a confused discussion of a technical matter.

One of these goals can be failed in its attempt with good nature. When you
smush all this together it seems an exercise in performative intellectualism,
or more accurately, pseudo-intellectualism.

------
_bxg1
I think this opinion makes some sense from a pure-functional standpoint, but
not from an imperative one. In imperative programming, where you're managing
lots of state, it's important to be able to represent the concept of a slot
with nothing in it.

That said, I think a large portion of the problems caused by null could have
been avoided by making one small change to its behavior: _accessing a member
(or element) of a null should evaluate to null instead of throwing_. This is
deeply intuitive and is the biggest cause of null pointer exceptions. If you
try to access a.b, and a is null, it makes perfect sense that b is also null.
Many languages have recently started adding an "optional chaining" operator
that works this way, but a lot of pain could've been avoided if things were
this way from the start.

~~~
vbezhenar
I don't agree that this is a good behavior. I rarely need that behavior. NPE
means that I forgot to handle null. And handling null usually means something
other than use null for a result. NPE is hard to miss and stacktrace points
exactly to the code that should be fixed. Propagating null will hide the code
that should be fixed. I think that NPE behavior is the best solution without
changing type system (and null-aware type system is just the best solution).

~~~
iaml
That's just your personal experience. Here's one from me: I use it all the
time. I wish this was how js worked or that chaining operator would become
standard soon. Reason is, there are times where you need some deeply nested
variable in state, but it may not be already initialized, so you need to check
it every time you access it. Gets tiring really fast.

------
YeGoblynQueenne
>> Logically-speaking, there is no such thing as Null (...)

Oh but there is.

The author is probably considering only propositional calculi with two truth
values, such as Boolean algebra and, er, well, they're probably only
considering Boolean algebra because that's what we use in computers, because
it maps nicely to 0s and 1s.

However, two-valued logics are by no means the only possible logics, neither
are they the only ones that have actually been described in the framework of
mathematical logic. Probably the most well-known many-valued logics are
Łukasiewicz's and Kleene's that have three truth values (i.e. values assigned
to literals): true, false ...and unknown.

... which is to say, "null".

And just to blow your mind, there are also infinite-valued logics, like fuzzy
logic and, I'd argue, the good old probability calculus of the reverend Bayes,
which is nothing if not a many-valued logic.

The mistake is, I think, that the author is taking "logic" to mean
Aristotelian logic, however that is not at all the logic we use in computers.
Like I say above, computers use Boolean algebra which is an entirely different
formal system with its own axioms, separate to grandpa Aristotle's own. For
instance- Aristotle never said anything about functions, mappings from the set
of literals to {0,1}, neither did he formally define the algebra of the
Boolean operators AND, OR and NOT (a.k.a. conjunction, disjunction and
negation). Although you can project Aristotelian logic onto Boolean logic,
they are far from the same and I would really struggle to see how one would
implement a programmable computer using Aristotelian logic.

P.S. Am I mansplaining now? Wouldn't that be a little ...weird?

~~~
WkndTriathlete
>> Kleene's that have three truth values (i.e. values assigned to literals):
true, false ...and unknown. ... which is to say, "null".

Regardless of what logics exist the statement you make - that "unknown" is
"null" \- is actually wrong and the heart of the problem. "Unknown" is only
one semantic interpretation of null; there are many others, and problems arise
in software development because of those (sometimes slight) differences in the
interpretation of what "null" should reflect in the real world.

Some sources suggest as many as 129 possible semantic meanings for "null",
which is why Date rails against "null" values in SQL. With Option/Maybe we're
constrained to the universe of values of that type plus exactly one more value
(None); with null who knows how many of the 129 possible meanings of the null
value we are dealing with in addition to the base type? Null is
computationally and mentally expensive to deal with.

~~~
zeth___
The same is true for true and false. What meaning gets attached to Boolean
values is immaterial to how they act.

That null is implemented oddly doesn't negate the fact it's a meaningful and
well defined mathematical construct.

I don't see people saying false doesn't exist because forth implements it as
-1.

~~~
YeGoblynQueenne
This.

Edit: removed unnecessary blablah.

------
camdenreslink
The functional paradigm answer to this is to have an Option<T> type, or
Result<TSuccess, TFailure>, which allow you to flexibly account for the “null
case” while still having total functions
([https://en.m.wikipedia.org/wiki/Total_functional_programming](https://en.m.wikipedia.org/wiki/Total_functional_programming)).

The only catch, is now you have to unwrap your function results somehow
(functional languages provide ways to do that, like the scary monad).

I like this website, which demonstrates the concept:
[https://fsharpforfunandprofit.com/rop/](https://fsharpforfunandprofit.com/rop/)

~~~
barrkel
Option monads have the same semantics as C# null described in the article;
they don't apply the function if the value isn't present, you get another non-
present value back out.

Result<,> has the same semantics as checked exceptions, BTW. The conversion
between the two representations of code is mechanical.

Checked exceptions are a poor idea the more dynamically bound your language
is, and are generally anti-abstraction in any case (failure modes are
implementation specific, i.e. non-conformant with an information hiding
interface). An error result is only useful if you can make a decision based on
the specific value; that's not the case for almost all sources of error in
most user (i.e. non-system) programs, where complete coverage of error cases
with specific handlers is outside their design parameters, and termination (of
program or request or whatever) is preferable.

~~~
camdenreslink
It’s about the ability to reason about the code. Option types force matching
expressions whenever you encounter the wrapped value, forcing the developer to
handle the failure case.

Equivalent C# code is to add checks for nulls all over the place, aka
“defensive coding”. C# has an Option type sort of with nullable types, but
they are for value types only, and developers can still reach in and just grab
the value, eliminating the safety that option types provide.

------
FilWisher
I don't mean to defend Null.

 _> Null is neither true nor false, though it can be coerced through a truthy
operation, so it violates Identity and Contradiction. It also violates
Excluded Middle for the same reason._

Intuitionistic logic doesn't have the law of excluded middle and is the form
of logic underlying the simply-typed lambda calculus.

There is a more nuanced relationship between logic and programming languages
than is being discussed here.

------
robobro
> Aristotle was the only logician and if you point me towards Buddhist or
> Daoist philosophy, you're mansplaining!

OP should check out trinary logic and first-order predicate calculus. Just
because OP doesn't appreciate "null" doesn't mean that it should be removed
from programming languages or from existing programs.... false means not true,
zero means no things, null means absence.

~~~
robconery
OP here - OP has checked out (and lived with) trinary logic. Just because you
appreciate null doesn't mean it should be kept in programming languages and
existing programs.

That there is a logical truism, isn't this fun?

Also: false means not true, zero is a number and null doesn't exist, by
definition. We can model true/false/zero easily as they exist. Null is made
up, so every language gets to think about what it means in an abstract made up
way. Thus the pain, thus the post.

~~~
zeth___
The last 100 years have been dealing with the fact that true and false are not
enough to describe the world of computation. You also need undecidable, or
null, for statements that can be proved to never terminate.

It is very much still an open question if a statement is absolutely
undecidable and if we need to add something like null in all logic [0].

As for your arguments on why we don't need null, they sound exactly like the
arguments against zero from the middle ages [1].

>Just as the rag doll wanted to be an eagle, the donkey a lion and the monkey
a queen, the zero put on airs and pretended to be a digit.

[0]
[http://logic.harvard.edu/koellner/QAU_reprint.pdf](http://logic.harvard.edu/koellner/QAU_reprint.pdf)

[1] Menninger, Karl (1969), Number Words and Number Symbols. Cambridge, Mass.:
The M.I.T. Press.

------
baking
As a programmer with assembly language in my blood, all I can think is "Why
should I care that this silly person thinks 'zero' doesn't exist?"

~~~
maxk42
It's not that 'zero' doesn't exist.

It's that in assembly Null's just a zero.

------
jeandejean
I like the fact that the author says: "don't be complacent and mansplain your
feelings about null", but that is exactly what he does...

People claiming the industry is wrong and that we should use some logically
pure language are usually impractical and deny the compromise that must be
made for systems to be usable by the masses.

~~~
TheCoelacanth
A language does not need to be logically pure to avoid null. Having null in a
modern language is simply a boneheaded mistake. There are no compromises that
need to be made to get rid of it. Optional types are superior in every way.

~~~
he0001
How are they superior?

------
Animats
C++ would be a lot better off if references were used more heavily. (And C
ought to have references by now.) One still sees too many asterisks in C++
code. References are not supposed to be null. Sometimes they are, though,
because there's no checking when a pointer is converted to a reference.

"this" should have been a reference, not a pointer. Strostrup admits that was
a mistake.

~~~
gpderetta
technically references can't be null ever. To create a null reference you
would have to deference a null pointer and then you are into nasal demons
territory.

/pendantic

------
dshoemaker
How is it possibly mansplaining to respond to this with an opinion or
alternate explanation? Also... isn't the other's gender fairly important to
the mansplaining comment since the term references the phenomenon of women
being explained things they already know?

~~~
mjburgess
When a man is condescended to by other men we call that "communication", when
a woman is its called "mansplaining".

------
gwbas1c
C# has null because all new memory is initialized as all zeros. The constraint
is that any object or struct needs to be valid if it's in-memory content is
all zero. Thus, null must be supported for object references because the
pointer has to support zero.

Value types can work without null because 0 is valid.

Could the language designers implement object types without null? It would be
very difficult for fields, especially in structs, because it's nearly
impossible to force memory initialization. You couldn't do "default
(StructType)" if it had a not null object as a field.

~~~
wvenable
It would be nice if C# simply had a nullable type for reference types. So like
int? you could also have string? or SomeClass?. Structs with non-nullable
reference types would have to be initialized at creation time. This would
solve a lot of errors. But you still need to have the option to have nullable
references.

------
jstewartmobile
Someone needs to write an essay on the productivity disaster of "considered
harmful" essays.

We traded _goto_ for callback hell and ten-layer inheritance hierarchies. A
null-purge would probably end similarly.

------
sklivvz1971
Clearly there's a place for null: most languages have it, most developers have
no problem with it, it does represent something (lack of information). It is
logical and mathematical--I suggest you read the GEB book for tons of more
info on that.

That said, people are _also_ free to experiment with null-free languages, or
to avoid null. If it works for their use case, fantastic!

Just the attitude of the post is terrible. For example the user creates a sock
puppet and posts a trolling/edgy question to Stack Overflow... just to see if
it's toxic.

Sorry, these posts are toxic, in my opinion.

~~~
_bxg1
Yeah, it was super weird how the subject migrated from programming theory to a
rant about stack overflow.

On the actual topic, I think a good compromise is what typescript and I think
rust do, which is, a value is only nullable if you explicitly declare it as
such. It forces you to be judicious, while still allowing you the option when
you need it.

~~~
sklivvz1971
I've been trying that, but so far I don't find a big advantage, but it _is_ a
bit annoying to have to annotate all types that could be null.

This is to say: it could be good, or it could be another "exceptions in Java"
moment

------
contingencies
It's about leaky abstractions.

If programming is about managing complexity and status is ideally specified
explicitly, then null is simply the option in a mathematical set defining that
status which corresponds to the option commonly seen on surveys: _Other
(please specify): ..._

While it can be handy to have this 'extra value' in a set, and it is most
commonly used to denote special meanings within a carefully controlled context
(eg. SQL column in a result set is empty, a variable is of no type - ie. not
defined at all, etc.), issues arise when people accidentally carry context or
presumptions about the meaning of null across contexts, creating a leaky
abstraction.

Most of the author's article appears to deal with differences in these
context-specific assumptions.

Perhaps the Java approach: throw an exception.

Unix approach? Nonzero return values and arbitrary stream or file data to
clarify. In edge-case leakiness, very similar to the Java approach.

The functional and dedicated non-OO procedural programming approach: define
exit parameters to your function, specifying complete precision and ending any
ambiguity.

Since a type is a formal context (set), then using a typed language is another
solution, although that adds overhead it brings benefits in rigor.

 _Sometimes, the elegant implementation is just a function. Not a method. Not
a class. Not a framework. Just a function._ \- John Carmack

------
speedplane
Is "null" really the problem, or is not knowing the state of something the
problem? I agree that it would be better for the world if we always knew what
a "thing" was or referenced, but more often than not, we don't. Having a
construct that expresses this uncertainty nicely models our own uncertainty.

------
decebalus1
I always found this[0] analogy quite accurate whenever I discussed this issue
with others.

Setting aside how different languages (mis)treat null as a concept, I think
the whole discussion is about convoluting boolean logic with memory addresses
and some languages do a better/worse job at being 'intuitive' than others.

I had great success with this[1] whenever I felt things were going in the
wrong direction or when the language constructs of dealing with nulls were in
the way of the domain design.

[0] [https://www.b4x.com/android/forum/attachments/unbenannt-
jpg....](https://www.b4x.com/android/forum/attachments/unbenannt-jpg.54095/)

[1]
[https://en.wikipedia.org/wiki/Null_object_pattern](https://en.wikipedia.org/wiki/Null_object_pattern)

------
barrkel
C# has (a subset of) SQL null semantics because of LINQ. That's the answer to
his specific question.

Null values propagating through expressions SQL style is an approach to
handling unknown values. Whether it's desirable is somewhat besides the point;
databases have null values, and LINQ enables capturing expression trees that
get converted into SQL, so keeping similar semantics makes a kind of logical
sense.

Nulls are undesirable in a language until you want to initialise large
structures in a simple language with a simple compiler. Staying away from the
temptation then takes ingenuity and discipline.

~~~
bobbyi_settv
Was the behavior of null with integers really changed when LINQ was added?
What would his examples have done in C# 1.0?

------
tuna-piano
In an integer column in a relational database, what is the alternative of
having null?

0 is not an option, as that will return incorrect mean/median calculations. So
what would be there? Or am I misunderstanding?

~~~
oldandtired
If there is missing data to occur within a column, then pragmatically, you
split that column off into its own table with the associated key from the
original and store only those keys that have valid values.

If you do anything else, such as using nulls, then you are just creating a
problem that WILL come back and bite you in the nether regions of your psyche.

Of course, there will other opinions about how to handle this. As far as I am
concerned, Nulls are a curse foisted on us by those vendors and standards
bodies who took the the easy way out.

~~~
bvrmn
> you split that column off into its own table with the associated key from
> the original and store only those keys that have valid values.

That will create real problems and bite you immediately.

~~~
oldandtired
Demonstrate that it will bite you immediately. This technique has ensured that
only valid data has been kept and that reporting works.

When nulls are stored there are no guarantees that anything you ask of the
database will ever turn out right. Especially when there are millions of
records stored. Two queries that should give you the same answer give
different results when nulls exist. Seen it too often.

~~~
bvrmn
Table separation leads to record fragmentation and big IO cost on record
fetching. Your ideal academic world will be crashed under production reality.
"Millions of records". Bwa-ha-ha.

~~~
oldandtired
If your DBMS is so poorly written that record fragmentation is an issue then
you need to change the DBMS. Since most (>99%) of the database design and
implementation work that I have been involved since the mid-80's was business
related and for a variety of different industries, I didn't find table
separation to be a problem. The appropriate designs led to faster
applications.

I have also worked for companies that didn't use relational database theory
for their products and they had far more issues. In a couple, I was able to
hive off the database designs from the main systems and got the applications
to actually work and work properly.

------
pera
Haskell have the _Maybe_ monad, which is defined as _Just a | Nothing_. One
may argue then that languages with nullable integers implicitly define this
type as _Maybe Int_.

~~~
Peaker
That's the Maybe _type_. The _Maybe monad_ is this:

    
    
      instance Monad Maybe where
        return = Just
        Nothing >>= _ = Nothing
        Just x >>= f = f x
    

(And is not really related to the discussion)

~~~
pera
Right! that definition was the type, thanks for the correction :) still, the
Maybe monad is used for computations that may fail, which is often the purpose
of the null pointer.

------
Zak
> _This leads to an inconsistency: if to_i will turn nil into a 0, why won’t
> that coercion happen when trying to multiply?_

Because Ruby doesn't automatically coerce arbitrary types to numbers when you
try to perform arithmetic on them. This is a Good Thing.

    
    
        2 * "4"
    

> TypeError
    
    
        2 * "4".to_i
    

> 8

Interestingly, it _is_ possible to multiply a string by a number, but no
coercion takes place.

    
    
        "2" * 4
    

> "2222"

~~~
robconery
OP here - Yes that's the operation the question wasn't supposed to be a
literal one, rather a consistency issue, which illustrates the larger point
that different languages deal with null differently because it's not logical
and therefore confusing and a pain in the ass :)

~~~
Zak
Languages are definitely not consistent in what they do if you try to coerce
null to various types, nor are they consistent (often even internally) about
whether they perform coercions implicitly. The Ruby example seemed to deal
mostly with the latter.

I think implicit coercion is bad, but that's a separate issue from null.

------
mirimir
I have no clue why coding languages need null. I mean, they're deterministic,
right? If something is missing, undefined or whatever, that's an error. What
does the null concept gain you?

In data, on the other hand, null is a very useful concept. It explicitly means
that there is no value in a field. And that prevents such mischief as
interpreting missing data as zeros.

~~~
millstone
In a language like C, NULL solves problems that are otherwise awkward to
solve.

1\. What should `malloc()` return on failure?

2\. How should I indicate that I don't care about certain out parameters, e.g.
the parameter to `time()`?

3\. How should I initialize variables that are going to be used as out
parameters, e.g. in `strtol()`?

Other languages address these at the cost of significantly complicating their
type system and ABI: generics, multiple return values, etc. In a language
intended to be small like C, I'm not sure how you can do better.

~~~
mirimir
Thanks. But ...

1\. Why not just return meaningful values? Wouldn't that help in debugging?

2) ???

3) That makes sense to me.

~~~
AlotOfReading
C functions can only return one value. Since the stdlib generally adheres to
the idea of minimizing overhead, that would mean malloc would have to accept a
mutable reference to a reference, so it could assign the reference it returns.
Since null references are unusable, using that impossible value as your error
flag is a pretty clever optimization.

~~~
mirimir
Thanks, I see that.

But isn't ambiguity the tradeoff?

------
vemv
Null/nil tends to represent the absence of a thing. That's perfectly logical.
Things can be absent.

------
pabl0rg
Kotlin solves this problem nicely, assuming your codebase is all new or has
been annotated with @Nullable/@NotNull

[https://kotlinlang.org/docs/reference/null-
safety.html](https://kotlinlang.org/docs/reference/null-safety.html)

~~~
tigershark
It doesn’t seem that nice to me compared to making it impossible to assign
nulls everywhere, without the need of annotating anything.

------
kllrnohj
Surprised there's no mention of Kotlin here. It handles this really nicely,
forcing you to declare if a value can be null and if it is null it requires a
null check before it can be used (with some syntax sugar to make that cleaner)

------
amelius
Even if you eradicate Null, programmers will create a singleton object to
represent it.

~~~
Xuper
Yeah, just like they invented Unit/Void singleton in functional languages.

------
threepipeproblm
Unknowns are not even remotely problematic for logic. Not only SQL, but
illustrious languages such as VB6, include built-in trivalent logic.

~~~
ErwinSmout
Yeah and the results are so massively intuitive.

~~~
threepipeproblm
I suppose this is sarcasm but the results are quite intuitive to me. From my
perspective, it's trivial to construct the truth tables for trivalent logic
based on a sense of what "should" be in them. Maybe give it a try.

~~~
ErwinSmout
Well guess what. Your "sense" of what "should" be in them has no place in
computer _SCIENCE_.

Computer _SCIENCE_ is that ugly thing that tells us that three-valued logic
gives rise to 19683 distinct binary logical operators, while two-valued has
16.

Computer _SCIENCE_ is that ugly thing that tells us that if you want a
computer language over a three-valued logic to be expressively complete, then
you need to implement all of those 19683 logical binary operators one way or
another. In the worst case, that's 19683 operator names for the programmer to
remember. And you come here claiming that it's "trivial" because you have a
"sense" of what the results ought to be ? That proves just one thing but site
policy probably won't allow me to spell that out.

(In case you were wondering what the 16 names are in two-valued logic : they
aren't needed because the system being two-valued gives rise to certain
symmetries that gracefully allow us to reduce the set we need to remember to
just {AND OR NOT} (or some such) which beautifully parallels the way we
communicate in everyday life.)

~~~
threepipeproblm
Ah, you seem to be a bit nutters. So I'm not going to engage further but I do
want to give you a serious response to the argument you seem to be making.

First, the same argument above is also an argument that, say, integers, are
not "Computer SCIENCE".

More to the point, you might enjoy reading the work of Charles Pierce and
other logicians of that era who began to explore many variations on formal
logic. Note that just as many operations arise from trinary relations in
bivalent logic. Are binary relations "Computer SCIENCE", but not trinary or
higher relations? Before you answer, you might want to look into whether all
possible relations can be expressed using only binary relations (hint: nope).

Look deeper into the concept of functional completeness (with respect to a
subset of operators), which you reference above without naming. You might be
able to understand how many of those many trivalent operators are actually
necessary to reason with (hint: not very many, hardly more than for bivalent
logic, where, as you note, we only tend to use a few, and need not worry about
it).

Consider also the relationship between operators folks have identified as
useful in bivalent vs trivalent logic (hint: they not picking at random).

Could it be that just as with the 16 binary operators, many of which have
relations to one another (e.g. inverses and complements, among others) that
the trinary operators could fall into similar groups, which, making the 3^9
number you mentioned seem a whole lot less complex? Could that be _why_ it's
neither necessary nor customary to work with all the operators in either sort
of logic?

Once you've caught up to state of the art in formal logic as of the 1930's you
might have a new perspective -- perhaps you might even begin to let us know
when "Computer SCIENCE" will catch up!

~~~
ErwinSmout
If you wanted that to be a _real argument_ then you _should have done the
maths yourself_. Without those maths you are doing nothing but gratuitious
handwaving. I've done them for you and they prove me right (and you wrong at
least where you say "hardly more than for bivalent logic").

Oh, and if you want to know _why_ people don't _want_ to find more "useful"
operators than what they're used to from good old two-valued logic then I have
a hint for you too : it's because they all immediately sense that their brains
are not up to it as soon as they actually try (and my actually doing the maths
has very clearly shown me why - so as you suggested to me "perhaps give it a
try").

------
combatentropy

      > Logically speaking, there is no such thing as Null
    

Yes, there is:

    
    
      "Did you pass your test?"
      "I haven't taken it yet."
      "Okay, will you pass it?"
      "Null."
    

Suppose there was a database table of students, with a column called "passed,"
which is boolean. If it's in the middle of the semester, that column must be
null. True means they passed. False is put there when they fail.

\---

Another example, in a table of help tickets, suppose there is a timestamp
field called "closed," for when the ticket was closed. If the ticket is open,
then that column must be null.

\---

    
    
      "Which show is currently playing on channel 3?"
      "It's just static. It's midnight, and channel 3 has stopped broadcasting."
    

(This of course must have been back in the '80s.)

If the TV guide were a database table, with time slots for each channel, then
the columns for which show is scheduled for midnight on channel 3 must be
null.

In fact, that's how I picture null: ever-shifting static inside the little
cell in my database table. That's why I'm fine with how if you ask if two null
values equal, the answer is null (at least in Postgres).

Someone may say that there is a show currently on channel 3, and the name of
the show is "Static." But that's not quite true. If channel 3 recorded static
and broadcast it, then yes. But channel 3 has literally turned off its tower.
It is broadcasting nothing. Your TV is showing static because you tuned to a
station that is not there.

\---

The writer complains about inconsistent implementations of null, like in
JavaScript. I agree that there are misimplementations. I would not have had
both null and undefined.

In fact, that's another way of understanding what null is. Suppose you say my
earlier examples with SQL are flawed, because SQL is flawed, because it
insists on every row having columns it doesn't need, that the field for
"passed" should even exist in that row until the class is over. Say instead
you were using objects. Within the semester it may look like this:

    
    
      {
         name: "Edgar Smith"
      }
    

Then at the end you add:

    
    
      {
         name: "Edgar Smith",
         passed: true
      }
    

Well, what if you asked for the value of student.passed in the middle of the
semester? You might say it is a mistake, but it is a question that can be
grammatically formed: "What is the value of the 'passed' key in the object?"
The only answer is null. To me null often means "not applicable."

\---

Again, another example. In school we played a game of guess-the-object, where
you could ask only yes-or-no questions. There were three possible answers:
yes, no, or "does not compute." Clearly the answer of "does not compute" was
needed for when the answer was neither yes or no. "Does not compute" was a
fancy synonym for "null."

\---

Null is the answer to the infamous loaded question, "When did you stop beating
your wife?"

\---

Null is quantum foam. It is what is happening at the base of the universe, if
you look close enough. Is the particle here or there? Null.

You may say that doesn't make sense, that it's just that we don't have the
right instruments. But that is Quantum Mechanics. If you wish to disprove
Quantum Mechanics, Einstein and I would both be interested, as he was uneasy
with it too. But going after quantum mechanics would be a better use of your
time than going after null. Right now, null and the scientific consensus about
physical reality agree.

------
_Codemonkeyism
I've been coding since around 1981. What changed my coding style most, is
Option when I've started Scala 10y ago.

~~~
mavdi
Scala is a very confused language and specially about the use of Options vs
null. I find that Rust and Swift enforce Options a lot better.

------
Guthur
This was a decent post until he went on the stack overflow rant.

------
benjohnson
What I'd like to tell the author.

Don't like null? Don't use it.. and you find null propagation ruining your
abstraction then treat it as an error and fix your program.

~~~
robconery
OP here - as a matter of fact I try to do just this, starting with the
database. I'm mostly a data person so I try to think through, as deeply as I
can, what I should expect in every table - there _has_ to be a sensible
default and if I can't find one then I rethink my design. You'll probably
disagree with me and grunt out another single sentence missive, which is fine,
but I think it's worth taking some extra time and using Null as a bit of a
warning. It's a crutch! A way to stop thinking and say "whatever I don't know
what this value is supposed to be so... it's null. Let's go shopping!"

~~~
chris_wot
What do you use in a database when you have a field where you literally do not
know what the value should be?

~~~
jessaustin
If you have a PEOPLE table and some birthdates are unknown, then remove the
"birthdate" column and make another table called PEOPLE_BIRTHDATES with a
"birthdate" column and a foreign key pointing to PEOPLE. Now your queries can
have lots of left joins. The results will still have nulls, however.

~~~
ErwinSmout
Which is the reason why you shouldn't write outer joins.

~~~
jessaustin
So if we don't know the customer's birthdate we can't serve her? I can imagine
a problem with that...

~~~
ErwinSmout
Sigh.

Where have I said any such thing ?

~~~
jessaustin
If there's no row for the customer in the joined table, the customer won't
show up in an inner join.

~~~
ErwinSmout
Great. Now if you can explain to me where you got the idea that a join (inner
or otherwise) is the _only_ possible way to query two tables then we might get
somewhere. Because you can also just do two queries. And no, that does not
necessarily mean "two roundtrips to the DBMS" (which I know perfectly well is
undesirable). There are techniques for avoiding that. Perhaps not in SQL, but
that's a reason you should be pressing the vendors to improve SQL. Not for you
to agree to the status quo of sticking with the vendors' old bypasses-and-
hacks cheating bag.

~~~
jessaustin
Haha OK then use a UNION... oh wait we're gonna have NULLs with that too. One
suspects you'll also have some vague objection to this point, but if the only
way to address that is to wait on somebody to invent an "improved" SQL, one
won't worry about it too much.

~~~
ErwinSmout
That "improved SQL" was already defined in the previous century, and has been
implemented as well. Your ignorance drips off of every word you write.

~~~
chris_wot
E.F. Codd literally designed null into the relational model. I don't think
calling someone ignorant is very helpful.

~~~
ErwinSmout
But the demeaning ridicule that gets thrown at me is ?

(BTW I doubt very much that "Codd designed null into the RM". Even his 12
rules mention only "a systemic way to deal with missing information", not
"null".)

