Skip to content

Instantly share code, notes, and snippets.

@StevenXL
Created May 20, 2018 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StevenXL/3d64c6cd72a54b9db4344f3ffb62f6d6 to your computer and use it in GitHub Desktop.
Save StevenXL/3d64c6cd72a54b9db4344f3ffb62f6d6 to your computer and use it in GitHub Desktop.
Monad Transformers

Monads allow us to build computations that have effects. However, in "real code", we often need to be able to use several effects at once. This is what monad transformers allow us to do.

A monad transformer modifies the behavior of the underlying monad. It modifies the behavior in that it adds teh effect of the base monad to the inner monad.

Some examples:

  1. The StateT monad transformer adds mutable state to the underlying monad.
  2. The MaybeT monad transformer adds failure to the underlying monad.
  3. The EitherT / ExceptT monad transformer adds error messages to the underlying monad.

Monad transformers allow us to build a monadic stack. In the example below, WriterT is a monad transformer and MonadicStack is our monadic stack. Furthermore, we say that MonadicStack is a stack with WriterT stacked on top of IO.

newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }

type MonadicStack = WriterT [String] IO

Finally, another very important aspect of monad transformers is that partial application of the monad transformer type constructor is itself monad. This property means that monadic stacks can be used as an underlying monad in a new stack:

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

type StackOnStack = MaybeT MonadicStack

Our StackOnStack type synonym is a monadic stack where the underlying monad is another monadic stack. The StackOnStack type synonym is equivalent to:

-- StackOnStack ~ MaybeT (WriterT [String] IO)

StackOnStack is a monad that has the effects of both a Writer monad and a Maybe monad and an IO monad; we get logging, failure, and IO effects.

At the value leve, we know that when we "run" the StackOnStack monad, we are going to get an IO (Maybe a, [String]) - an IO action that returns a tuple, an that tuple contains an a (maybe) and a list of strings. How do we know this? Through the substitution model:

First, we start of with type StackOnStack = MaybeT MonadicStack.

Second, we substitute MaybeT for its definition:

MaybeT MonadicStack = MaybeT { runMaybeT :: MonadicStack (Maybe a)}

Third, we substitue MonadicStack for its definition:

MaybeT { runMaybeT :: MonadicStack (Maybe a ) } = MaybeT { runMaybeT :: WriterT [String] IO (Maybe a) }

Next, we substitue WriterT for its definition:

MaybeT { runMaybeT :: WriterT [String] IO (Maybe a) } = MaybeT { runMaybeT :: IO (Maybe a, [String]) }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment