[HN Gopher] Null References: The Billion Dollar Mistake
       ___________________________________________________________________
        
       Null References: The Billion Dollar Mistake
        
       Author : wheresvic3
       Score  : 45 points
       Date   : 2020-01-11 12:55 UTC (10 hours ago)
        
 (HTM) web link (www.infoq.com)
 (TXT) w3m dump (www.infoq.com)
        
       | rzwitserloot wrote:
       | This old chestnut again.
       | 
       | There is an inherent problem in designing processes and writing
       | code to capture them: The notion of not-a-value.
       | 
       | There are great many ways to solve them. The most common ones are
       | 'null' and 'Optional[T]'. Neither just makes the problem
       | magically go away. If a process is designed (or a programmer
       | writes it) thinking that 'ah, well, here, not-a-value cannot
       | happen', but it can, then.. you have a bug.
       | 
       | Some language features might make it possible to help reduce how
       | often it occurs, but eliminate it? I don't think so.
       | 
       | Imagine, for example, in an Optional based language, that you
       | just map the optional to a lambda to execute on the optional, and
       | the behaviour of the optional is to then simply silently do
       | nothing if it's optional.none. That'd be a much harder to find
       | bug than a nullpointer error. (errors with stack traces pointing
       | at the problem are obviously vastly superior to mysterious do-
       | nothing behaviour with no logs or traces of any sort!).
       | 
       | Some other creative solutions:
       | 
       | * [Pony](https://www.ponylang.io/) tries to be very careful about
       | registering when an object is 'valid' and when it isn't, and when
       | you write code, you have to say which state the objects you
       | interact with can be in. This lets you avoid a lot of the
       | issues... but pony is quite experimental.
       | 
       | * In java you can annotate any usage of a type with nullity info,
       | and then compiler linter tools will simply tell you that you have
       | failed to take into account a potential null value. You are then
       | free to ignore these warnings if you're just writing test code,
       | or know better. Avoids clogging up the works with optional, but
       | as the java ecosystem shows, you can't just snap your fingers and
       | make 30 years of massive community effort instantaneously
       | instantly be festooned with 'might-not-hold-a-value' style
       | information. At least the annotation style gives the hope of
       | being backwards compatible (to be clear, optional, for java?
       | Really bad idea).
       | 
       | * in ObjC, if you send a message to a null pointer, it silently
       | does nothing, in contrast to virtually all other languages with
       | null types where attempting to message a null ref causes an error
       | or even a core dump.
       | 
       | * Just write better APIs. Have objects that represent blank state
       | (empty strings, empty collections, perhaps dummy streams which
       | provide no bytes / elements, etc). For example, in java: Java's
       | map (a dictionary implementation) has the `.get(key)` method
       | which returns the value associated with that key, and returns
       | `null` if there is no such value. About 6 years ago another
       | method was added in a backwards compatible fashion (so, all java
       | map implementations got this automatically): `getOrDefault(key,
       | defaultValue)`. This one returns the provided default value if
       | key isn't in the map. You'd think optionals provide a general
       | mechanism for this, but, in scala, you have both: There's
       | `someMap get(key)` which returns an optional, so to get the 'give
       | me a default value' behaviour, that'd be
       | `someMap.get(key).getOrElse(defaultValue)`, but maps in scala
       | also have the java shortcut: `someMap.getOrElse(key,
       | defaultValue)`. Sufficient thought in your APIs mostly obviates
       | the issues.
       | 
       | null is not a milion dollar mistake. It is a solution to an
       | intrinsic problem with advantages and disadvantages over other
       | solutions.
        
         | melling wrote:
         | I remember tracking down the null silent message failure issue
         | in the early 1990s on NextStep. Then again almost 2 decades
         | later on the iPhone. Personally, I'm not a fan of silent
         | failures.
         | 
         | IMHO, allowing for non-nullable variables is a huge improvement
         | in language design. Adding boilerplate annotations is an ugly
         | way to handle it. Optimize for the common case and make
         | variables non-nullable by default.
        
           | strictfp wrote:
           | In my experience, forbidding null refs usually only results
           | in getting null objects instead, such as empty strings or
           | empty collections. Thats not bad, but might not solve such a
           | silent error message, you'll still end up with an empty
           | message in the end. In order to solve it proper, I'm thinking
           | you'd might want to go further and have more expressive type
           | constraints, like Ada subranges.
        
         | temac wrote:
         | The mistake is being nullable/optional by "default", that is
         | with the least amount of effort for programmers using such a
         | language. Or worse only ever nullable (like Java is except for
         | its built-in scalars I think?).
         | 
         | There is obviously a need about having optional things, but
         | this is not the common case, so this should not be the default
         | and even less the only solution. And it should enforce handling
         | the absent case.
         | 
         | "null" is a shortcut for talking about solution which does
         | nothing of that (and is even UB in case of mistake in some
         | languages). Billion Dollar Mistake is generously low; probably
         | the cost is already _Multi_ -Billion Dollar, and counting.
        
         | JoshMcguigan wrote:
         | The goal of `Optional[T]` is not to "make the problem magically
         | go away", in fact that is almost the opposite of the goal.
         | 
         | Optional[T] exists to make it very obvious when a value is
         | nullable. Having non-nullable types as default, with
         | Optional[T], allows a developer to model a system more
         | accurately. This is helpful both to the compiler as well as
         | anyone else who reads/maintains that code.
         | 
         | > Imagine, for example, in an Optional based language, that you
         | just map the optional to a lambda to execute on the optional,
         | and the behaviour of the optional is to then simply silently do
         | nothing if it's optional.none. That'd be a much harder to find
         | bug than a nullpointer error. (errors with stack traces
         | pointing at the problem are obviously vastly superior to
         | mysterious do-nothing behaviour with no logs or traces of any
         | sort!).
         | 
         | This is just one of the things a developer could decide to do
         | when faced with an optional which is none. It is up the
         | language design to make it easy to express this behavior (or
         | any other behavior they might choose) without hiding it.
        
           | agumonkey wrote:
           | Not contradicting, just that there a slight benefit in
           | reifying an issue as it opens for notation and operators to
           | simplify it. Maybe monad or option chaining are more than
           | making the thing obvious, it make them half disappear.
        
           | strictfp wrote:
           | Sure it's up to the language design, but in practice a `None`
           | gets a similar treatment as an empty collection, usually
           | effectively short-circuiting remaining calculations. As the
           | parent poster pointed out, this might either be the behavior
           | you want, or actually mask the error, depending on the
           | situation. By this logic, optionals aren't better than null
           | refs, just different. The same argumentation holds for
           | exceptions vs optionals.
        
             | JoshMcguigan wrote:
             | In my experience, languages with strict non-null guarantees
             | (and optional types), do the exact opposite of "mask the
             | error". If anything, they are sometimes faulted for being
             | too verbose.
             | 
             | The idea is, by explicitly marking things which can be null
             | (wrapping them in an Option[T], for example), you can be
             | sure that everything else is not null. This alone relieves
             | the developer of a large cognitive load.
             | 
             | Further, the language can provide syntax to make handling
             | optional types obvious without being painful. Rust match
             | statements are one example of this.
             | 
             | Can you provide a specific example of how using an optional
             | type makes a potential "missing-thing" type of bug harder
             | to see?
        
         | kerkeslager wrote:
         | > There are great many ways to solve them. The most common ones
         | are 'null' and 'Optional[T]'. Neither just makes the problem
         | magically go away. If a process is designed (or a programmer
         | writes it) thinking that 'ah, well, here, not-a-value cannot
         | happen', but it can, then.. you have a bug.
         | 
         | > Some language features might make it possible to help reduce
         | how often it occurs, but eliminate it? I don't think so.
         | 
         | On the contrary, you can 100% eliminate it by forcing null
         | handling at compile time with your `Optional` type. Haskell and
         | some other strongly-typed languages do this (but they call it
         | Maybe).
         | 
         | The way to do this in a C-syntax-ish language would look
         | something like this:                   Optional<int>
         | increment(Optional<int> i) {             // return i + 1; would
         | throw an error at compile time,             // because Optional
         | doesn't implement the + operator                  //
         | i.applyToValue would throw an error at compile time
         | // if you didn't handle both possible cases             return
         | i.applyToValue(                 ifNull: (void) => { return new
         | Optional<int>(null); },                 ifValue: (int i) => {
         | return i + 1; }             );         }
         | 
         | This is syntactically a bit heavy, partly because I was a bit
         | more verbose than a real implementation would need to be, for
         | clarity, and partly because C-style syntax doesn't do this
         | well. Languages that support this generally have some syntactic
         | sugar to make it a bit more terse.
         | 
         | I've argued before on HN that the benefits of strong static
         | typing are overstated, but this is a case where strong static
         | types really do completely eliminate an entire category of
         | errors. Given how common these errors are, not using stronger
         | types in this situation for popular languages has absolutely
         | been a billion dollar mistake.
        
           | masklinn wrote:
           | > Haskell and some other strongly-typed languages do this
           | (but they call it Maybe).
           | 
           | Most call it either Option or Optional FWIW. `Maybe` is the
           | term used by Haskell and its derivatives (like Idris or Elm).
        
         | twic wrote:
         | > Imagine, for example, in an Optional based language, that you
         | just map the optional to a lambda to execute on the optional,
         | and the behaviour of the optional is to then simply silently do
         | nothing if it's optional.none. That'd be a much harder to find
         | bug than a nullpointer error.
         | 
         | It's worth noting that this is only possible if the operation
         | you're mapping over the optional can have side-effects. Without
         | side-effects, mapping over an optional _always_ does nothing,
         | in a way - all the difference is in the value returned.
         | 
         | Adding that constraint does make programming pretty painful,
         | though.
        
       | olliej wrote:
       | Null termination is still easily much worse. At least the general
       | case of null dereferences today (less so earlier) is a page
       | fault.
        
       | agumonkey wrote:
       | Should every domain have a Nil element instead ?
        
         | loopz wrote:
         | What is required is an optional "No" element. Then you can say,
         | I have a "No" Problem, and people will think you're joking and
         | remain on their happy path.
        
         | fhars wrote:
         | No, obviously not. Every domain having a Nil element is exactly
         | the problem null references have introduced (at least for the
         | call by reference parts of the affected languages).
        
           | agumonkey wrote:
           | Null is a single nil for all, I meant having a null per
           | domain would force people to think of what it means to have
           | nothing in that field and handle it. Maybe I'm too naive.
        
             | augusto2112 wrote:
             | Sometimes you don't want to allow a value to be null at
             | all, but with null references you can't represent that at
             | the language level.
        
               | agumonkey wrote:
               | But for numbers, a zero is not considered null, because
               | it was handled in the operators rules.
        
               | fhars wrote:
               | Numerical zero has nothing to do with the issue discussed
               | here. What you are proposing is to add another "number"
               | to types like int and float that results in the program
               | crashing whenever you try to add it to another number.
        
               | agumonkey wrote:
               | I think it does
        
               | strictfp wrote:
               | It does! It's the numerical "null object", just like the
               | empty string and empty collection.
        
             | masklinn wrote:
             | > I meant having a null per domain would force people to
             | think of what it means to have nothing in that field and
             | handle it.
             | 
             | In what way would it change anything? The "billion dollars
             | mistake" is that because "nothing" is part of every type,
             | any value you get could really be missing, and you have to
             | either hope for the best (and die like the rest) or program
             | ridiculously defensively.
             | 
             | Having a magical sentinel per type would have the exact
             | same issue, namely that "nothing" is part of the type
             | itself, and so you can never be sure at compile time that
             | you do have "something".
             | 
             | That's what opt-in nullability (whether through option
             | types or language builtins or a hybrid) changes, by default
             | if you're told you have an A it can _only_ be a valid A,
             | and if you 're told you _might_ have an A you _must_ either
             | check for it or use  "missing-safe" operations.
        
             | erik_seaberg wrote:
             | This is one of Go's weirdest features. When you cast nil to
             | an interface, you pay for extra storage so the runtime can
             | do method dispatch on what type of object you _don 't
             | have_, even though every implementation is likely to panic
             | immediately.
        
       | seventh-chord wrote:
       | "Making everything a reference: The Billion Dollar Mistake" is
       | the talk I want to see
        
         | dorfsmay wrote:
         | Everything in Python is a reference, and there's no null
         | pointer issues.
        
           | brudgers wrote:
           | Everything in Python is an object. In Python, containers are
           | objects that reference other objects.
           | 
           | https://docs.python.org/3/reference/datamodel.html
           | 
           | https://docs.python.org/2.0/ref/objects.html
        
           | auxym wrote:
           | I've certainly had some "None" errors in Python.
           | 
           | I think the difference comes from dynamic vs static typing.
           | In Python, you sort of get into the habit of "defensive"
           | programming: checking inputs to your function, catching
           | Nones, etc.
           | 
           | In java, you tend to rely more on the type system. If it
           | typechecks/compiles, there's a good chance it's OK. That is,
           | until you get a null value that's not handled.
           | 
           | That's the root issue I think: If null is an acceptable value
           | per the type, then the same type system should force you to
           | handle it. As do the type systems in ML languages for option
           | types, for example.
        
             | JakobProgsch wrote:
             | The first line is why I'm not 100% convinced of the
             | severity of this mistake compared to the alternatives. The
             | problem fundamentally is the use of magic values/numbers to
             | represent the concept of "no value". You don't need
             | explicit language support to have that concept and the bugs
             | it causes. I guess having that as an intrinsic concept in
             | the language makes it more likely that people use it badly.
             | On the other hand debuggers etc. also intrinsically
             | understand this and segfaults due to null pointers are
             | usually very easy to localize once you see them. On the
             | other hand if a "bad programmer" introduced their own magic
             | non-value in a supposedly safe language, debugging that
             | becomes way more confusing.
        
               | andrepd wrote:
               | No, that's not the "fundamental problem". The fundamental
               | problem is a type system that lies. A "pointer to string"
               | is not actually a pointer to a string, it's a pointer to
               | a string _or to nothing_. If your api returns a pointer
               | of the latter type, it should signal this by making the
               | return type  "maybe-pointer to string" (although it has
               | the same memory representation as "pointer to string").
               | Then, if the user tries to dereference a maybe-pointer
               | (that is, to use a maybe-pointer as a pointer), the type
               | system can statically catch this and make it a simple
               | type check failure compilation error. The user must first
               | check if it's null through a function that casts a maybe-
               | pointer to a pointer.
               | 
               | Nothing about this precludes the usage of sentinel
               | values.
        
         | kragen wrote:
         | You may be interested in http://canonical.org/~kragen/memory-
         | models then. I don't think it's necessarily a _mistake_ but it
         | 's definitely taken for granted far too much.
        
         | x3ro wrote:
         | Can you elaborate? I can't remember the last time I thought "oh
         | darn it why is this a reference", but I can think of a billion
         | problems I've had with nulls in jvm languages
        
           | emsy wrote:
           | Cache incoherency, which will cost us more and more
           | performance as CPUs will improve slower in the future.
        
         | gameswithgo wrote:
         | there are a few completely different ways to interpret this,
         | can you explain?
        
           | seventh-chord wrote:
           | in languages like c, rust or go, where you can put arbitrary
           | data on the stack, it seems to me as if such issues are less
           | common because you dont have to worry about initializing
           | pointers and allocating memory unless you actually want to
           | put something on the heap. Thus if you make everything a
           | reference in your language its no wonder you run into issues
           | like null-pointers more often
        
             | DaiPlusPlus wrote:
             | With stack allocation you then encounter problems with
             | object lifetime. Rust solves this problem by binding
             | references to scope, and Go solves this by invisibility
             | changing an allocation to the heap (and uses ref-counting?
             | I think?).
             | 
             | I wish C had a feature that would let you allocate
             | something on the stack and then return to the parent stack
             | frame without popping the stack-pointer - that would be
             | handy for self-contained object-constructors.
        
               | zozbot234 wrote:
               | Go uses fully-general GC, not reference counting.
               | Obligate reference counting is used in other languages
               | such as Swift, probably with worse throughput than
               | obligate tracing GC.
        
               | cm2187 wrote:
               | Plus also doesn't the stack need to be small to fit into
               | the CPU cache?
        
               | seventh-chord wrote:
               | There is absolutely no requirement from the hardware that
               | the stack be any particular size
        
               | DaiPlusPlus wrote:
               | On the Windows desktop, the default stack size is 1MB. In
               | IIS-hosted applications the default stack size is reduced
               | to 250KB due to the popularity of the now-outdated
               | programming trope of "one thread per request (per-
               | connection)". On x86 Linux the default stack size is 2MB
               | - which seems generous.
        
               | cm2187 wrote:
               | I am not thinking requirement by rather performance wise.
        
               | giulianob wrote:
               | Regardless of whether it's on the stack or heap the point
               | still stands. If all your objects are randomly allocated
               | then an array is just references to those objects and
               | will start out null. If you're using value types then
               | your array of objects will never be null (empty instead)
               | and you will benefit from CPU caching the data.
        
       | x3ro wrote:
       | This comes up again and again in one form or the other, yet new
       | languages still seem to be making the same mistake. Of all
       | languages I've touched, Rust seems to be the only one that mostly
       | circumvents this problem. Are there other good examples?
        
         | deepaksurti wrote:
         | Swift with optional and optional chaining. [1]
         | 
         | [1] https://docs.swift.org/swift-
         | book/LanguageGuide/OptionalChai...
        
         | zozbot234 wrote:
         | > Rust seems to be the only one that mostly circumvents this
         | problem.
         | 
         | The Rust hype is getting ridiculous here. There are plenty of
         | languages with non-nullable references as first-class, and
         | optionals for the nullable case.
         | 
         | (...And I say this as a Rust fan myself, for what it's worth.)
        
           | samatman wrote:
           | This comment would be much improved with a list of those
           | languages.
           | 
           | Kotlin and Swift come to mind, what are others?
        
             | kragen wrote:
             | If we're limiting ourselves only to new languages, then
             | nulls are statically excluded not only by Kotlin and
             | Apple's imitation of it, Swift, but also by F#, Agda,
             | Idris, and (sort of) Scala. But the zozbot didn't seem to
             | be talking only about new languages, so Haskell, Miranda,
             | Clean, ML, SML, Caml, Caml-Light, and OCaml are also fair
             | game. (It wouldn't be hard to list another dozen in that
             | vein.) Moreover I think you could sort of make a case for
             | languages like Prolog and Aardappel where you don't have a
             | static type system at all, much less one that could
             | potentially rule out nils, but in which the consequences of
             | an unexpected nil can be much less severe than in
             | traditional imperative and functional languages like Java,
             | Lua, Python, Clojure, Smalltalk, or Erlang, which more or
             | less need to crash or viralize the nil in those cases.
        
           | amelius wrote:
           | Imho, Rust is an awkward language because it positions itself
           | as a systems language but it makes low-level stuff more
           | difficult (there's even a book teaching how to implement
           | doubly linked lists in Rust [1]), hence prone to mistakes. At
           | the same time, people are using Rust to build non-systems
           | programs, where other languages would be more appropriate
           | (e.g. those with garbage collectors). I don't think it is a
           | good idea that Rust is promoted as the language that will
           | rule them all; in my opinion, it is still a research
           | language.
           | 
           | Linus Torvalds said the following about Rust [2]:
           | 
           | [What do you think of the projects currently underway to
           | develop OS kernels in languages like Rust (touted for having
           | built-in safeties that C does not)?]
           | 
           | > That's not a new phenomenon at all. We've had the system
           | people who used Modula-2 or Ada, and I have to say Rust looks
           | a lot better than either of those two disasters.
           | 
           | > I'm not convinced about Rust for an OS kernel (there's a
           | lot more to system programming than the kernel, though), but
           | at the same time there is no question that C has a lot of
           | limitations.
           | 
           | [1] https://rust-unofficial.github.io/too-many-lists/
           | 
           | [2] https://www.infoworld.com/article/3109150/linux-
           | at-25-linus-...
        
             | zozbot234 wrote:
             | > there's even a book teaching how to implement doubly
             | linked lists in Rust [1]
             | 
             | Doubly-linked lists are an awkward example because the
             | "safety" of a doubly-linked list as a data structure
             | involves fairly complex invariants that Rust can't even
             | keep track of at this point, much less check independently.
             | These things are exactly why the unsafe{} escape-hatch
             | exists and is actively supported. But just looking at the
             | _amount_ of unsafe code in common Rust projects should
             | suffice to figure out that this is not the common case, at
             | all.
             | 
             | > At the same time, people are using Rust to build non-
             | systems programs, where other languages would be more
             | appropriate (e.g. those with garbage collectors).
             | 
             | Garbage collectors are good for one thing, and one thing
             | only: keeping track of complex, spaghetti-like reference
             | graphs where cycles, etc. can arise, perhaps even as a side
             | effect of, say, implementing some concurrency-related
             | pattern. Everything else is most likely better dealt with
             | by a Rust-like system with _optional_ support for reference
             | counted data.
             | 
             | That's without even mentioning the _other_ advantages that
             | a Rust-like ownership system provides over a GC-only
             | language. See e.g.
             | https://llogiq.github.io/2020/01/10/rustvsgc.html this
             | recent post for some nice examples.
        
         | pkulak wrote:
         | Kotlin
        
         | progval wrote:
         | > Rust seems to be the only one that mostly circumvents this
         | problem. Are there other good examples?
         | 
         | Rust is not the first one to have an Option type; it's a common
         | feature of functional languages because they have ADTs (
         | https://en.wikipedia.org/wiki/Algebraic_data_type )
        
         | masklinn wrote:
         | > Rust seems to be the only one that mostly circumvents this
         | problem. Are there other good examples?
         | 
         | Swift, Kotlin, and of course older languages of a functional
         | bend like MLs, Haskell, Idris, Scala, ...
         | 
         | Some are also attempting to move away from nullable references
         | (e.g. C#), though that is obviously a difficult task to perform
         | without extremely severe disruptions.
        
           | the_alchemist wrote:
           | Scala happily accepts null as it is the bottom type for
           | AnyRef and needed for jvm compatibility. Kotlin has a
           | compiler check that enforces it, Scala does not.
        
             | gmartres wrote:
             | It's coming to Scala too:
             | https://dotty.epfl.ch/docs/reference/other-new-
             | features/expl...
        
               | rubyn00bie wrote:
               | I really love(d) Scala for introducing me to the whole
               | idea of Optionals.
               | 
               | I wish for the life of me I felt like I could approach
               | Scala at a time when it wasn't going through huge flux (I
               | have shitty luck). I spent a good amount of time pre-
               | version 2.10 :( and then recently went to have a look but
               | saw Dotty (version 3.0?) coming by the end of 2020 and I
               | was like "well, FML, time to wait a few more years and
               | try again."
               | 
               | Anyone have any tips for using the Scala ecosystem
               | effectively these days? Should I just wait for 3.0? Is it
               | going to be a long winding road of breaking changes until
               | a "3.11" version?
               | 
               | Is there a good resource for what folks are using it for
               | these days? It seems like all the projects I used to know
               | are ghostly on Github (but that could also be the fact it
               | has been quite a few years, heh). Or do most folks just
               | pony-up and use plain ol' Java libraries while writing
               | their application/business logic in Scala?
        
         | augusto2112 wrote:
         | Functional programming languages have been doing it for ages.
         | Most "newer" statically typed languages also have it (Swift,
         | Kotlin, Rust) by default. And old languages had it bolted on
         | (C# 8, Java 8, C++ 17).
         | 
         | I think at this point basically everyone has realized null by
         | default is a terrible idea.
        
           | masklinn wrote:
           | > And old languages had it bolted on (C# 8, Java 8, C++ 17).
           | 
           | C#: actually true, you can switch over to non-nullable
           | reference types
           | 
           | Java 8: meeeh, it provides an Optional but all references are
           | still nullable, including references to Optional. There are
           | also @Nullable and @NotNull annotations but they're also meh,
           | plus some checkers handle them oddly[0]
           | 
           | C++17: you can deref' an std::optional, it's completely
           | legal, and it's an UB if the optional is empty. Despite its
           | name, _std::optional is not a type-safety feature_ , its goal
           | is not to provide for "nullable references" (that's a
           | pointer), it's to provide a stack-allocated smart pointer
           | (rather than have to allocate with unique_ptr for instance).
           | 
           | [0] https://checkerframework.org/manual/#findbugs-nullable
        
         | gameswithgo wrote:
         | rust, f#, ocaml, latest version of c# has an option to sort of
         | get rid of nulls, zig
        
         | hawkice wrote:
         | Haskell, notoriously. I believe it pioneered the ergonomics of
         | the alternatives used elsewhere.
        
           | cmrdporcupine wrote:
           | AFAIK Standard ML predates Haskell and it has an option type.
        
             | dunefox wrote:
             | ML is even older than SML and has algebraic data types.
        
         | davidgay wrote:
         | The reason they come up again and again is that it's hard to
         | design an imperative language without them (try, assuming you
         | want to provide generic user-defined data structures that allow
         | for cycles).
         | 
         | As a result, calling them a "mistake" is reasonably dishonest,
         | as it implies there was an obvious, better alternative.
        
         | jrockway wrote:
         | I assume two reasons, efficiency and because an efficient
         | implementation of mutable state would have the same problem.
         | 
         | Right now, a single sentinel value makes a pointer null or not
         | null (0x0 is null, everything else is not null). This is
         | exactly how you'd implement a stricter type, like "Maybe".
         | Encoded as a 64-bit integer, "Nothing" would be represented as
         | 0x00000000 and "Just foo" would be represented as 0xfoo. No
         | object may be stored at the sentinel value, 0x00000000. Exactly
         | the same as what we have now, and provides no assurances that
         | 0xfoo is actually a valid object.
         | 
         | Meanwhile, Haskell which "doesn't have null" crashes for
         | exactly the same reason your non-Haskell program crashes with a
         | null pointer exception:                   f :: Num a => Maybe a
         | -> Maybe a         f (Just x) = Just (x + 41)
         | 
         | This blows up at runtime when you call f Nothing, because f
         | Nothing is defined as "bottom", which crashes the program when
         | evaluated.
         | 
         | It's exactly the same as langages with null pointers:
         | func f(x *int) *int {             result := *x + 41
         | return &result         }
         | 
         | And the solution is the same, your linter or whatever has to
         | tell you "hey maybe you should implement the Nothing case" or
         | "hey maybe you should check the null pointer".
         | 
         | Where I'm going with this is that you need to develop entirely
         | new datatypes and have an even stricter type system than
         | Haskell. Maybe Rust is doing this, but it's hard. We all know
         | null is a problem, but calling null something else doesn't make
         | the problems go away.
        
           | anderskaseorg wrote:
           | > It's exactly the same as langages with null pointers:
           | 
           | Four huge differences:
           | 
           | 1. You don't need to pass around 'Maybe a' everywhere. If
           | null isn't expected as a possible value (which usually it
           | isn't), you just pass around 'a', and when you do use 'Maybe'
           | it actually means something.
           | 
           | 2. The Haskell compiler can, and does (with -Wall), tell you
           | that your pattern match is non-exhaustive. You don't need a
           | separate "linter or whatever". This is possible because the
           | needed information is present in the type system, and doesn't
           | need to be recovered with a complicated and incomplete static
           | analysis pass.
           | 
           | 3. If you do this anyway, the error is thrown at exactly the
           | point where 'Maybe a' is pattern-matched, not at some random
           | point several function calls later where your null has
           | already been coerced into an 'a'.
           | 
           | 4. This program is defined to throw an error; it's not
           | undefined behavior like in C that could result in something
           | weird and unpredictable happening later (or earlier!).
           | 
           | Also, Rust optimizes away the tag bit of 'Option' under
           | common circumstances; for example, 'None: Option<&T>' (an
           | optional reference to 'T') is represented internally as just
           | a null pointer, which is safe because '&T' cannot be null.
        
             | jrockway wrote:
             | > You don't need to pass around 'Maybe a' everywhere.
             | 
             | You don't need to pass pointers around everywhere.
             | Languages with null still have value types that cannot be
             | null.
             | 
             | > You don't need a separate "linter or whatever".
             | 
             | Optional compiler flags count as "whatever" to me.
             | 
             | > it's not undefined behavior like in C that could result
             | in something weird and unpredictable happening later (or
             | earlier!)
             | 
             | C++ doesn't define this, but the OS does (and even has help
             | from the CPU).
             | 
             | Anyway, my TL;DR is that it's easy to have a slow program
             | that passes everything by value, or east to have a fast
             | program that uses pointers or references. Removing the
             | special case of null is meaningless, because you can still
             | have a pointer to 0x1 which is just as bad as 0x0,
             | probably. This goes back to my original answer to the
             | question "why don't more languages get rid of null" which
             | was "it's harder than it looks." I think I'm right about
             | that. If it were easy, everyone would be doing it.
        
               | temac wrote:
               | > Languages with null still have value types that cannot
               | be null.
               | 
               | Not all languages.
               | 
               | > C++ doesn't define this, but the OS does (and even has
               | help from the CPU).
               | 
               | That's not how it works anymore, because C / C++ front-
               | ends interacting with the optimizers are yielding too
               | "optimized" results. See the classic
               | https://t.co/mGmNEQidBT
        
           | tibbe wrote:
           | This missed the point. The point of not that you can forget
           | to check the null case. The point is that you can express
           | that sometimes there's no null case.
        
             | jrockway wrote:
             | The "no null" case in traditional languages is just "int"
             | instead of "*int". All values inside an "int" are valid
             | integers.
             | 
             | Certainly it's problematic to use the same language
             | primitive to mean "a pointer" and "this might be empty",
             | but it's what people use them for in every language that
             | has pointers (that I've used anyway).
        
           | dunefox wrote:
           | That's not the same thing as a null pointer because Nothing
           | isn't allowed in place of e.g. integers, strings, etc. like
           | in Java. What you're doing is defining a non-total function.
           | Haskell, per default, doesn't perform exhaustivity checks
           | when pattern matching, but you can enable that via a compiler
           | flag - then it won't let you compile your example. Ocaml, for
           | example, does that by default.
        
           | [deleted]
        
       | RickJWagner wrote:
       | No comment on the Null References, but I will say I _love_ the
       | time-index provided for the video. I wish every video had these!
        
       ___________________________________________________________________
       (page generated 2020-01-11 23:00 UTC)