Optionality is subtle

Published on

Programming languages often encode optionality. Some languages allow arbitrary values to be null, others use a type that encodes optionality (Maybe in Haskell, Optional in Elm). I claim that these constructs are insufficient to fully encode optionality in the sense that humans understand it. These constructs are all abstractions over common patterns in computer hardware, and while the patterns were indeed designed as an encoding of a particular thought, they leak the fact that a computer is underneath. Consider the following questions and answers:

What is the name of the 8th weekday? There is no 8th weekday.

How old is Jack? We don’t know because we haven’t asked him yet.

What is in that box? There is nothing in the box.

A Haskell programmer might describe all of these answers the same way: Nothing. The language provides many useful tools for dealing with Nothing, many of which do indeed correspond to related thoughts that a person might have. But it is still necessary for a programmer to translate between his or her thoughts and the corresponding elements of the program. I suppose we programmers don’t usually think about this as it is the air we breathe, but it has been on my mind recently. I believe this kind ambiguity is the cause of all sorts of problems but rarely gets the blame.

In the functional programming world, types can be viewed as specifications which presumably correspond to human thoughts about systems. But in most languages that I have used, types come with an implementation. In Haskell, String is a singly-linked list of Unicode characters, Text is a contiguous sequence of Unicode characters, and ByteString is a contiguous sequence of bytes. Someone wanting to store an English sentence in their program might choose any one of these types depending on their desired performance characteristics and what operations are available, but ambiuity arises because nothing about any of these types says “English sentence”. A common solution to this problem is to introduce a new name for the type, or a wrapper:

newtype EnglishSentence = EnglishSentence Text

But this forces a particular set of available operations for the concept of the English sentence, when prior to this abstraction the developer had a freer choice. The obvious solution to this problem is to use a typeclass:

class EnglishSentence a where
  ...

Using typeclasses is awkward compared with using types, and can result in slower programs because of vtable lookups. Also, there is no built-in way to convert between EnglishSentence implementations for different purposes, so you have to provide something like

convert :: (EnglishSentence a, EnglishSentence b) => a -> b

which may not even be possible without an auxiliary conversion typeclass. Under the hood, you are still abstracting over types, which abstract over computer hardware.

I don’t have a ready answer to these complaints, but I feel there must be one. Some food for thought: if someone could encode their thoughts in a language that is designed for only that purpose, then making this into a program is the process of providing a mapping from this language into the computer’s native language.