Right on time to switch track.

The Switch

When we learn how to get Nothing from Just something

Michel Belleville
Wat, the Elm-ist
Published in
4 min readFeb 20, 2019

--

me> Wat… how are you doing?

wat> I’m on the right tracks, baby… 🚋

me> Do I want to ask you what you’re doing on a train?… 🤔

wat> Well, since last time we found a map and all I thought I’d explore a little bit. 😉

me> Good, because I found another family of functions that looks quite like map and I was wondering if you could tell me more…

Maybe.andThen : (a -> Maybe b) -> Maybe a -> Maybe b
Generator.andThen : (a -> Generator b) -> Generator a -> Generator b
Decoder.andThen : (a -> Decoder b) -> Decoder a -> Decoder b

wat> Oh my… looks like you’ve stumbled over the dreaded M-word!

me> What’s the M-word? Is it like Functor is the F-word?

wat> Yes, but wooooorse. The horrible, terrible M-word is so powerful it is rumored that those who understand it are cuuuuursed and unable to explain it to others. 👻

me> You’re kidding me right?

wat> Do I look like the kind of ducky that believes in curses? 😆 Nah, it’s nothing as complicated as programmers’ folk tales have it. In fact, it’s pretty much as simple as understanding map and Functors. Remember you can replace the word Functor by “something that can be map-ed over”?

me> Yup.

wat> So now, the word is Monad. And it can be replaced by “something that can be andThen-ed over”.

me> But… I don’t know what… andThen… does… and… 🙄 you’re going to make me read signatures and figure it out by myself again, aren’t you?

wat> 😇

me> Ok… Let’s start with Maybe, at least I know what a Maybe looks like…

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

me> … so, andThen for Maybe :
- takes a function that takes an a and returns a Maybe b
- then it takes a Maybe a
- and finally, returns a Maybe b
…which I guess is produced by unpacking the a value in the Maybe a, feeding it to the a -> Maybe b function?

wat> Exactly. And if the Maybe a was a Nothing after all?

me> It just returns Nothing?

wat> Yes, because Nothing is a valid Maybe b value too.

me> So… how is that different from map? 😅

wat> Let’s compare their signature:

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

me> Oh… Maybe.map takes an a -> b transformation while Maybe.andThen takes an a -> Maybe b transformation.

wat> Which means that the operation you give to Maybe.map can only change what is inside your Maybe ; if your original Maybe was a Just, the result will necessarily be another Just, never a Nothing. With Maybe.andThen you can choose whether you’ll return another Just or, in a surprise twist give back a Nothing!

me> Hmm… So, instead of letting Maybe.map unpack and re-pack the value for me, Maybe.andThen only unpacks it and lets me do the re-packing?

wat> Yep. 📦

me> Why?

wat> Let’s consider a situation in which we receive an input from the user. We receive a String, and it needs to be casted to Int

me> Sounds familiar 😉 we can use String.toInt and we get a Maybe Int. Now what?

wat> We also want the Int value to be a positive value, so Just -1, Just -23 or Just -777 are as useful to us as Nothing. Say we write a function that takes the Int we got (maybe) and returns a Maybe Int if it’s negative:

onlyPositive : Int -> Maybe Int
onlyPositive value =
if value >= 0 then
Just value
else
Nothing

me> So… that’s where Maybe.andThen comes in and saves the day, right?

wat> 👏 Exactly:

input
|> String.toInt
|> Maybe.andThen onlyPositive

wat> Now, let’s say I only want integers that are lower than 9000 too…

me> We could change onlyPositive to integrate that condition?

wat> We’d have to change the name too if we don’t want to surprise whomever comes next to read the code… besides, isn’t it better to have small functions that do one thing and do it well? So let’s make another simple function instead:

itsUnder9000 : Int -> Maybe Int
itsUnder9000 value =
if value < 9000 then
Just value
else
Nothing

me> And now we can add it to the pipeline I guess:

input
|> String.toInt
|> Maybe.andThen onlyPositive
|> Maybe.andThen itsUnder9000

wat> Yup 😄 Since we always get a Maybe value back (just like with Maybe.map), we can pile up Maybe.andThens and Maybe.maps in the pipeline and work on the potential value as if we were sure it was there (or let the piepline bypass all those Nothing values right to the end).

me> So, it’s a bit as if we had two parallel tracks… when we use Maybe.map, whichever track we’re on we continue along, transforming the value when there is one, or just passing by on the other track when there’s none…

Just 1 |> Maybe.map ((+) 2) -- on the 'Just' track...
> Just 3 -- ...we continue with a 'Just'
Nothing |> Maybe.Map ((+) 2) -- but on the 'Nothing' track...
> Nothing -- ...we continue with a 'Nothing'

wat> …whereas when we use Maybe.andThen, we have the opportunity to go from the Just track to the Nothing track.

Just 1 |> Maybe.andThen onlyPositive -- on the 'Just' track..
> Just 1 -- ...we may continue with a 'Just'...
Just -1|> Maybe.andThen onlyPositive
> Nothing -- ...but we may also continue with a 'Nothing'
Nothing |> Maybe.andThen onlyPositive -- on the 'Nothing' track...
> Nothing -- ...we always continue with a 'Nothing'it to others. 👻

me> Ok… but what’s a Monad in all this? And how does it work with a Generator or a Decoder?

wat> Well, I guess this is a story for next time… 😪

--

--