3 ms·
> 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" mapp
by chriswarbo 11y 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`).
- david-given 11y agoYeah, 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.
- chriswarbo 11y ago> 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.