Mixing Monads

Monads Can Be Used for Many Things!

  • Partial Functions
  • Global Variables
  • Parsing
  • Exceptions
  • Test Generation
  • Concurrency















Exception Handling

Recall our expressions with division

We had a potentially crashing evaluator















We defined a Result type

made it a Monad

and then we can write

which doesn’t crash but returns an Err

and when it succeeds it returns an Ok















Generalizing Result to Either

The standard library generalizes the Result type to Either

  • Err s becomes Left s
  • Ok v becomes Right v
  • Result a becomes Either String a

(But we can data other than String in the Left values)









EXERCISE: Generalizing Result Monad to Either Monad

Lets translate the old Monad instance for Result

into a Monad instance for Either










QUIZ

We can rewrite eval to return an Either

What does quiz evaluate to?

A. Err "DBZ: Plus (Number 5) (Number (-5))"

B. Left "DBZ: Plus (Number 5) (Number (-5))"

C. Run-time Exception

D. Plus (Number 5) (Number (-5))

E. Left (Plus (Number 5) (Number (-5)))
















Either is an Exception Monad!

What can you do with exceptions?

  1. throwError an exception (with some value) …

  2. catchError an exception (and use its value) …
















1. throwing an Exception

We can simply define

and now voila

Exactly the same evaluator

  • Result is a Left ==> an exception came all the way to the top.

  • Either monad ensures the “exception” shoots to the top!

No further evaluation happens after a throw because ???















catching an exception

How to catch an exception?

Lets change our Expr type to

Informally, try e n evaluates to e but

  • if e is undefined due to divide-by-zero

  • then evaluate to n

QUIZ

What should the type of catch be?

A. Either e a -> (a -> Either e b) -> Either e b

B. Either e a -> (e -> Either e b) -> Either e b

C. Either e a -> (e -> Either e a) -> Either e a

D. Either e a -> Either e a -> Either e a

E. Either e a -> Either e b -> Either e b














Implementing catch

Lets implement the catch function!










QUIZ

What does quiz evaluate to?

A. Right 7

B. Left 7

C. Right 0

D. Left 0

E. Left (Plus (Number 5) (Number (-5)))












Either is an Exception Monad!

  1. throw an exception (with some value) …

  2. catch an exception (and use its value) …














Monads Can Be Used for Many Things!

  • Partial Functions
  • Global State
  • Parsing
  • Exceptions
  • Test Generation
  • Concurrency

… but what if I want Exceptions and Global State ?










Mixing Monads

What if I want Exceptions and Global State ?























Profiling with the ST Monad

Lets implement a profiling monad that counts the number of operations

We can write a runProfile that

  • executes the transformer from 0
  • and renders the result

A function to increment the counter




























A Profiling Evaluator

We can use count to write a profiling evaluator

And now, as there are two operations, we get



















But what about Divide-by-Zero?

Bad things happen…

Problem: How to get global state AND exception handling ?
















Mixing Monads with Transformers

Start with a Basic Monad

m implements

  • no special operations

Transform it to add some Capabilities

Transform1 m implements

  • m operations and
  • operations added by Transform1

Transform again to add more Capabilities

Transform2 (Transform1 m) implements

  • m operations and
  • operations added by Transform1 and
  • operations added by Transform2

… And so on

Transform3 (Transform2 (Transform1 m)) implements

  • m operations and
  • operations added by Transform1 and
  • operations added by Transform2 and
  • operations added by Transform3

Reminiscent of the Decorator Design Pattern or Python’s Decorators.














Mixing Monads with Transformers

  • Step 1: Specifying Monads with Extra Features

  • Step 2: Implementing Monads with Extra Features


















Specifying Monads with Extra Features

First, instead of using concrete monads

  • e.g. Profile or Either

We will use type-classes to abstractly specify a monad’s capabilities

  • e.g. MonadState s m or MonadError e m












A Class for State-Transformers Monads

The class MonadState s m defined in the Control.Monad.State says

  • m is a State-Transformer monad with state type s

That is to say, m implements

  • >>= and return operations specified by Monad and

  • get and put operations specified by MonadState!

Generalize Types to use Classes

So we can generalize the type of count to use MonadState Int m













A Class for Exception Handling Monads

The class MonadError e m defined in [Control.Monad.Except][6] says

  • m is a Exception-Handling monad with exception type e

That is to say, m implements

  • >>= and return operations specified by Monad and

  • throwError and catchError operations specified by MonadError!

Generalize Types to use Classes

So we can generalize the type of tryCatch to use MonadError e m

















Generalize eval to use Constraints

We can now specify that eval uses a monad m that implements

  • MonadState Int and MonadError Expr

Lets try to run it!



















Mixing Monads with Transformers

  • Step 1: Specifying Monads with Extra Features

  • Step 2: Implementing Monads with Extra Features

















Implementing Monads with Extra Features

Transform2 (Transform1 m) implements

  • m operations and
  • operations added by Transform1 and
  • operations added by Transform2

We require

  • A basic monad m
  • A Transform1 that adds State capabilities
  • A Transform2 that adds Exception capabilities














A Basic Monad

First, lets make a basic monad

  • only implements >>= and return

A very basic monad: just a wrapper (Id) around the value (a)

  • No extra features










A Transform that adds State Capabilities

The transformer StateT s m defined in the Control.Monad.State module - takes as input monad m and

  • transforms it into a new monad m'

such that m' implements

  • all the operations that m implements

  • and adds State-transformer capabilities

StateT s m satisfies the constraint (MonadState s (StateT s m))

A State-transformer over Int states

We can go back and give evalProf the type











A Transform that adds Exception Capabilities

The transformer ExceptT e m

  • takes as input a monad m and
  • transforms it into a new monad m'

such that m' implements

  • all the operations that m implements

  • and adds Exception-handling capabilities

ExceptT e m satisfies the constraint (MonadError e (ExceptT e m))

An Exception Handler Monad with Expr-typed exceptions

We can go back and give evalThrowCatch the type










Composing Transformers

We can use both transformers to get both powers!

ExnProf implements State-transformer-over Int and Exception-handling-over-Expr














EXERCISE: Executing the Combined Transformer

Recall that

Lets write a function

such that














TRY AT HOME: Combining in a Different Order

We can also combine the transformers in a different order

ExnProf implements State-transformer-over Int and Exception-handling-over-Expr

Can you implement the function

such that when you are done, we can get the following behavior?










Summary: Mixing Monads with Many Features

1. Transformers add capabilities to Monads

Transform2 (Transform1 m) implements

  • m operations and
  • operations added by Transform1 and
  • operations added by Transform2

2. StateT and ExceptT add State and Exceptions

  • Start with a basic monad Identity
  • Use StateT Int to add global-Int state-update capabilities
  • Use ExceptT Expr to add exception-handling capabilities

Play around with this in your homework assignment!