Down the Rabbit-Hole with Monad Transformers

My experience with Haskell has been all about rabbit-holes. This one was not quite so scary as what Alice experienced. There was no watch or waist coat pocket. The tunnel didn’t seem like a very deep well and I always knew where to find bottom. That’s a good sign. It was not always that way.

I had taken an interest in type families and was working my way through Fun with Type Functions. I saw an expression and wondered,  What can I actually do with that? It was at that point that I decided to go down the rabbit-hole with monad transformers.

I haven’t yet returned to that expression, but I picked up something on the way down and brought it back.

{-# LANGUAGE NoMonomorphismRestriction, DatatypeContexts, FlexibleContexts, FlexibleInstances #-}
-- http://en.wikibooks.org/wiki/Haskell/Monad_transformers
-- http://www.cs.nott.ac.uk/~nhn/MGS2006/LectureNotes/lecture03-9up.pdf

module Main where

import Control.Monad
import Control.Monad.Trans.Class

-- |A parameterized type I representing an "inner type" with one constructor IC
data I a = IC a deriving (Show)

-- |Unwraps the value in the type I
unI (IC x) = x

-- |A parameterized new type O m a representing an "outer type" with a named constructor
newtype (Monad m) => O m a = OC { runO :: m (I a)}

-- uses runO to unwrap x into an m (I a) computation
-- v <- runO x extracts an I a value v
-- unI v extracts the value in a
-- f (unI v) applies the function to the (I a) value
-- runO (f (unI v)) executes f value in O m a, since f has type O m a as a result type
-- do block has type m (I a) it's wrapped in O m a constructor

-- |A monad transformer instance on type O m a
instance Monad m => Monad (O m) where
 return = OC . return . IC
 x >>= f = OC $ do { v <- runO x; runO (f (unI v))} -- the type of x is (O (I a))   

-- an alternative bind closely resembling the references above
-- x >>= f = OC $ do { v <- runO x; case v of IC value -> runO $ f value}

-- uses runO to unwrap x into an m (I a) computation
-- extracts an I a value v
-- case v of IC value allows pattern match on a, the value
-- apply f to a, the value, aka ... applies f to the I a value ...
-- since f has type O m a as a result type, runO executes f value in O m a
-- do block has type m (I a) it's wrapped in O m a constructor

-- a similar example that
-- uses new type on the inner type
-- unwraps O m a

-- |A parameterized new type I' representing an "inner type" with one constructor IC'
newtype I' a = IC' a -- deriving (Show)

-- |Unwraps the value in the inner type
unI' (IC' x) = x

-- |A monad instance on the inner type
instance Monad I' where
 return = IC'
 m >>= f = f (unI' m)

-- | A show instance of I' a
instance Show a => Show (I' a) where
 show (IC' x) = "IC' " ++ show x

-- |Witness on inner type
w :: Num a => I' a
w = (IC' 0) >>= return . id

-- |A parameterized new type O' m a representing an "outer type" with a named constructor
newtype O' m a = OC' {runO' :: m (I' a)} -- deriving (Show)

-- | A Show instance of O' I' a
instance Show a => Show (O' I' a) where
 show (OC' x) = "OC' " ++ show (unI' x)

-- |Unwraps the value in the outer type
unO' (OC' (IC' a)) = a

-- uses runO' to unwrap x into an m (I' a) computation
-- v <- runO' x extracts an I' a value v
-- unI' v extracts the value in a
-- f (unI' v) applies the function to the (I' a) value
-- runO' (f (unI' v)) executes f value in O' m a, since f has type O' m a as a result type
-- do block has type m (I' a) it's wrapped in O' m a constructor

-- |A monad transformer instance on type O' m a
instance Monad m => Monad (O' m) where
 return = OC' . return . IC'
 m >>= f = OC' $ do {v <- runO' m; runO' (f (unI' v))}

-- | Witness on monad transformer
w' :: Num (I' a) => O' I' a
w' = (OC' (IC' 0)) >>= return . id

-- | An Num instance lifted into I'
instance Num a => Num (I' a) where
 fromInteger x = IC' (fromInteger x)

-- lifting, liftM executes a function in a monad, lift executes the monad

-- | Executes addition on the zero in IC'
w'' :: Num a => I' a
w'' = liftM (+1) (IC' 0)

-- |Instance of monad transformer lifts m into O' m a
instance MonadTrans O' where
 lift m = OC' (m >>= return . IC')

-- |Executes addition on the zero in IC' and lifts the monad into O' m a
w''' :: (Num a) => O' I' a
w''' = lift ((IC' 0) >>= return . (+1))