Hacker News new | ask | show | jobs
by efaref 3897 days ago
With nicer syntax, nulls are equivalent to Maybe types. Recent C# for example has the ?. and ?? operators, which are flatMap and orElse.
3 comments

> 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`).

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.

> With nicer syntax, nulls are equivalent to Maybe types.

In a language that lacks maybes, but has nulls, what is the equivalent of the type Maybe (Maybe Int)?

> Recent C# for example has the ?. and ?? operators, which are flatMap and orElse.

Even ignoring the lack of composability, flatMap is an operation on a broad category of which Maybe is one example; that generality is a key part of its utility. ?. is a special-case operation on nullables, if you want to use it on any of the other things that flatMap works on (e.g., lists), you need a different operation, so you can't build generic algorithms. So, its not substantively the equivalent of what Maybe provides in languages where maybe is part of some broader category of operations on which flatMap can be applied.

> In a language that lacks maybes, but has nulls, what is the equivalent of the type Maybe (Maybe Int)?

    int **
> Even ignoring the lack of composability, flatMap is an operation on a broad category of which Maybe is one example; that generality is a key part of its utility.

While it's certainly part of it, I would disagree that it's the key part. The ?. operator is flatMap in the context of Maybes, and Maybes alone are useful for preventing a class of bug (NullPointerExceptions) even without the rest of Monadkind.

> ?. is a special-case operation on nullables, if you want to use it on any of the other things that flatMap works on (e.g., lists), you need a different operation, so you can't build generic algorithms.

Being able to use generic algorithms like sort() on Maybes-as-collections-of-arity-[0,1], while very cute, isn't actually that useful in real code. Particularly real C# or Java code.

Don't get me wrong, I've written code in Scala, and I love the generality of Maybe and related collection types, even though I have hit some interesting issues where I've needed to call .toSeq on my Maybe in order to get the right thing to happen, because they're not quite equivalent. But being pragmatic, and given that C# (and Java) don't have proper Monadic Maybe, and are unlikely to ever get them, I'd quite like the ?. and ?? operators, please.

Equivalent at runtime, yes, but with less static type checking.