Skip to content

NULL/nil/none/void/... value considered harmful (a "billion dollar mistake" of Tony Hoare) #46

@dumblob

Description

@dumblob

This post will be written with XL being conceptual language in mind. At some places it might sound differently, but I've chosen to use that terminology to be clear about the intent. The concepts I'm presenting are though generic and apply as well to XL as a conceptual language.

I believe everybody is familiar with the "billion dollar mistake" (as Tony Hoare calls it). I saw there is a bunch of recent commits (c3dde14 , 68b258d , 3da52b4 , 11ed0bd ) leveraging nil functionality.

I'd like to oppose using nil value. The question is though "how to deal with such interfaces"?

My answer is "use ephemeral optionals". First "optionals" is a well known concept (Haskell's Maybe, Rust's Option, V's ?, Zig's ...). Despite all the pros (being dead-simple to reason about even in highly parallel systems, no stack unwinding like with exceptions, universal - can be used for errors or alternative values of any kind etc.), it brings some serious usability issues.

Namely it "reifies" the fact whether it's a value or error and thus feels like a "primed bomb" you're tossing around. So what does the ephemeral mean? That means just one thing - the primed bomb can't be assigned to anything. In other words it can be only returned and directly at the caller site has to be dealt with.

Sounds still too inflexible? Well, not anymore if you provide some sugar covering the most common use case - namely propagate (return) the optional as I don't want to handle it here yet. V lang does this perfectly - you just need to write ? after a function call and it'll propagate it. And to handle the ephemeral optional you just append or { print( err ) } to the function call to "catch" the optional and work with the data the callee has put into the optional (which is under the hoods a struct holding arbitrary data).

All in all I'd like XL to get rid of a "lonely" nil value completely and instead use this ephemeral optionals trick together with e.g. sum types (aka tagged unions aka variants). Note there should definitely exist a nil type (a type can't be assigned to anything in run time because it's a type 😉 and thus doesn't appear in run time) - e.g. for tree structures - but no lonely nil value. So a function returning sum type T | nil (the value is either of type T or of type nil - note here nil is not lonely but "bound" to T) will be something totally different than ?T (aka "optional T" - the value is either of type T or an ephemeral error).

Note one can easily have ?(T | nil). E.g. a function which shall return the leftmost child of the leftmost tree node at level 5 - if the tree depth is 4 or less, it'll return an error otherwise it'll return the value of type "sum type of T and nil" which can be assigned to a variable unlike a lonely nil (this assumes it's impossible to define a sum type with less than two types - otherwise one could again create the generic nil bomb by defining a sumtype consisting of only nil type). In other words nil as value actually exists under the hood, but always as a "special" case of some more important value thus providing a compile time explicit guarantee that all such nil values under the hood will be handled and will not leak anywhere.

A bit related is the fact, that nil value should never be used to designate an all-encompassing "alternative value". Not having nil value at all (but only a nil type) is a simple measure to ensure this. Thus e.g. all the I/O functions in one of the commits I refer to above should return some optional instead of nil.

Of course this ephemeral optionals mechanism is totally agnostic from any exception-like or effect-like or Dylan-like or any other mechanism for dealing with alternative computational branches. So no need to do anything on that side.

Any thoughts on this?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions