News for hackers. Stuff that matters.
Re:The Logical Disaster of Null by jasode on Wednesday May 09, 2018 @09:47PM (#17035859)
>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.

Re:The Logical Disaster of Null by paulajohnson on Thursday May 10, 2018 @02:38AM (#17036854)
The point is that option types let you explicitly state which values are nullable, but null references mean that every value is potentially null even if it doesn't make sense.
Re:The Logical Disaster of Null by pryce on Thursday May 10, 2018 @03:08AM (#17036953)
And that having everything being 'implicitly nullable' is a categorically worse for maintainability, readability and comprehensibility than using explicit Option<T> types at the boundaries; such as the implementation in F# [1], with an expressive matching syntax.

It is now common when creating a library to rely on manual checking and coders explicitly writing code to check and throw NullArgument exceptions for each object parameter in every public method, and then writing dozens or hundreds more pointless unit tests to ensure this null exception behaviour works as expected; this is totally unnecessary in languages that demand explicitly stated Option<T> when an Option is meaningful.

Instead of requiring all the above for safety -an approach which will notably fail to report any errors if the null argument checks are partially or completely omitted- we have -and we should prefer- languages where the compiler or interpreter will not let us pass null into a function where null makes no sense, because that fact is encoded in the function definition.

At the library boundaries, which are comparatively rare to internal code, we can simply use the explicit Option type.

I should note I have one objeciton with the F# implementation - in that I think that ideally the properties { .IsSome, .IsNone, .Value } should not exist, as they promote bad programming practices.

[1] https://fsharpforfunandprofit.com/posts/the-option-type/

Re:The Logical Disaster of Null by jcelerier on Thursday May 10, 2018 @04:00AM (#17037118)
That is only the case in reference-centric languages such as Java, C#, JS, etc. In value-based languages, "values" can't be null, while "references" can.

For instance a `std::string` or a `double` in C++ can never be null ; a pointer to either can be but that's far from idiomatic.

Re:The Logical Disaster of Null by lmm on Thursday May 10, 2018 @08:21AM (#17038177)
That approach doesn't really work, because C++ conflates where a value is stored with whether the value can semantically be null. Sometimes you want a nullable value that's on the stack. Sometimes you want a non-null value in the heap. Both these things are hard to do in C++.
Re:The Logical Disaster of Null by TheCoelacanth on Thursday May 10, 2018 @10:34AM (#17039166)
Neither of those is hard to do in C++. The first one is std::optional. The second one is a reference or a non-nullable smart pointer like https://github.com/dropbox/nn
Re:The Logical Disaster of Null by frabert on Friday May 11, 2018 @01:28AM (#17045148)
It's easy to do _since less than a year_
Re:The Logical Disaster of Null by jandrese on Friday May 11, 2018 @03:14PM (#17050067)
And probably not in your compiler yet. Or if it is, you'll blow up on thousands of other compilers when people try to build it themselves.
Re:The Logical Disaster of Null by jcelerier on Sunday May 13, 2018 @05:27AM (#17058070)
boost::optional has been available since 2003. There's no excuse.
Re:The Logical Disaster of Null by RX14 on Thursday May 10, 2018 @05:23AM (#17037374)
You don't need option types for the compiler to enforce safety around nulls. Its possible to model null as its own type in the type system to enforce safety.
Re:The Logical Disaster of Null by mannykannot on Thursday May 10, 2018 @09:38AM (#17038687)
In such a type system, is every variable and function potentially null, or does its type have to be declared as being potentially null (or not declared as not null)? Would the latter case not be, in practice, just like using option types, and would the former case require ubiquitous null checking?

My understanding so far is that option types have two advantages over the way current mainstream languages handle the null case: a) you can find the potential uses of null values statically, and also ensure that the programmer writes some code that at least nominally handles the case; b) you can avoid this burden in cases where null is not an option. Does null-as-its-own-type improve on this?

Re:The Logical Disaster of Null by RX14 on Thursday May 10, 2018 @10:50AM (#17039313)
I mean the latter. And in practice, it's not just like using option types because it works well with flow typing. Being able to express your nil checking in ordinary control flow instead of special optional methods is often cleaner (think `raise "foo" if bar.nil?` as a guard statement and for the rest of the method body bar is no longer nil). All absolutely safe and checked at compile-time. If you then allow calling methods on nil you can even implement the "try" method that most Optional implementations have, which is pretty nice sometimes even when you have flow typing.

In Crystal we go a step further and allow type unions between arbitrary types (of which nil is just an ordinary empty struct in the stdlib which you can call methods on). I love how it works in practice.

Re:The Logical Disaster of Null by coldtea on Thursday May 10, 2018 @06:10AM (#17037580)
This means you need a different type system though.
Re:The Logical Disaster of Null by RX14 on Thursday May 10, 2018 @06:33AM (#17037681)
Yeah, its a good option for new languages though.
Re:The Logical Disaster of Null by lmm on Thursday May 10, 2018 @08:23AM (#17038187)
It's the same mistake as checked exceptions all over again. If you make null a special case in the type system then anything that interacts with the type system has to know about null or will handle it wrong. Far better to have the type system work with plain (non-nullable) values, and implement a plain old library type like Maybe/Option for use in places that need absence semantics; that way your concerns are properly separated and you can spend your type system complexity budget in more generally applicable ways.
Re:The Logical Disaster of Null by RX14 on Thursday May 10, 2018 @08:26AM (#17038195)
Just because null is a type in the type system doesn't mean it has to be a special type.
Re:The Logical Disaster of Null by lmm on Thursday May 10, 2018 @09:02AM (#17038453)
That's the only way I've ever seen it implemented. If you're proposing some idea for having null in the type system as a normal type (that doesn't boil down to just being equivalent to Option/Maybe), can you be more concrete?
Re:The Logical Disaster of Null by RX14 on Thursday May 10, 2018 @10:36AM (#17039184)
Being able to create arbitrary union types between different types, like crystal (which I work on). The difference between that and option/maybe is that it ends up playing nicely with flow typing without any special cases for optional/maybe in the flow typing.
Re:The Logical Disaster of Null by lmm on Thursday May 10, 2018 @11:09AM (#17039525)
You shouldn't need special cases for optional/maybe in the first case; optional/maybe are simple sum types, you would want whatever flow-typing system you have to do the right thing with user-defined "x or y or z" types, and if you do that then it'll automatically work just as well for optional/maybe.

(I don't like inclusive union types because they're noncompositional - code you write with them isn't parametric any more - which seems particularly awkward in a compiled language - how do you compile code that forms unions involving type parameters?)

Re:The Logical Disaster of Null by RX14 on Thursday May 10, 2018 @12:57PM (#17040661)
You shouldn't be able to use a type without giving all the necessary type parameters, so you can't form a union type with unbound type parameters in so it's not a problem? I'm not 100% sure what you're asking...

I'm not 100% sure how you can integrate sum types with a flow-typing system, perhaps with pattern matching?

Re:The Logical Disaster of Null by lmm on Friday May 11, 2018 @02:04AM (#17045246)
> You shouldn't be able to use a type without giving all the necessary type parameters, so you can't form a union type with unbound type parameters in

So inside the body of a generic method (or class) you can't form unions involving the method's type parameters? That works but makes them much less useful as a language feature - most language features work as normal within a generic method.

> I'm not 100% sure how you can integrate sum types with a flow-typing system, perhaps with pattern matching?

Whatever you do for union types should work, surely. Indeed it ought to be simpler since you have more information to work with - with a sum type if the thing is B then you know it's not A, whereas with a union type it's possible for the thing to be both A and B.

Re:The Logical Disaster of Null by RX14 on Friday May 11, 2018 @05:10AM (#17045885)
In crystal, the compiler always knows the concrete values of the type parameters of any generic before typing it. I really am interested in knowing more about what you said about union types being non compositional because I'm definitely not a type theorist. The crystal compiler wasn't written by type theorists and the typing algorithm is completely home-grown.

And regarding sum types I was just thinking syntactically instead of in terms of the type system.

Re:The Logical Disaster of Null by lmm on Friday May 11, 2018 @07:48AM (#17046517)
> In crystal, the compiler always knows the concrete values of the type parameters of any generic before typing it.

Hmm. Does that mean you can't have a generic method in a separate compilation unit from where it gets used?

> I really am interested in knowing more about what you said about union types being non compositional because I'm definitely not a type theorist.

I'm not really a type theorist, I'm just thinking about e.g. if you have a method like:

    def doSomething[A](userSuppliedValue: A) = {
      val x = if(someCondition) Some(userSuppliedValue) else None
      x match {
        case Some(a) => y ...
        case None => z ...
      }
      x match {
        case None => v ...
        case Some(a) => w ...
      }
    }
then the compiler knows that if we take branch y we will also take branch w and if we take branch z we will also take branch v. Whereas if you do the same thing with an inclusive union type, that's true most of the time but not when userSuppliedValue is null.
Re:The Logical Disaster of Null by RX14 on Friday May 11, 2018 @08:50AM (#17046904)
We don't have compilation units, all code is compiled at once in Crystal. It's a little painful in terms of compile times with large codebases but nowhere near C++ compile times yet :) We're thinking about caching and various modifications to the crystal compiler to make compilation faster.

I'm confused by your example too. If you mean replacing matching None with testing for nil, then you'd have to replace Some(T) with an `else`, then the compiler absolutely can prove that if you take one branch you take the other - given that x is not assigned to (not sure why the compiler would want to prove that though). And after all that I'm not sure how your example relates to, or what you even mean by "noncompositional".

Re:The Logical Disaster of Null by lmm on Saturday May 12, 2018 @11:50AM (#17054600)
> I'm confused by your example too. If you mean replacing matching None with testing for nil, then you'd have to replace Some(T) with an `else`, then the compiler absolutely can prove that if you take one branch you take the other

If you use an inclusive union, the type of x becomes A | nil (or however you write it). The trouble is that you don't know whether A might also be a type that includes nil, and userSuppliedValue might be nil already. So if you write in match/case style your branching behaves inconsistently in that case (because x is both an A and a nil) - and if you write in "if(x == nil)" style then you can't infer that branch y will be taken when someCondition is true.

> And after all that I'm not sure how your example relates to, or what you even mean by "noncompositional".

What I mean is that viewing these types as type-level functions, they don't compose. For option/maybe-style types, composition works as expected: you can know the behaviour of X[Y[A]] by reasoning about X and Y independently. That doesn't work for inclusive unions, because whether A | nil is a different type from A depends on what type A is.

Re:The Logical Disaster of Null by RX14 on Saturday May 12, 2018 @05:15PM (#17056048)
Ah! I understand completely now! Interestingly I've only very rarely noticed that being a problem in practice. I'm not quire sure what to say about it more than that because it's definitely a real problem but I haven't noticed it being a pain point in practice. Most type parameters in crystal are only used for collection APIs which never union the type parameter with nil.
Re:The Logical Disaster of Null by lmm on Sunday May 13, 2018 @05:01PM (#17061033)
Interesting. I imagine that's the Ruby influence - on paper at least, Crystal's featureset is almost ML-family, but sounds like the culture must be rather different.

Being able to move all your concerns into the type system is really nice, and the difference between a type system that does what you expect 95% of the time and one that does 100% of the time is huge - but I don't know any way to get there except gradually. I've been doing Scala for 8 years and it probably took 4 before I realised how important this stuff was even in the simple case of Option - and that's coming from a background of Java before then and already being dubious of things that step outside the type system.

Language inconsistencies matter, but only in large codebases, and by the time you have those large codebases it's probably too late to change the language. I think Scala gets a lot right, I think something like Idris gets more right. I can make the case for why those things are the right things in terms of theoretical purity/consistency. But when it comes to practical differences I don't know how to convince anyone other than by saying "go maintain a 300kloc system in an ML-family language for 5 years and see how much easier it is" :/.

Best of luck. I hope Crystal finds some things that work well, and that we all add something to the language design state of the art. I do think we're getting gradual progress and consensus - if nothing else, almost every new language has at least some form of sum typing and pattern matching these days - but I guess progress on the aspects that matter for big systems is inherently going to be slow, because it's only after we've built those big systems that we can see what's wrong (and what's right) with them.

Re:The Logical Disaster of Null by RX14 on Monday May 14, 2018 @10:57AM (#17066071)
Interestingly crystal has neither sum types or pattern matching (and it doesn't need them) :)

And yeah, nobody has any experience maintaining 300kloc+ systems in crystal. And i'm sure we get loads of things wrong and there are lots of pain points. I hope we find some type theorists to pick through the problems when that happens too, because crystal's type system is really quite different to anything I've ever seen before. And it would be nice to see what comes out of that theory wise.

Re:The Logical Disaster of Null by lmm on Tuesday May 15, 2018 @11:32AM (#17074699)
> Interestingly crystal has neither sum types or pattern matching (and it doesn't need them) :)

Well, the use cases will be there, and any general-purpose language will need answers for them. One can of course achieve the same effect with other language features (e.g. Java's famous use of the "visitor pattern" for those cases), but there's a cost in verbosity and maintainability.

Re:The Logical Disaster of Null by RX14 on Wednesday May 16, 2018 @11:35AM (#17083227)
Crystal can essentially have a sum type by representing it as a union of different record types. It's not common to do in crystal though, at least not with 3+ union entries.

And pattern matching isn't really needed if you have flow typing + OO (at least I haven't found a usecase for pattern matching which crystal doesn't already have an answer to)

Re:The Logical Disaster of Null by lmm on Friday May 18, 2018 @07:13AM (#17100456)
> Crystal can essentially have a sum type by representing it as a union of different record types. It's not common to do in crystal though, at least not with 3+ union entries.

Sum types usually only have two or three entries too.

> And pattern matching isn't really needed if you have flow typing + OO (at least I haven't found a usecase for pattern matching which crystal doesn't already have an answer to)

How does "flow typing + OO" solve the problem? You can use OO to emulate pattern matching via the visitor pattern, yes, but that's notoriously cumbersome; I don't see how flow typing makes any difference to that?

Re:The Logical Disaster of Null by RX14 on Friday May 18, 2018 @11:27AM (#17102223)
Pattern matching, at least in functional languages, is mainly used both as control flow to match on the type of arguments, and also to destructure data structures. In flow typing you replace the control flow part of pattern matching with `if x.is_a? Type` and since everything you can do to an object is just passing a message to it (thanks to ruby's smalltalk origins), you don't really need the destructuring since you can just then use `foo.bar`.

Crystal has method overloading (dynamic dispatch), which is a form of pattern matching (but not destructuring) but that's it. So I guess we have pattern matching but definitely not to the extent say haskell or elixir has it.

Visitor pattern in Crystal is just a class with a bunch of `def visit(node : Type)` and then just call `visit(foo)` and let dynamic dispatch handle the rest. The visited data structure has no knowledge about the visitor.

Re:The Logical Disaster of Null by lmm on Monday May 21, 2018 @05:38AM (#17117184)
> In flow typing you replace the control flow part of pattern matching with `if x.is_a? Type`

Hmm, I guess flow typing lets you do an exhaustiveness check of an if-else style set of cases. Still seems like a syntactic shortcut would be nice, just as one misses "switch" in Python.

Re:The Logical Disaster of Null by RX14 on Monday May 21, 2018 @06:34PM (#17122411)
Yeah, crystal has one of those (and it's about to gain exhaustiveness checking), so I didn't literally always mean `if/else` :)
Re:The Logical Disaster of Null by lmm on Tuesday May 29, 2018 @09:35AM (#17178995)
If you can switch on the type of a variable and have it have the relevant type within each switch arm I'd consider that to be pattern matching, though I guess that's just semantics.
Re:The Logical Disaster of Null by rosser on Wednesday May 09, 2018 @11:44PM (#17036216)
> The essay is conflating 2 different concepts of "null."

I'd take it a step further, and posit that The Fine Article's rage comes from its author not grokking the distinction, or even recognizing there is one.

Of course null is going to seem frustratingly and dangerously wrong if that's your concept of it.

Re:The Logical Disaster of Null by tigershark on Thursday May 10, 2018 @03:03AM (#17036945)
Null is dangerously wrong. Languages that avoid it give a much nicer experience because you explicitly write your model considering which values can be missing.
Re:The Logical Disaster of Null by Twisell on Thursday May 10, 2018 @03:44AM (#17037059)
And booom you just then reinvented a new concept of "missing" exactly as he said.

PS: To disembiguate I was speaking of the null a value in a nullable type. Maybe I got you wrong.

Also I guess Null is way less a problem in strictly typed language than in JavaScript for instance.

Re:The Logical Disaster of Null by tigershark on Thursday May 10, 2018 @04:21AM (#17037167)
I didn’t reinvent anything, the concept of optional value is well ingrained in the real world. If you are registering on a website the email address is mandatory while the home address is optional. If you are sending a parcel the home address is mandatory and the email address is optional. If lazy programmers instead of modelling the optional concept in the type system use a null the result is the huge amount of null pointer exceptions that are riddling a lot of software and the useless “defensive guards” everywhere.
Re:The Logical Disaster of Null by dragonwriter on Thursday May 10, 2018 @04:24AM (#17037179)
> If lazy programmers instead of modelling the optional concept in the type system use a null the result is the huge amount of null pointer exceptions

Null values and null pointers aren't fundamentally the same thing, though some programming languages may be implemented in a way such that use of one exposed the other. A language can have a Null value that never results in NPEs.

Re:The Logical Disaster of Null by T-R on Thursday May 10, 2018 @04:48AM (#17037247)
I think that's kind of his point - Optionals/tagged unions are "Null values", but only in the places where they semantically make sense.

The problem with nulls isn't strictly null references, in the sense of "a pointer that points to nothing" or "a pointer that throws a null pointer exception", it's that languages with nulls implicitly make null an element of every type. They tend to do this because variables are allowed to have null references, but if you changed the semantics of variables to technically forbid null references, but still had a null value as an implicit element of every type, you'd still have the problem of needing to do runtime checks for null everywhere.

Re:The Logical Disaster of Null by ivanhoe on Thursday May 10, 2018 @05:14AM (#17037335)
But how do you handle those optional values then? Let's say you run a classifieds site, some of your users will probably prefer not to specify a price, it has to be an optional value. Or age field on a dating sites. How do you filter that data, or handle sorting on that field, without using null values to mark those that are undefined?
Re:The Logical Disaster of Null by Munksgaard on Thursday May 10, 2018 @05:33AM (#17037414)
That has been done with option types in functional languages since at least the 80s. It's not hard.
Re:The Logical Disaster of Null by coldtea on Thursday May 10, 2018 @06:16AM (#17037603)
e.g:

  sum = 0
  for entry in entries:
    case (val):
      null: pass
      number n: sum += n
    endcase
in pseudocode. Several languages have offered this since forever (eg. Haskell and Maybe).

The fun part is that the typesystem enforces that you check all possible cases -- or only lets you explicitly un wrap potentially null values.

So it's not like a C programmer checking if (x == NULL) manually.

Re:The Logical Disaster of Null by tigershark on Thursday May 10, 2018 @05:55AM (#17037517)
Option types and comprehensive pattern matching in F#. If you just need to filter them you can simply use Seq.choose.
Re:The Logical Disaster of Null by WillReplyfFood on Thursday May 10, 2018 @03:12AM (#17036965)
Or you write sunny day code and wonder why everyboy elses code is riddled with those strange defensive structures.
Re:The Logical Disaster of Null by tathougies on Thursday May 10, 2018 @01:52AM (#17036684)
> 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.

Alternatively, you can only use languages which offer actual tagged unions, and avoid the ridiculousness of these 'hasvalue' fields.

Re:The Logical Disaster of Null by wool_gather on Thursday May 10, 2018 @08:45AM (#17038320)
Your comment seems to be missing the point of the parent. It wasn't saying that adding this field was a good thing to do. In fact, it relies on the extra field being self-evidently ridiculous -- it was saying that there's no way to avoid doing something.
Re:The Logical Disaster of Null by tathougies on Thursday May 10, 2018 @10:32AM (#17039155)
The parent said any solution implemented within the language would be worse. Decades of experience with actual tagged unions would seem to indicate otherwise
Re:The Logical Disaster of Null by jasode on Thursday May 10, 2018 @07:56AM (#17038066)
>Alternatively, you can only use languages which offer actual tagged unions,

I tried to address explicit-null-handling programming concepts such as Option<T> or tagged unions in my last paragraph.

Nevertheless, it didn't seem like the essay was about non-nullable concepts but instead, he was writing about the idea of "null" as a data representation for unknown values to be fundamentally flawed because of various boolean operations strangeness.

My point is that "null" as a data representation mapping to "missing" is an irreducible complexity which is orthogonal to Option<T>. E.g. F# Option<T> still has ".None" which is a "null by another name". Yes, the programming language support is superior because it forces the programmer to handle possible nulls instead of crashing. Safety around nulls enforced by the compiler is progress. (The null handling as a verb assisted by compiler checks.) However, it didn't remove the need to represent a null as a state -- or null as a noun. Why do I separate those 2 verb-vs-noun concepts? Because he used examples with constants such as "10* null" returning a "null" instead throwing an error as violating math logic. And "10< nil" violating 2-value boolean logic. Therefore he plays with the idea that nulls in the language shouldn't exist. Let's rewrite one of his examples using variables instead of constants:

  x = Option<int>
  y = Option<int>
  x = null
  y = 10 * x
What should "y" be? The essay says that's not the question to ask. He's wondering why multiplication doesn't throw an error. The idea of tagged unions and Option<int> really doesn't address "data" / "mapping" / "noun" concept of null and how it fits into certain intuitions about math and booleans.

This irreducible complexity is why "null as noun" will get reinvented as "99999", "-1", etc if you don't have null (whether spelled as ".None", "Nil", or some other standard way). Even if you have an array of Option<int>s and want to serialize to disk, you still need to write "null as data concept" in some fashion (empty string, sentinel value, extra "hasdata" field etc).

As side note, I find it interesting that some commenters believe the author is talking about Optional<T> or similar concept and that I missed his point. I ask those readers to look at the essay again and confirm that it makes no mention of "Option", "Optional", "Maybe", "sum types", "tagged unions", etc. His essay seems to be based on the flawed concept of data representation of unknown values. A key sentence near the top:

>Logically-speaking, there is no such thing as Null, yet we’ve decided to represent it in our programs.

Re:The Logical Disaster of Null by tathougies on Thursday May 10, 2018 @10:59AM (#17039415)
> Logically-speaking, there is no such thing as Null, yet we’ve decided to represent it in our programs.

But he's right. It shouldn't be an inherent part of any data. Sometimes, something not being present is a reasonable logical state to be in. For these times, there are tagged unions. In other cases, it is absolutely insane to simply add on the possibility of the NULL value to every data type in a language.

Re:The Logical Disaster of Null by jasode on Thursday May 10, 2018 @11:27AM (#17039719)
>For these times, there are tagged unions.

Again, tagged unions do not solve the author's examples of Aristotle logic puzzles and inconsistencies around null/None/Nil/Nothing/etc.

He's not talking about "type safety" enforced by static compiler checks, or opt-in nulls as the default vs opt-out, etc. Therefore, compiler enforced explicit pattern matching code on tagged unions misses the point.

I suggest you click on the Stackoverflow q&a link in the author's essay to get a better idea of what the his philosophical conversation is about.

>In other cases, it is absolutely insane to simply add on the possibility of the NULL value to every data type in a language.

That's a totally valid position but the author isn't talking about that. He's questioning the _meaning_ of "null" in light of observing (in his view) non-intuitive behavior with math and boolean operators.

Your "tagged unions" doesn't address his philosophical conversation at all.

My point is that if the author removed "null" from a hypothetical language because it's "weird", the users of such a hypothetical language would recreate the "null" again. This has nothing to do with discriminated unions or Option<T>.

Re:The Logical Disaster of Null by dsego on Thursday May 10, 2018 @08:02AM (#17038082)
I think the way C# handles it is actually good. Any operation with null should result in null. Throwing errors willy-nilly just constrains you into doing extra null-checks in dozens of places instead of only where it is needed.
Re:The Logical Disaster of Null by empath75 on Thursday May 10, 2018 @08:58AM (#17038426)
I’m not sure that you understand how options work, tbh.

Your example would be nonsensical in any language I’m aware of.

Re:The Logical Disaster of Null by jasode on Thursday May 10, 2018 @09:05AM (#17038481)
It's just pseudocode. Whether one explicitly expands out the "x" as "x.GetValueOrNone()" or the type system implicitly converts/coalesces any "null" value of "x" to propagate the null to "y" or one has to explicitly write pattern-matching code is not relevant because none of those syntax details affect the author's thesis.
Re:The Logical Disaster of Null by Zak on Wednesday May 09, 2018 @10:24PM (#17035975)
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.

Re:The Logical Disaster of Null by davidgay on Thursday May 10, 2018 @01:38AM (#17036639)
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.

Re:The Logical Disaster of Null by viraptor on Thursday May 10, 2018 @03:31AM (#17037025)
Circular data types are perfectly fine with optional types. You can have optional<*raw_pointer> for example which after compilation may even be equivalent to a simple null, since valid pointers are not-null.

This was discussed in rust some time ago: https://mail.mozilla.org/pipermail/rust-dev/2013-March/00330...

Re:The Logical Disaster of Null by skybrian on Thursday May 10, 2018 @04:10AM (#17037133)
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

Re:The Logical Disaster of Null by davidgay on Thursday May 10, 2018 @11:09AM (#17039520)
You also need type polymorphism (which Java and most of the languages under discussion did not have/did not start with), or it will drive you crazy.

And you may start needing fancier versions of polymorphism to express things like "this accepts a T=Foo or Optional<Foo>, and returns Bar<T>".

In passing, I remain to be convinced that that x?.length/etc syntax (and equivalents in other variations of Optional) is really a good idea - to me, it feels like an error-prone way of papering over the fact that non-explicit null(aka absent-optional) checks are just too painful to program with. Phrased differently, I see no reason to believe that the sufficiently-common-to-warrant-special-support appropriate handling of null is just to feed it up through as the computation of the computation if it occurs in any part.

Re:The Logical Disaster of Null by chaosite on Thursday May 10, 2018 @06:39AM (#17037703)
Well, not really. What Kotlin has is exactly an Optional type. It's just a different syntax for it.
Re:The Logical Disaster of Null by Zarel on Thursday May 10, 2018 @08:23AM (#17038182)
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")`

Re:The Logical Disaster of Null by chaosite on Thursday May 10, 2018 @12:27PM (#17040365)
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.

Re:The Logical Disaster of Null by htgb on Thursday May 10, 2018 @02:33AM (#17036832)
Interesting, I haven't heard this argument before. So what is it about null that makes it more feasible here than for example option types/tagged unions?

Also, regarding your third point, IMO the question mark syntax in for example TypeScript and C# is fine.

Re:The Logical Disaster of Null by annabellish on Thursday May 10, 2018 @03:54AM (#17037088)
Way back when when the JVM/CLR were being originally designed, I'm not sure that Option types as a concept were quite as proven, and both languages were trying to be "safe" languages that didn't have any complicated or new concepts in them. Optional types might not be that now, but they probably were then, when they were strictly in the domain of fairly academic languages.
Re:The Logical Disaster of Null by ratmice on Thursday May 10, 2018 @07:14AM (#17037877)
I'm not entirely sure where or when it was introduced, the first mention of it I could find was May 1989 (pg 25)

ftp://ftp.cs.princeton.edu/reports/1989/220.pdf

Re:The Logical Disaster of Null by Peaker on Thursday May 10, 2018 @09:39AM (#17038689)
Destroying a language's safety for a single use case is a terrible trade-off.

There are various possible solutions for circular structures that do not require destroying all static safety with nulls everywhere.

Re:The Logical Disaster of Null by ajkjk on Thursday May 10, 2018 @12:54AM (#17036490)
Do people keeping doing that? All the new languages I see seem to embrace optional.
Re:The Logical Disaster of Null by hawkice on Thursday May 10, 2018 @01:12AM (#17036554)
Go is one of the worst/weirdest. It has semantics around uninitialized variables that most programs will rely on for correctness. And it also has nil.
Re:The Logical Disaster of Null by vbezhenar on Thursday May 10, 2018 @06:03AM (#17037554)
And nils in Go might be different. That's even more weird.
Re:The Logical Disaster of Null by hawkice on Thursday May 10, 2018 @07:33AM (#17037961)
Right, interface Nils compare on type too, right? This is bananas.
Re:The Logical Disaster of Null by justincormack on Thursday May 10, 2018 @03:42AM (#17037056)
Go is bad in that it uses pointers to make something nullable, but i dont think the uninitialised non nullable values are zero is that bad. Otherwise uninitialised is another type of null...
Re:The Logical Disaster of Null by danek on Thursday May 10, 2018 @01:16AM (#17036568)

  Doing so *nullifies*
pun intended?
Re:The Logical Disaster of Null by collyw on Thursday May 10, 2018 @06:34AM (#17037685)
I use dynamically typed languages the majority of the time, i never really saw the advantage of static typing. I do however rely on the database for typing quite heavily.
Re:The Logical Disaster of Null by Zak on Thursday May 10, 2018 @10:09AM (#17038956)
I also usually prefer dynamic typing. When I do use languages with static typing, I really want the compiler to be able to tell me that `foo` is definitely an Int and not Null.
Re:The Logical Disaster of Null by cdoxsey on Thursday May 10, 2018 @02:53AM (#17036914)
c# has an option type. You can make any struct type nullable with a ?.
Re:The Logical Disaster of Null by masklinn on Thursday May 10, 2018 @04:18AM (#17037152)
The issue is you can't make reference types non-nullable (yet, they're working towards that end for 8.0).
Re:The Logical Disaster of Null by pred_ on Thursday May 10, 2018 @04:32AM (#17037200)
Those interested in how C# 8.0 approaches the problem might appreciate Mads Torgersen's thoughts on the matter [0] (relevant part starts at 5:40).

[0] https://channel9.msdn.com/Blogs/Seth-Juarez/A-Preview-of-C-8...

Re:The Logical Disaster of Null by qu4z-2 on Thursday May 10, 2018 @07:20PM (#17043732)
Do you know how they're solving the problem of the language expecting a `default(T)` to exist for every type?
Re:The Logical Disaster of Null by masklinn on Wednesday May 16, 2018 @08:43AM (#17081909)
You can get a pretty complete overview at https://blogs.msdn.microsoft.com/dotnet/2017/11/15/nullable-...

But essentially they're not solving it: you will get a warning on explicit default-ing of a non-nullable reference, you will get a warning on implicitly initialising/defaulting class fields, but you will not get warnings when doing so for structs or arrays.

Re:The Logical Disaster of Null by qu4z-2 on Sunday May 27, 2018 @07:17PM (#17169679)
Thanks. That looks quite interesting.
Re:The Logical Disaster of Null by cannam on Thursday May 10, 2018 @03:56AM (#17037093)
> 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

As an aside here, there is also a school of thought that NULL in SQL was a mistake, see e.g. https://www.dcs.warwick.ac.uk/~hugh/TTM/Missing-info-without... and http://thethirdmanifesto.com/ (Hugh Darwen and Chris Date)

Re:The Logical Disaster of Null by Izkata on Thursday May 10, 2018 @12:05AM (#17036287)
> All those other clunky techniques will reinvent the same kinds of "null" programming errors!

Worse, instead of blowing up and yelling "this is wrong, fix it!", they'll pretend to work and give you the wrong results.

I imagine if null is the "billion dollar mistake", then null never having existed - and relying on sentinels - would've been the trillion dollar mistake.

Re:The Logical Disaster of Null by wvenable on Thursday May 10, 2018 @01:13AM (#17036557)
My company uses a very expensive and common accounting software to run the entire business. It uses a sentinel date value that represents no-end-date. That date is 2025-12-31. I suppose in the 80's when the system was first designed they never imagined that the system would still be in use in 2018. There is a slim chance we will have changed software by then but that chance gets smaller every day.
Re:The Logical Disaster of Null by daveFNbuck on Thursday May 10, 2018 @02:10AM (#17036755)
I imagine businesses plan ahead and make deals for more than 7 years. It's already too late if your sentinel value is within the range of your real values.
Re:The Logical Disaster of Null by thaumasiotes on Thursday May 10, 2018 @04:59AM (#17037279)
Is it a sentinel value or a sentinel range? There's nothing fundamentally wrong with e.g. having a service begin on May 1st 2028 (2028-05-01) and continue until forever (2025-12-31). The logic will only fail if you assume that 2025-12-31 and all subsequent dates are all equivalently forever.

Doing things that way means every time you check the value of a date, the very first check needs to be "is it the sentinel value?". But that's kind of the concept of a sentinel value anyway.

Re:The Logical Disaster of Null by daveFNbuck on Thursday May 10, 2018 @10:18AM (#17039026)
But there is something wrong with having some services actually end on 2025-12-31 and other services that are marked as ending on 2025-12-31 because they continue forever. Once 2025-12-31 is in the range of valid end dates, you can't tell the difference between the two.
Re:The Logical Disaster of Null by wvenable on Thursday May 10, 2018 @10:10AM (#17038962)
> having a service begin on May 1st 2028 (2028-05-01) and continue until forever (2025-12-31).

That doesn't work as most of the program logic simple checks of a date is between two dates. It basically just treats 2025-12-31 as the end of all time. I suspect they will patch it before the end and change that date to something else.

Re:The Logical Disaster of Null by coldtea on Thursday May 10, 2018 @06:20AM (#17037624)
>I imagine if null is the "billion dollar mistake", then null never having existed - and relying on sentinels - would've been the trillion dollar mistake.

The whole idea is that you don't have to rely on sentinels -- and with optionals and pattern matching we didn't have, ever since the 80s or so.

Re:The Logical Disaster of Null by tigershark on Thursday May 10, 2018 @02:52AM (#17036913)
> The logic-variant of Null usage for "unknown" "missing" "invalid" values is unavoidable in programming.

On the contrary it’s perfectly avoidable. You just need to use an option type with a language that supports comprehensive pattern matching and won’t even compile if you don’t explicitly consider the case of a missing value. If your model is flawed and you don’t want to explicitly specify that some value is optional then the problem is in your way of thinking. And there is no need to invent a language that doesn’t support nulls, they already exist.

Re:The Logical Disaster of Null by viraptor on Thursday May 10, 2018 @03:05AM (#17036949)
It's just another name for Null in practice. Or a "typed null" to be more precise. It's got some nice properties, but really it's the good old tri-state.

As the op says - is a "logic-variant of Null"

Re:The Logical Disaster of Null by dragonwriter on Thursday May 10, 2018 @04:16AM (#17037149)
> It's just another name for Null in practice.

No, it's not.

> Or a "typed null" to be more precise.

To the extent this is true, it is a fundamental difference.

> It's got some nice properties, but really it's the good old tri-state.

No, it's not. Because the Boolean type contains only True and False, not Null and the Option<Boolean> type (likewise, Option<Option<Boolean>>, etc.) does not contain True or False. There's no weirdness in doing logic operations on an Option<Boolean>, or a pair of them, because it is a type error to attempt it because Option<Boolean> is not Boolean.

Re:The Logical Disaster of Null by viraptor on Thursday May 10, 2018 @04:35AM (#17037209)
Sure, they're different things. The comment wasn't meant to be in vacuum, but as a response to option being a better logic 3-state.

I agree that for other reasons it's better, but conceptually they are used to represent the same thing. (Logic variant of null)

Re:The Logical Disaster of Null by tomasz_bekas on Thursday May 10, 2018 @03:57AM (#17037098)
>really it's the good old tri-state

Yes it is, the point they make is that Option<Boolean> is explicitly tri-state in contrast to regular Boolean which should be two-state.

Re:The Logical Disaster of Null by annabellish on Thursday May 10, 2018 @03:52AM (#17037081)
That style of "null" has a _lot_ of advantages over the common null found today. The perfect is the enemy of the good - Option/Maybe types aren't perfect, but they're a lot better than null references.
Re:The Logical Disaster of Null by xamuel on Thursday May 10, 2018 @09:42AM (#17038727)
In languages with pointers, it's not really possible for the compiler to know when null needs to be checked for. Consider this fragment:

void f(char *x) {g(&x); printf(x);}

It's possible that calling g(&x) might cause x to no longer be null, even if x originally was null. It would then be inappropriate for the compiler to complain that the next instruction attempts to use x without checking whether x is null.

Re:The Logical Disaster of Null by robconery on Wednesday May 09, 2018 @11:58PM (#17036274)
OP here - I was going to go into that - it's an interesting story. There are null references, as you say, but there's also the null object pattern and the null type. I decided to just focus on the idea of null, which all three of those things represent. A pointer points to a null space, a type represents a value that can optionally be null, and the object pattern allows you to deal with null in your program directly (like Ruby's nil).

So... yeah I dig it man :) I just left some things out to keep on a pace.

Re:The Logical Disaster of Null by SAI_Peregrinus on Thursday May 10, 2018 @11:33PM (#17044795)
I feel like the idea of null is fine, but the implementations are idiosyncratic. Null is an unknown/unknowable/invalid value. You can't know if null = null. This should be an exception or other error. You can't know if 10 = null. Null is unknown. It might be 10, it might not. This should be an exception or other error. You can't have a meaningful result of 10 * null. A pointer to null is meaningless.

Languages which lack a way to signify an error other than null can be an issue. Most modern languages have option types and/or exceptions, both of which provide good ways to deal with error conditions.

Re:The Logical Disaster of Null by coldtea on Thursday May 10, 2018 @06:07AM (#17037569)
>This is why that null concept shows up repeatedly in different forms such as NaN in floating point, NULL in SQL language, etc.

NULL in SQL is also problematic -- e.g. as it behaves wrt aggregations and such. Explicitly user defined unknown values are better.

Re:The Logical Disaster of Null by ddellacosta on Thursday May 10, 2018 @02:42AM (#17036869)
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".

I'm struggling to understand what you mean here. My understanding is that there is no distinction between "logical null" and a "null reference" in terms of the problem we're discussing--as soon as you introduce nulls as a placeholder for values that are not yet initialized you have to deal with the logical implications of null being a member of those types, no? It's been a while since I watched the talk, but scanning the transcript from the talk we're discussing (https://www.infoq.com/presentations/Null-References-The-Bill...) the way Prof. Hoare talks about them seems to be the same in terms of their logical impact on the type system. I quote:

25:55 One of the things you want is to be able to know in a high level language is that when it is created, all of its data structure is initialised. In this case, a null reference can be used to indicate that the data is missing or not known at this time. In fact, it's the only thing that can be assigned if you have a pointer to a particular type.

...

27:40 This led me to suggest that the null value is a member of every type, and a null check is required on every use of that reference variable, and it may be perhaps a billion dollar mistake.

And another thing you wrote which I don't understand:

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!

Isn't that exactly backwards? The entire point Prof. Hoare was trying to make is that using nulls to model the "real-world (lack of) information" causes us to have to contend with the logical problems inherent in making null a member of every type. Additionally, sum types seem to model this in a logically consistent way very well, as other commenters have noted.

Re:The Logical Disaster of Null by sklivvz1971 on Thursday May 10, 2018 @02:26AM (#17036806)
Totally agree: further to that, if you had int+bool to represent nullable values, you'd also have to sort the ridiculousness of having a null value with a value, also error prone.
Re:The Logical Disaster of Null by prmph on Thursday May 10, 2018 @05:17AM (#17037351)
Another issue is that the concept of NULL is ill-defined, if you really think about it. There is usually no way to distinguish between use of NULL to represent (1) A missing value, (2) An unknown value, or (3) a default value.

These are not trivial distinctions; knowing the semantic intent of a NULL can either hamper or aid in reasoning about a code-base and avoiding subtle errors

Re:The Logical Disaster of Null by paulddraper on Wednesday May 09, 2018 @09:53PM (#17035875)
Correct. The real evil is sentinel values.

That said, the memory layout of Maybe/Optional (assuming it can be stack allocated) will look much like what you outlined, though of course the API can be more user friendly.

Re:The Logical Disaster of Null by tathougies on Thursday May 10, 2018 @01:53AM (#17036686)
> That said, the memory layout of Maybe/Optional (assuming it can be stack allocated) will look much like what you outlined, though of course the API can be more user friendly.

But it's really not. First of all, most Haskell compilers will optimize away usages of Maybe that are known to be Nothing or Just. Secondly, GHC at least uses tagged pointers for small sum types, including Maybe.

Re:The Logical Disaster of Null by bjz_ on Thursday May 10, 2018 @05:18AM (#17037354)
Rust will do optimisation on the layout of Option-looking data types to simplify them down to a pointer size if possible.
Re:The Logical Disaster of Null by noir_lord on Thursday May 10, 2018 @06:17AM (#17037608)
I agree, one of the nicest changes in programming for me is a plugin for intellij and PHP that does static analysis and flags "foo may be be null" via an inspection (it actually does a hell of a lot more than that, truly stellar plug "Enhanced EA Inspections" if anyone has a PHP codebase and uses intellij btw).

It catches so many stupid cases where I'd have call bar on foo where foo could be undefined.

TypeScript handles it pretty well given the constraints it has to work in.

Re:The Logical Disaster of Null by flipgimble on Thursday May 10, 2018 @07:10AM (#17037851)
I don’t think it’s conflating anything but trying to explain to you the difference between the two concepts. It’s saying that Null for all references types by default is a bad idea, and representing “missing” in a tagged union (sum type, whatever) leads to better code. Your worry about inconsistent type use for the same concept doesn’t really apply when combined with syntactic sugar for optionals.
Re:The Logical Disaster of Null by catnaroek on Thursday May 10, 2018 @09:18AM (#17038556)
Optionals are a better alternative to null. They compose better (i.e., they nest) and play nicely with data abstraction (i.e., you can define an abstract type that hides the fact that its underlying representation is optional), unlike null.
Re:The Logical Disaster of Null by oldandtired on Thursday May 10, 2018 @01:40AM (#17036645)
A correction, there are no "tri-state booleans". Booleans are two state logic systems. Extended Booleans require 2 ^ n states where n is an integer >= 1.

Any other number of states automatically ensure that the logic is non-Boolean or non-Extended Boolean.

Re:The Logical Disaster of Null by jovdg on Thursday May 10, 2018 @02:41AM (#17036864)
Re:The Logical Disaster of Null by oldandtired on Thursday May 10, 2018 @05:22AM (#17037368)
Boolean has a specific mathematical/logical meaning and semantics. The moment you add a third state, it is no longer Boolean.

The interesting thing is that positive powers of 2 will also allow consistent extended Boolean logic and semantics.