Reputation: 833
I find the MonadReader
instance for (->) r
difficult to understand.
Someone from irc mentions one use case for extending some polymorphic functions found in other people's package. I couldn't recall exactly what he meant. Here's an example that relates to what he said but I don't see the point. Could anyone give another example on the usecase of MonadReader
for (->) r
func :: (Show a, MonadReader Int m) => Bool -> m a
func b = case b of
True -> do
i <- ask
show i
False -> "error"
main :: IO ()
main = print $ func True 5
Upvotes: 1
Views: 234
Reputation: 50864
I'm not sure it was intended to have a use case separate from the Reader
monad.
Here's some of the history...
The inspiration for the transformers
library was the set of lecture notes Functional Programming with Overloading and Higher-Order Polymorphism (Mark P. Jones, 1995). In these notes, several named monads (State
, Id
, List
, Maybe
, Error
, and Writer
) were discussed. For example, the Writer
monad type and its instance were defined as:
data Writer a = Result String a
instance Monad Writer where
result x = Result "" x
Result s x ‘bind‘ f = Result (s ++ s’) y
where Result s’ y = f x
The reader monad was also discussed, but it wasn't defined as a separate type. Rather a Read
type alias was used together with a Monad
instance defined directly in terms of the partially applied function type (->) r
:
type Read r = (r ->)
instance Monad (r->) where
result x = \r -> x
x ‘bind‘ f = \r -> f (x r) r
I don't actually know if these type-level "sections" (r ->)
were valid Haskell syntax at the time. Anyway, it's not valid syntax with modern GHC versions, but that's how it appeared in the notes.
The first version of the transformers
library authored by Andy Gill -- or at least the first that I was able to find, and it was actually still part of the base
library at that time -- was checked into Git in June, 2001. It introduced the MonadReader
class and the newtype wrapped Reader
:
newtype Reader r a = Reader { runReader :: r -> a }
together with its Functor
, Monad
, MonadFix
, and MonadReader
instances. (No Applicative
-- that hadn't been invented yet.) It also included a set of instances for (->) r
with the comment:
The partially applied function type is a simple reader monad
So, I think the original formulation in the lecture notes led Andy to include these instances for (->) r
, even though he also introduced a dedicated Reader
newtype for consistency with the other monads in the transformers
library.
Anyway, that's the history. As for use cases, I can only think of one serious one, though perhaps it isn't that compelling. The lens
library is designed to interface well with MonadState
and MonadReader
to access complex states/contexts. Because functions like:
view :: MonadReader s m => Getting a s a -> m a
preview :: MonadReader s m => Getting (First a) s a -> m (Maybe a)
review :: MonadReader b m => AReview t b -> m t
are defined in terms of the MonadReader
instance, they can be used both in a traditional Reader
context:
do ...
outdir <- view (config.directories.output)
...
and in a plain function context:
map (view (links.parent.left)) treeStructure
Again, not necessarily a compelling use case, but it's a use case.
Upvotes: 1
Reputation: 531165
The point is to make it easier to combine functions that all take the same environment.
Consider the type a -> Env -> b
, where Env
is some data type that contains all your "global" variables. Let's say you wanted to compose two such functions. You can't just write h = f2 . f1
, because f1
's return type Env -> b
doesn't match f2
's argument type b
.
f1 :: a -> Env -> b -- a -> ((->) Env b)
f2 :: b -> Env -> c -- b -> ((->) Env c)
h :: a -> Env -> c
h x e = let v = f1 x e
in f2 v e
Because there is an applicable MonadReader
instance for the monad (->) Env
, you can write this as
-- The class, ignoring default method implementations, is
-- class Monad m => MonadReader r m | m -> r where
-- ask :: m r
-- local :: (r -> r) -> m a -> m a
-- reader :: (r -> a) -> m a
--
-- The functional dependency means that if you try to use (->) Env
-- as the monad, Env is forced to be the type bound to r.
--
-- instance MonadReader r ((->) r) where
-- ask = id
-- local f m = m . f
-- reader = id
h :: MonadReader Env m => a -> m c
h x = do
v <- f1 x
f2 v
-- h x = f1 x >>= f2
without explicit reference to the environment, which h
doesn't
care about; only f1
and f2
do.
More simply, you can use the Kleisli composition operator to define the same function.
import Control.Monad
h :: MonadReader Env m => a -> m c
h = f1 >=> f2
In your example, ask
is simply how you get access to the environment from inside the body of the function, rather than having it as a preexisting argument to the function. Without the MonadReader
instance, you would write something like
func :: Show a => Bool -> Int -> a -- m ~ (->) Int
func b i = case b of
True -> show i
False -> error "nope"
The definition of main
stays the same. However, (->) Int
isn't the only type that has a MonadReader
instance; there could be a more complicated monad stack
that you are using elsewhere, which the more general type (Show a, MonadReader Int m) => Bool -> m a
allows you to use instead of "just" (->) Int
.
Upvotes: 2