To define a monad for (->) r
, we need two operations, return
and (>>=)
, subject to three laws:
instance Monad ((->) r) where
If we look at the signature of return for (->) r
return :: a -> r -> a
we can see its just the constant function, which ignores its second argument.
return a r = a
Or alternately,
return = const
To build (>>=)
, if we specialize its type signature with the monad (->) r
,
(>>=) :: (r -> a) -> (a -> r -> b) -> r -> b
there is really only one possible definition.
(>>=) x y z = y (x z) z
Using this monad is like passing along an extra argument r
to every function. You might use this for configuration, or to pass options way down deep into the bowels of your program.
We can check that it is a monad, by verifying the three monad laws:
1. return a >>= f = f a
return a >>= f
= (\b -> a) >>= f -- by definition of return
= (\x y z -> y (x z) z) (\b -> a) f -- by definition of (>>=)
= (\y z -> y ((\b -> a) z) z) f -- beta reduction
= (\z -> f ((\b -> a) z) z) -- beta reduction
= (\z -> f a z) -- beta reduction
= f a -- eta reduction
2. m >>= return = m
m >>= return
= (\x y z -> y (x z) z) m return -- definition of (>>=)
= (\y z -> y (m z) z) return -- beta reduction
= (\z -> return (m z) z) -- beta reduction
= (\z -> const (m z) z) -- definition of return
= (\z -> m z) -- definition of const
= m -- eta reduction
The final monad law:
3. (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
follows by similar, easy equational reasoning.
We can define a number of other classes for ((->) r) as well, such as Functor,
instance Functor ((->) r) where
and if we look at the signature of
-- fmap :: (a -> b) -> (r -> a) -> r -> b
we can see that its just composition!
fmap = (.)
Similarly we can make an instance of Applicative
instance Applicative ((->) r) where
-- pure :: a -> r -> a
pure = const
-- (<*>) :: (r -> a -> b) -> (r -> a) -> r -> b
(<*>) g f r = g r (f r)
What is nice about having these instances is they let you employ all of the Monad and Applicative combinators when manipulating functions.
There are plenty of instances of classes involving (->), for instance, you could hand-write the instance of Monoid for (b -> a), given a Monoid on a
as:
enter code here
instance Monoid a => Monoid (b -> a) where
mempty _ = mempty
mappend f g b = f b `mappend` g b
but given the Monad/Applicative instance, you can also define this instance with
instance Monoid a => Monoid (r -> a) where
mempty = pure mempty
mappend = liftA2 mappend
using the Applicative instance for (->) r
or with
instance Monoid a => Monoid (r -> a) where
mempty = return mempty
mappend = liftM2 mappend
using the Monad instance for (->) r
.
Here the savings are minimal, but, for instance the @pl tool for generating point-free code, which is provided by lambdabot on the #haskell IRC channel abuses these instances quite a bit.
type F a b = (->) a b
and thisf :: (->) a b
. – R. Martinho Fernandes(->) a b
asa->b
, right? So, (->) is an operator in types. – makelcreturn :: a -> (a -> r)
! – yatima2975