## 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))