Reader monad and SKI combinators
TweetIn this post, I am going to show you the relationship between the reader monad and the SKI combinators.
Reader monad
The reader monad encapsulates computations which read values from a shared environment. Here’s the definition of our beloved Reader monad.
newtype Reader r a = Reader {
  runReader :: r -> a
}
instance Monad (Reader r ) where
  return a = Reader $ \_ -> a
  m >>= k = Reader $ \r -> runReader (k (runReader m r )) rThere are actually two instances of the reader monad in Haskell. The function monad (->) r is also a reader monad. The definition of the function monad is just like the Reader monad, but without newtype wrapping and unwrapping.
instance Monad ((->) r ) where
  return a = \_ -> a
  m >>= k = \r -> k (m r ) rFrom now on, I will use the function monad definition because it makes our presentation more clear. But remember these two monad definitions are isomorphic.
SKI combinators
Combinatory logic is a formal system without the need for variables. It was introduced by Moses Schönfinkel and Haskell Curry.
The SKI calculus is a combinatory logic with three combinators S, K and I. It is a universal system as all operations in lambda calculus can be encoded via abstraction elimination into the SKI calculus.
The Haskell definition of s, k and i are as follows:
k :: a -> b -> a
k x y = x
s :: (a -> b -> c) -> (a -> b) -> a -> c
s x y z = (x z) (y z)
i :: a -> a
i x = xFrom the types of these combinators, we can see that k is Haskell’s const function and i is Haskell’s id function.
Technically speaking, we don’t need the I combinator to encode lambda calculus into the SKI combinators because we can encode I in terms of S and K. I is just for convenience.
Relationship
So what’s the relationship between the reader monad and the SKI combinators? Let’s look at the types of the reader monad more closely.
return :: a -> r -> a
(>>=) :: (r -> a) -> (a -> r -> b) -> r -> bCan you see the similarity to the types of the combinator k and s? The function return is the same as k, but the function (>>=) is somewhat similar but not exactly the same as s.
Let’s see the applicative functor definition of the reader monad instead.
pure :: a -> r -> a
(<*>) :: (r -> a -> b) -> (r -> a) -> r -> bNow we can see that the type of (<*>) is the same to the type of s. Because these functions are fully polymorphic, we don’t need to check the definition of these functions to see if they are really equivalent.
Conor McBride and Ross Paterson’s paper also mentions this relationship briefly.
Conclusion
We’ve just discovered that k and s combinators of SKI calculus correspond to pure and (<*>) functions of the reader monad. Because we can encode any lambda calculus term into SKI calculus, we can encode any lambda calculus term into the reader monad too! Amazing, isn’t it?