The ((->) r) monad

To seasoned Haskell programmers, I expect the ((->) r) instance is obvious, but it took me a while to get a feel for it. In particular we’re talking about functions fromr, the type parameter of the monad, to some other type.

In the definition of x >>= f I often think of x as akin to a value, and f to a function. Let’s see what types they have in this instance:

x :: m a
f :: a -> m b
x :: r -> a
f :: a -> r -> b

So both x and f are functions, with an argument of type r which permeates everything.

As is often the case, the Kleisli arrow elucidates matters:

(f >=> g) = \x -> f x >>= g
= \x -> (\r -> g (f x r) r)

or,

(f >=> g) x r = g (f x r) r

Or in words: every time you evaluate a function, pass an extra r argument. Within a set of functions the value of this argument stays the same, so it provides a constant evaluation environment.

One could use this as a more civilized version of global variables.

If we now revisit >>= it makes more sense:

(x >>= f) r = f (x r) r

To evaluate the right-hand side, first evaluate x in the context of r to give x', then evaluate f of x' again in the context of r.

Let’s turn to return. We know this should be the most direct translation of a non-monadic value into the monad: here that translates to being a value which doesn’t depend on the environment.

return x e = x

This is a standard function though: it’s just const.

If we wanted to be rigorous, we would show that these definitions for >>= and return do indeed form a monad i.e. that they satisfy the monad laws.

Here the character value gets threaded through the functions, whilst the same integer environment is seen throughout. I say seen, but actually it’s all invisible: the monad implicitly supplies the context variable, so we don’t need to worry about it.

Alternatively, in do-notation, given:

munge c = do
x <- incN c
y <- inc2N x
z <- decN y
return z

We can say:

*Main> munge ’a’ 2
’e’

An arithmetic amusement

It’s quite nice to ponder this:

join (+) 2

From the discussion above:

join (+) 2 = (+) 2 2 = 2 + 2 = 4

Perhaps more interestingly if we join - we can calculate the additive identity.

join (-) x = (-) x x = 0

Here x doesn’t matter so in a loose sense (ignoring types)

join (-) ≈ const 0

The type caveat is just that

Prelude Control.Monad> :t join (-)
join (-) :: Num a => a -> a

but

Prelude Control.Monad> :t const 0
const 0 :: Num a => b -> a

In other words the true const takes anything to an arbitrary zero, but the join version takes any number to a zero of the same type. Surely this is still useful for some sort of obfuscation though ?

Perhaps the multiplicative identity is more useful:

join div 42 = 1

The Applicative ((->) r)

Again we can see the pattern that inside the applicative we first evaluate things in a fixed context, and then operate with them as normal. However, here it makes better to sense to think of everything as a function which takes a single parameter: the environment.

As usual if we make everything pure it just works as normal:

*Main> (pure (,) <*> pure ’a’ <*> pure ’b’) 2
(’a’,’b’)

Or more idiomatically with <$>:

*Main> ((,) <$> pure ’a’ <*> pure ’b’) 2
(’a’,’b’)

Now it’s easy to let any of the terms access the context, and we can use the incN function from above: