1
votes

Exploring this material: Lens over tea I've encountered an interesting (simple at first) point:

ex3 :: (a, b) -> (b, a)
ex3 = do
 a <- fst
 b <- snd
 return (b, a)

Everything's fine, but what type of monad does this function use (since we have a do-block inside). After a few attempts I arrived to this conclusion:

ex2 :: ReaderT (a, b) ((,) b) a
ex2 = ReaderT $ do
  a <- fst
  b <- snd 
  return (b, a)

ex3 :: (a, b) -> (b, a)
ex3 = runReaderT ex2

So, we have ReaderT that uses inner monad ((,) b). Interestingly enough - I got not enough satisfaction with this and decided to rewrite ex2 not using do-notation. This is what I got:

ex2 :: Monoid b => ReaderT (a, b) ((,) b) a
ex2 = ReaderT $ 
  \pair -> return (fst pair) >>= 
  \a -> return (snd pair) >>= 
  \b -> (b, a) 

or even:

ex2 :: Monoid b => ReaderT (a, b) ((,) b) a
ex2 = ReaderT $ 
  \pair -> (mempty, fst pair) >>= 
  \a -> (mempty, snd pair) >>= 
  \b -> (b, a) 

Both variants require b to have a Monoid type restriction. The question is: can I write this functions with (>>=) only and without using a Monoid restriction - like we have with do-notation variant? Apparently we do the same with or without do-notation. Maybe the even difference, that we have to construct monads at every step in the second and thirst fuctions and this requires us to state that "b" should be a monoid - some monoid. And in the first case we just extract our values from some monad - not constructing them. Can anybody explain am I thinking in the right direction?

Thank you!!

3

3 Answers

2
votes

You have not quite desugared this from do notation to (>>=) calls. A direct translation would look like this:

ex2 :: ReaderT (a, b) ((,) b) a
ex2 = ReaderT $
  fst >>= (\a ->         -- a <- fst
  snd >>= (\b ->         -- b <- snd
  return (b, a)))        -- return (b, a)

Also, you aren't actually using the monadness of (,) b, even though it fits into the slot here for the "inner monad" of ReaderT.

1
votes
ex3 :: (a, b) -> (b, a)

means, in prefix notation

ex3 :: (->) (a, b) (b, a)
       -----------m
                   ------t

Hence the monad is m = (->) (a, b) which is the Reader monad (up to isomorphism) with a pair as its implicit argument / read-only state.

You don't need a monoid. The plain reader monad is enough. If you want to use ReaderT, use the identity monad as the inner monad.

ex2 :: Monoid b => ReaderT (a, b) Identity (b, a)
ex2 = ReaderT $ 
  \pair -> Identity (fst pair) >>= 
  \a -> Identity (snd pair) >>= 
  \b -> Identity (b, a) 

Of course, the code above could be simplified.

0
votes

So to summarize:

  1. Type of monad is (->) r - just function or simple reader;
  2. How to desugar the initial function without do:

    ex3' :: (a, b) -> (b, a)
    ex3' = fst >>=
      \a -> snd >>=
      \b -> return (b, a)