I just don't know what to think about them.

The reason nulls annoy me is we have so many of them:

* Lisp's Nil, which is not really a null but sometimes used as one

* C's Null, which exists to cause an error when dereferenced, but also wasn't hardcore null enough so they added Void

* Javascript's separate Null and Undefined values, but Undefined gets coerced to Null when you output to JSON, so do we really need both? (plus an infinite set of distinguishable {} and [] objects which are not Null)

* whatever you get when you do ,, in CSV

* SQL NULL, which exists to break logic, because NULL != NULL but IS NULL (NULL) == True

* "" and 0, the empty string and zero

* IEEE floating point's NaN, which is Not a Number and certainly is not Zero but is probably also not Null, or is it?

* whatever the truth value of 'this statement is false' is

* _|_ (robo-butt), which is theoretically what you get when you try to evaluate an infinitely recursing function but what you actually get is Stack Overflow Exception which is diferent

* -0, negative zero, which either is or is not the same as positive zero depending on who wrote your math library

* and of course 0.0 is not the same as 0, how could it be, one is an Int and the other is a Float

* 0/0 is probably NaN, unless it just generates an error because maybe your system doesn't allow NaN as a numeric value

* Fail, which is what a Prolog predicate returns if it's not true (not the same as false), but is also not an actual value

* False of course, which is not Null

* If you have three-valued logic, then as well as True and False you also have Unknown or Undefined

* BUT! If you have a four-valued logic, which seems to be more natural than three-valued, you not only have 'unknown' you also have 'both true and false'

so which of these two null-like logical values (neither of which is 'false') 'more null' than the other? Is either of these a 'true' null? We usually think of SQL NULL as 'Unknown' but 4VL's 'both' is even more null than that

* what is the car of NIL? What's the thing inside an empty list? Some Lisps (including Common Lisp I think) say it's NIL, but wouldn't that then imply that () = (()) and that's not an equality that holds in Lisp

* the empty set in set theory is often CALLED 'null' but it's not actually 'nothing' null, it's an empty set - it's like an empty box. It has an outside, at least, and {{}} is most certainly not {}.

So if NIL works like set NULL then the car of NIL should not be NIL, right?

etc. ugh

so if nothing is, like, nothing, why do we have so many DIFFERENT KINDS of nothings and why are they all different

how can we even tell them apart in the first place

presumably it's because we already know 'what type of nothing' it is because there's some kind of notional 'type marker' or 'position' in the expression in which the nothing isn't.

@natecull On the otherhand it's extremely clear what C# (I think), Haskell, Whiley, and Rust thinks of them.

Having nulls can be useful but NullPointerExceptions should never occur at runtime. To that effect each of them (somehow or other) allows you to mark whether a variable can be null.

@alcinnz I'm not sure why dereferencing Null can't just give you Null?

@natecull I don't know either, that would make sense to me.

@natecull Have you read William Kent's "Data & Reality"? Fantastic book, and solidified for me that we need an "I don't know" answer and generally that's encoded as null.

I thought the case against null was that it's forced on you?

i.e. Where you explicitly want to disallow "don't know", the inherent possibility of null forces you to:

a) handle that case anyway, leaving you with over-complex code, or
b) ignore the possibility, leaving you with missing appendages if you slip up.

Whereas, if you want the allow the "don't know" option, it's possible without needing nulls.

Obvs. I've not read that book. Is there a case I'm missing here?

@mdhughes @natecull

@wu_lee @natecull All the Optional systems I've ever used introduce far more complexity than just allowing null for any object reference, and require generics and strong typing, makes every function call a documentation challenge and takes forever to compile.

The Objective-C model was by far my favorite, since mostly sending nil/doing nothing is what you'd do if a receiver was nil anyway.

But if you just want to handle the case the explosive way, assert(foo) as a pre/post-condition is super-simple.

@natecull My favorite null is the bottom, usually written like your robobutt, but not the same thing. The bottom is the one element in the bottom type, which is a subtype of every other type, so it breaks every attempt to avoid it with simple type constraints. I like it because it is evil.

Java's null is kind of this, and I believe SmallTalk's nil is very much this, although of course those two examples behave very differently, as ST nil accepts every message and returns nil (I believe), whereas trying to do anything with Java null raises an exception.

The non-evil alternative is Maybe monads, I guess?
@natecull Either way, having a generic null is almost always a bad idea. Use a specific sentinel value instead, like e.g. how Maybe does it. Just like how you should at least throw a MyPackageException rather than GenericRuntimeException, so that callers have the slightest chance at figuring out in a granular way what went wrong.

@clacke yeah, 'bottom' is what I mean by _|_

it's sort of null-y, but what kind of null...?

See, Java is consistent with type theory and trying to screen out the nulls is inconsistent!

or is it category theory

it's some type of category of theory I guess

@natecull Haskell’s answer to this (Maybe) was what brought me back to static typing

@mikelynch @natecull This could be solved elegantly in Common Lisp via multiple values, where the CAR of (NIL) is (NIL T), but the CAR of NIL is (NIL NIL). This would be similar to the behavior of GETHASH and require no modifications to existing programs.

@mikelynch @natecull i was just gonna say: `Maybe a` is an extremely good solution

@natecull I read this last post before seeing the preceding ones and was about to talk about Sartre.

If I recall Haskell's (and Racket's?) behaviour rightly, integer 0/0 is an error but 0.0/0.0 is infinity.

In DoomScript (Doom 3 and its mods) there is $null_entity but no generic null. So trying e.g. to get a key's value as a vector will yield '0,0,0' if the value is '0,0,0' OR unset; to be certain you have to get it as a string too and check for "".

Because being is said in many ways, and nothing is relative only to being?

@natecull At least in Schemes, which are the only LISPs I really understand, "nil" (there is no such symbol in the standard, or many implementations) is () which is not a pair, (()) is a pair of (() ()) so (car '(())) and (cdr '(())) both return ().

And any sequence of pairs that ends in () is a list, even if the car is also ().

I dunno if it's helpful in any way, normally you'd never write one of those.


> IEEE floating point's NaN, which is Not a Number and certainly is not Zero but is probably also not Null, or is it?

For added hilarity, Not a Number is usually a Number.

@natecull Also, Objective-C is great, it has NULL (C pointer, probably but not necessarily 0), nil (Objective-C type, any messaging to nil returns nil or 0), and [NSNull null] which is just a unique object to say "nothing here in this collection that can't contain nil".

Sign in to participate in the conversation

Follow friends and discover new ones. Publish anything you want: links, pictures, text, video. This server is run by the main developers of the Mastodon project. Everyone is welcome as long as you follow our code of conduct!