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 $.