On optional booleans
In programming, a “boolean” is a data type that is either true or false. An “optional” is a wrapper around a data type that indicates that the value may or may not be present. Thus, an optional boolean has three possible states:
- true
- false
- null
Typically, an optional boolean is used to represent a state where the records might be incomplete. registered
may indicate whether a user is already registered (true
if they are, false
if they are not), allowing for the information to be missing (in which case it’s null
).
Of course, true
, false
, and null
are just words. Any property with exactly three possible states can be encoded with an optional boolean. However, it might seem that one has to sacrifice readability in order to force most three-state properties into an optional boolean.
In my reading tracker app, I have a reading_status
property which has three possible states:
- to_read
- reading
- finished
I could definitely use an optional bool for that, but what does reading_status: null
really mean? I could do the mapping in my head, but chances are, if I look at my code a few months later I will be confused.
Luckily, I found that in more cases than not, the name of the property can make the optional bool perfectly readable. Instead of calling my property reading_status
, for example, I could call it finished_reading
. In that case, true
obviously corresponds to finished, false
to not finished (i.e. currently reading), and null
to “not even engaged in the process of reading, so the question if I have finished does not make sense” (to_read).
Then, if we abstract away from true
, false
, and null
, an optional boolean nicely represents a three state mapping in which two of the states are tightly connected to each other, with the third one being a conceptual outlier or a middle ground.
Could we really force all three state properties to this conceptual paradigm? Most certainly not, but I have been pleasantly surprised at how many of them do lend themselves to such conceptualization with very little in the way of “forcing”. Here are some:
- Voted
in_favor
: yes (true
), no (false
), abstained (null
) - Faction
is_horde
: yes (true
), no (false
), neutral (null
) - Particle’s
spin_up
: yes (true
), no (false
), undetermined/superposition (null
) - A country
is_agressor
: yes (true
), no (false
), not a belligerent (null
) - A person’s
platonic_understanding
: noēsis (true
), doxa (false
), dianoia (null
) - Aristotilean
actualization_status
: tree (true
), dead seed (false
), a seed (null
) - Being
is_sentient
: humans (true
), rocks (false
), animals (null
since unknown) - Soul
is_saved
: in Heaven (true
), in Hell (false
), in Purgatory (null
)
All of these are, in my opinion, very readable. It is reasonable to ask, however, “to what benefit?”, i.e. why not just encode things in simple strings: “to_read”, “reading”, “finished”?
One practical benefit is consistency by constraint: if my reading tracker app stores things in strings, another app that operates on the database, even if written by me, might unintentionally store “reading” as “currently_reading” (as Goodreads does). Now I have four unique strings in that column, which is a pain. What’s worse, I may not even notice, so my SQL queries will be missing some of the rows.
More importantly, it forces me to think about the naming of the properties. Thinking about names forces me to better understand the concepts that I am dealing with. It also lets me build connections between concepts. Aristotilean actualization seems to fit an optional boolean, but Christian Trinity certainly doesn’t.
Finally, of course, it is pretty neat in its own right.