5

I'm working trough the book Haskell in depth and I noticed following code example:

withReader :: (r' -> r) -> Reader r a -> Reader r' a

This looks like contramap. What the relationship between Control.Monad.Reader and Data.Functor.Contravariant?

senjin.hajrulahovic
  • 2,961
  • 2
  • 17
  • 32

1 Answers1

8

Reader's type parameters aren't in the right order for that to be contramap for it. A Contravariant functor always needs to be contravariant in its last type parameter, but Reader is contravariant in its first type parameter. But you can do this:

newtype FlippedReader a r = FlippedReader (Reader r a)

instance Contravariant (FlippedReader a) where
    contramap f (FlippedReader x) = FlippedReader (withReader f x)

Reader is also almost a Profunctor, with lmap = withReader and rmap = fmap, but that doesn't quite work since Reader r a is really a type synonym for ReaderT r Identity a (although you could use another newtype wrapper to make it work like I did above). And (->) actually is a Profunctor with equivalent behavior, and it's isomorphic to Reader.

  • 2
    `newtype FRT m e a = FRT (ReaderT e m a)`. This is a `Profunctor` whenever `m` is a `Functor`, and I believe an `Arrow` whenever `m` is a `Monad`. – dfeuer Nov 03 '21 at 08:27
  • 2
    @dfeuer Yup -- `newtype FRT m e a = FRT (ReaderT e m a) deriving (Category, Arrow) via Kleisli m` – duplode Nov 03 '21 at 22:23
  • @duplode, yeah, I knew it was a `Category` but I wasn't totally sure about `Arrow`. I don't think I ever realized that `ReaderT` is a flipped version of `Kleisli`, though it's pretty obvious. – dfeuer Nov 04 '21 at 01:53
  • 1
    @dfeuer Indeed; I don't think I'd have realised it either hadn't I stumbled upon [this (Scala!) question](https://stackoverflow.com/q/61899370/2751851) a while ago. – duplode Nov 04 '21 at 01:58