Maybe it’s in this cup, maybe it’s in that cup.

Maybe: it’s Just / Nothing

When failure is an option and runtime errors are not

Michel Belleville
Published in
4 min readJan 22, 2019

--

me> Hey.

wat> 🎼 Hey, I just met you 🎶 and I’m your ducky 🦆 so what’s the matter? 🎵 Just tell me maybe…

me> 👏 soooo… I need to convert a String into an Int and what I found is this: String.toInt : String -> Maybe Int ; what’s a Maybe?

wat> It’s a type. Here’s its definition:

Maybe something
= Just something
| Nothing

me> Ok, let me see if I get it: Maybe something is a type with one type parameter (something) ; its values are either Just (which holds a value of type something inside) or Nothing (and there’s nothing inside Nothing).

wat> Yep. So you can have a Maybe Int that can take values like Just 1, Just 747, Just -23 or Nothing for example.

me> So that means String.toInt will try and convert the String into an Int, and give me back either a Just with the Int inside when it succeeds, or Nothing when it failed?

wat> Looks like it does.

me> Cool… But when I tried to add a number to my Maybe Int I had a compilation error:

> (String.toInt "213") + 456
-- TYPE MISMATCH --------------------------------------- elm
Addition does not work with this value:4| (String.toInt "213") + 456
^^^^^^^^^^^^^^^^^^
This `toInt` call produces:
Maybe IntBut (+) only works with Int and Float values.Hint: Use Maybe.withDefault to handle possible errors. Longer term, it is usually better to write out the full `case` though!

wat> Aaaah, the elm compiler, always helpful. What it’s telling you here is that you’re trying to add a Maybe Int with a number, and that is not possible because you can only add a number to another number as the signature for the (+) function attests: (+) : number -> number -> number.

me> Fine, I can’t do that. But what can I do?

wat> You can do what the compiler also suggests: using Maybe.withDefault to convert your Maybe Int into an Int ; here’s the signature for Maybe.withDefault : something -> Maybe something -> something

me> So… Maybe.withDefault takes a value of type something, another value of type Maybe something and returns another value of type something?

wat> Exactly. From the signature, I’ll hasard a guess as to how it works:

Maybe.withDefault : something -> Maybe something -> something
Maybe.withDefault default maybe =
case maybe of
Just value ->
value
Nothing ->
default

me> It takes the maybe and checks whether it’s a Just and return the value inside the Just, or it finds a Nothing in which case it returns the default?

wat> Yep. So now we can do what you wanted to do, and it works:

> (Maybe.withDefault 0 (String.toInt "123")) + 456
579 : Int

me> Cool… and if the String was not convertible into a number, Maybe.withDefault 0 (String.toInt "not convertible into a number") would be 0 and the result of the addition would be 456?

wat> You’re getting good at this. The point of a Maybe is to give you the possibility of failing without causing a runtime error (forcing the programmer to deal with the failure, ultimately). And that’s an interesting property because sometimes you don’t want to deal with the error just now.

me> You mean, change what is in the Just (if it’s a Just and not a Nothing) but keep it under the Maybe wrapping?

wat> Yes. It would be cool if we had a function that takes a maybe and a transformation on its content, and returns a new maybe with the transformation applied to its content…

me> We can write one with a case:

transformInsideMaybe : Maybe a -> (a -> a) -> Maybe a
transformInsideMaybe maybe transformation =
case maybe of
Just value ->
Just (transformation value)
Nothing ->
Nothing

wat> What you’ve just written looks just like another function I know…

Maybe.map : (a -> b) -> Maybe a -> Maybe b

me> What does it do?

wat> It does just what your transformInsideMaybe function does, but the transformation argument goes first, and the transformation can change the type of what may be inside the Maybe value.

me> Changing the type is cool… but why put the transformation first?

wat> That’s because it allows you to “package” a transformation with the Maybe.map, which is useful when you want to use the pipeline style:

someString
|> String.toInt
|> Maybe.map (\n -> n + 456)
|> Maybe.withDefault 0

me> 😮 looks like someone will have to tell me about this “piping” style

wat> 😉 looks like someone needs a break…

me> 😅 looks like someone’s guilty as charged.

--

--