Functors Compose, Monads Do Not

riccardoodone

Riccardo Odone

Posted on April 14, 2019

Functors Compose, Monads Do Not

You can keep reading here or jump to my blog to get the full experience, including the wonderful pink, blue and white palette.


Functor Composition

Let’s start with a refresher of map:

map :: forall a b. (a -> b) -> f a -> f b
Enter fullscreen mode Exit fullscreen mode

In other words, map takes a function a -> b and gives us a function f a -> f b. For that reason, we can take any two nested functors (e.g. Array and Maybe) and run a function on the nested values by putting two maps together:

v :: Array (Maybe Int)
v = [Just 1, Nothing, Just 3]

f1 :: Int -> String
f1 = show

main :: Effect Unit
main = do
 logShow $ map (map f1) v

-- [(Just "1"),Nothing,(Just "3")]
Enter fullscreen mode Exit fullscreen mode

Monad Composition

This time we want to take a look at bind:

bind :: forall a b. m a -> (a -> m b) -> m b
Enter fullscreen mode Exit fullscreen mode

If we tried to compose the same way we did with functors, we would notice the code does not compile:

v :: Array (Maybe Int)
v = [Just 1, Nothing, Just 3]

f2 :: Int -> Array (Maybe String)
f2 i = [Just $ show i]

main :: Effect Unit
main = do
 logShow $ bind v (\x -> bind x f2) -- DOES NOT COMPILE!!
Enter fullscreen mode Exit fullscreen mode

The problem here is in the nested bind:

bind v (\x -> bind x f2)
     ^ Maybe Int
       ^ Int -> Array (Maybe String)
Enter fullscreen mode Exit fullscreen mode

In fact, Maybe Int -> (Int -> Array (Maybe String)) -> ?? is not what bind expects: the first argument seems to indicate that m is Maybe but the second seems to indicate that m is Array. This does not compile since the monad m is supposed to be the same.

To make the program compile we have to make use of a function (i.e. maybe) specific to the monad we are dealing with (i.e. Maybe):

main :: Effect Unit
main = do
 -- logShow $ bind v (\x -> bind x f2) -- DOES NOT COMPILE!!
 logShow $ bind v (maybe (pure Nothing) f2)
Enter fullscreen mode Exit fullscreen mode

Or we could use the MaybeT monad transformer:

v2 :: MaybeT Array Int
v2 = MaybeT [Just 1, Nothing, Just 3]

f3 :: Int -> MaybeT Array String
f3 i = MaybeT [Just $ show i]

main :: Effect Unit
main = do
 --logShow $ bind v (\x -> bind x f2)
 logShow $ bind v (maybe [Nothing] f2)
 logShow $ runMaybeT $ bind v2 f3
Enter fullscreen mode Exit fullscreen mode

Outro

I’ve blatantly copied the content of this blog post out of a talk by Tony Morris. So be sure to check the original stuff out!


Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my PinkLetter!

💖 💪 🙅 🚩
riccardoodone
Riccardo Odone

Posted on April 14, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related