Refactoring to Monads, Applicatives and Functors

It takes a bit of practice to get used to using Functors, Applicatives and Monads so here are some common patterns I have found that are more succinct when refactored. Hopefully it will help speeding up the journey to becoming a Functor ninja.

Nested Maybes

If you have a series of nested case statements on Maybes :

nestedMaybePre :: Maybe Int -> Maybe Int
nestedMaybePre maybe =
  case maybe of
    Nothing -> Nothing
    Just value -> case evenThing value of
                   Nothing -> Nothing
                   Just num -> Just num

Since Maybe is a monad, you can instead chain them using bind (>>=) :

nestedMaybePost :: Maybe Int -> Maybe Int
nestedMaybePost maybe = 
    maybe >>= evenThing

Or with do notation :

nestedMaybePostDo :: Maybe Int -> Maybe Int
nestedMaybePostDo maybe = do
  value <- maybe
  evenThing value

Functions on the Just

You want to call a function on the value that maybe held in a Maybe :

somethingOnTheJust :: Maybe Int -> Maybe String
somethingOnTheJust maybe =
  case maybe of
    Nothing -> Nothing
    Just value -> Just $ show value

Maybe is a Functor, so you can just use fmap (<$>) :

somethingOnTheJustPost :: Maybe Int -> Maybe String
somethingOnTheJustPost maybe = show <$> maybe

Doing something with multiple Justs

You have a function that takes multiple parameters. You have multiple Maybes to pass in, but if any of them are Nothing the whole function should return Nothing.

onlyMultiplyEvens :: Int -> Int -> Maybe Int
onlyMultiplyEvens num1 num2 =
  case evenThing num1, evenThing num2 of
    Just num1', Just num2' -> Just $ Int.rem num1' num2'
    _, _ -> Nothing

This is where you can use the Applicative instance of Maybe.

onlyMultiplyEvensPost :: Int -> Int -> Maybe Int
onlyMultiplyEvensPost num1 num2 =
    Int.rem <$> evenThing num1 <*> evenThing num2

Bored of intermediate variables

You have a function like the following :

printDateAndTimePre :: Effect Unit
printDateAndTimePre = do
  log "The date is "
  date <- nowDate
  log $ formatDate date

  log "The time is"
  time <- nowTime
  log $ formatTime time

Since nowDate and nowTime return their values wrapped in the Effect monad, you cant directly call log $ formatDate nowDate as formatDate needs the value pulled out of the Monad. The obvious answer is to use an intermediate variable as per the previous solution. This can get a tad arduous, so sometimes it is neater to use bind to pass the variable directly :

printDateAndTimePost :: Effect Unit
printDateAndTimePost = do
  log "The date is "
  log <<< formatDate =<< nowDate

  log "The time is"
  nowTime >>= formatTime >>> log

Note both forms of bind (>>=, =<< are possible. Use the one that makes the most sense.

Also note that because we are essentially passing the parameter to a curried version of log formatDate we need to compose these two functions using function composition (<<<, >>>) rather than with $.