Search code examples
haskellfunctional-programmingmonadsfunctor

Defining fmap with id and return


In Category Theory for Programmers by Bartosz Milewski, Milewski writes the following code to define return and the 'fish' operator (composition in Kleisli category) for the Writer monad.

return :: a -> Writer a
return x = (x, "")

(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)
m1 >=> m2 = \x ->
  let (y, s1) = m1 x
      (z, s2) = m2 y
  in  (z, s1 ++ s2)

He then proceeds to define fmap as follows:

fmap f = id >=> (\x -> return (f x))

I have difficulties understanding how the id function is used here. The first argument to the fish operator clearly is (a -> Writer b) but id has the type signature a -> a.

Is this an error or a flaw in my understanding? Replacing id with return makes more sense to me.


Solution

  • Don't forget the universal quantifications.

    Fish (>=>) has type (a -> Writer b) -> ..... for any a and b.

    id has type a -> a for any a.

    Hence, in particular, fish also has type (Writer b -> Writer b) -> ... for any b (just take a = Writer b as a special case).

    Further, id has also type Writer b -> Writer b (again, as a special case).

    The "trick" here is to "merge" the two types using unification. We start by requiring (a -> Writer b) = (a' -> a'), and then we infer a = a' and Writer b = a'. From here, we can see that these two types can be unified, so there is no contradiction in passing the arguments.

    (Also note that here we renamed the a in the type of id to a' to avoid confusion with the other a for the fish)