Type discipline and missing values
Thus spake Fabian Pascal:
The relational model is predicate logic applied to databases. Predicate logic is the real-world’s two-valued logic (true/false). As I explain in chapter 10 of PRACTICAL ISSUES in DATABASE MANAGEMENT (a DATABASE FOUNDATIONS paper, “Can’t Assert What You Don’t Know,” is forthcoming), logic guarantees correctness — defined as consistency — of query results. It is to preserve logical correctness, therefore, that Codd’s Information Principle requires that all information in relational databases be represented as values in relations. The term “NULL values” suggests that Chamberlin does not realize that part of the problem with NULLs is that they are not values — indeed, they are supposed to be markers for the absence of values. Whatever a database table with NULLs is, neither is it a relation, nor do NULLs represent anything in the real world and, consequently, correctness and the rest of the relational benefits are lost.
It is probably best if we pass over in silence that line about the real world’s two-valued logic
. I want instead to talk about the Maybe data type in Haskell.
First of all, here’s the type of Maybe itself:
data Maybe a = Nothing | Just a deriving (Eq, Ord, Read, Show)
This is a parametric type, which means that the a in the type definition can stand for any type. Thus, we can declare a new type as follows:
type FirstType = Maybe Integer
We can declare and assign values of this type, for example:
firstValue :: FirstType firstValue = Just 1 firstNullValue :: FirstType firstNullValue = Nothing
What’s more, we can declare a second type, parameterizing Maybe differently:
type SecondType = Maybe String
Again, we can declare and assign values of this type:
secondValue :: SecondType secondValue = Just "One" secondNullValue :: SecondType secondNullValue = Nothing
It’s worth emphasizing that Maybe is an ordinary parameterized algebraic data type, just like any other parameterized algebraic data type in Haskell, that the subtypes we have declared are also ordinary types and that the values we have assigned are ordinary values. We can define functions (that is, mappings from a domain to a codomain, both typed sets of values) using these types:
timesTwo :: Maybe Integer -> Maybe Integer timesTwo val = case val of Nothing -> Nothing Just n -> Just (n * 2) exclaim :: Maybe String -> String exclaim val = case val of Nothing -> "?!!!" Just s -> s ++ "!"
In the case of arithmetic operations, it’s probably wisest to decree that Nothing will come of Nothing. When promoting a humdrum string to an exclamation, we may perhaps allow that exclaim Nothing results in the sound of one hand clapping.
As far as type discipline is concerned, the Haskell type checker will (at compile-time) object to any attempt to pass a Nothing value of type Maybe String to a function expecting a value of type Maybe Integer:
typeError = timesTwo secondNullValue
It will not even permit the comparison of the two Nothings:
typeError = firstNullValue == secondNullValue
However, two Nothings of the same type are treated as equivalent:
firstNullValue == timesTwo firstNullValue
evaluates to True. But:
secondNullValue == exclaim secondNullValue
also results in a type error at compile time. It is nonsensical to compare a string-typed value to a value of a type whose values range over the discriminated union of the values of the types just a string
and no string
(which is a clumsy way of putting it, but less incorrect than some alternatives).
It is possible to view a nullable
field as (deep breath) a field of a type whose values range over the discriminated union of the values of the types Just a and Nothing, and if one is consistent in doing so then no breakdown of logical coherence need occur. This is not to say that all uses of nulls in SQL databases are subject to the type discipline outlined here, or that a nullable field is a good way of representing a potentially missing value (on the contrary: Nothing is a value). However, there may be cases when the use of a nullable field provides an acceptable representation of the type of value we wish to assert: far from being by definition absurd, it is possible for Maybe a to be exactly what we mean.
