Hacker News new | ask | show | jobs
by chriswarbo 3896 days ago
> With nicer syntax, nulls are equivalent to Maybe types.

No they're not, since you can't nest NULLs.

Say I have a database of "Users", with "birthdates" mapping a "User" to a "DateOfBirth" and "spouses" mapping a "User" to a "User", I can look up a User's DateOfBirth either using a Maybe or by allowing NULLs:

    -- Maybe
    getDOB :: User -> Maybe DateOfBirth

    -- Nullable
    getDOB :: User -> DateOfBirth
I can also look up a User's spouse with either a Maybe or a NULL:

    -- Maybe
    getSpouse :: User -> Maybe User

    -- Nullable
    getSpouse :: User -> User
So far they're pretty much equivalent. However, what if I want to look up a spouse's DateOfBirth?

    -- Maybe
    spouseDOB :: User -> Maybe (Maybe DateOfBirth)
    spouseDOB u = fmap getDOB (getSpouse u)

    -- Nullable
    spouseDOB :: User -> DateOfBirth
    spouseDOB u = let s = getSpouse u
                   in if s == NULL then NULL
                                   else getDOB s
These are no longer equivalent. In the Maybe case we can distinguish between three kinds of values:

- `Nothing` indicates that we can't find the spouse

- `Just Nothing` indicates that we can find the spouse but not the DOB

- `Just (Just x)` indicates that we found both the spouse and the DOB

In the nullable case we can only distinguish between two kinds of values:

- `NULL` indicates that either we couldn't find the spouse or we couldn't find the DOB, but we don't know which

- Anything else indicates that we found the spouse and the DOB

The Maybe approach gives us strictly more information; although we can choose to ignore it if we like, either by using `join` or by replacing `fmap` with `>>=` (AKA "bind", which is a combination of `fmap` and `join`).

1 comments

Yeah, but you're comparing apples and oranges. The nullable case isn't preserving the information you want, because you haven't written the code to preserve it, while in your Maybe case, you have. The two examples you give aren't doing the same thing.

Your nullable example is better compared to this, which also discards the required information:

    spouseDOB :: User -> Maybe DateOfBirth
    spouseDOB u = case getSpouse u in
                    s -> getDOB s
                    Nothing -> Nothing
Likewise, the nullable case, modified to preserve it (in a language which actually has nulls this time):

    DateOfBirth* spouseDOB(User& user) {
      Spouse* spouse = getSpouse(user);
      if (spouse)
        return &spouse->dateOfBirth;
      return NULL;
    }
Maybes are just pointers. There's nothing magic about them.
> Your nullable example is better compared to this, which also discards the required information:

Exactly. Your version is just doing what `join` or `>>=` would do, which I mentioned at the end; ie.

    mySpouseDB u = fmap getDOB (getSpouse u)

    -- Using join
    yourSpouseDOB u = join (mySpouseDOB u)

    -- Using >>=
    yourSpouseDOB u = getSpouse u >>= getDOB
> Maybes are just pointers. There's nothing magic about them.

Of course there's nothing magic; `Maybe` just increments types. I was phrasing myself in the context of a language like Java, which has NULL but no pointer manipulation.