Haskell exercise: Monads
Department of Mathematics and Computer Science
University of Southern Denmark
October 25, 2017
1. The Maybe-monad, and a monad for Expr
You are given a data type for expressions:
data Expr a = Var a | Add (Expr a) (Expr a) deriving Show
1. To turn Expr into a monad, we should give valid definitions for return
and bind:
return :: a → Expr a
(>>=) :: Expr a → (a → Expr b) → Expr b
Fill out the function bodies with appropriate definitions:
instance Monad Expr where
return x =⊥
(Var a) >>= f = ⊥
(Add x y) >>= f = ⊥
2. We would like to define a function
replace :: Eq a ⇒ [(a, b)] → Expr a → Expr (Maybe b)
which replaces occurences of type a with something of type Maybe b.
Example:
• replace [ ] (Var ’a’) = Var Nothing
• replace [(’a’, 3)] (Var ’a’) = Var (Just 3)
1
• replace [(’a’, 3)] (Add (Var ’a’) (Var ’b’)) = Add (Var (Just 3)) (Var Nothing)
You should use the functionality of the Expr -monad to implement
replace. You can use
lookup :: Eq a ⇒ a → [(a, b)] → Maybe b
from the Prelude in your implementation of replace.
3. Now we would like to make a function
convert :: Expr (Maybe a) → Maybe (Expr a)
which returns Nothing if there is an occurrence of Nothing inside the
input expression e, otherwise it returns Just e 0 , where e 0 is a new
expression where the internal values of type a are not wrapped in Just.
You should use the functionality of the Maybe monad to implement
your convert function.
2. Random numbers and the State Monad
Functions involving randomness in Haskell take a seed g :: StdGen as input,
and returns an output and a new seed g 0 :: StdGen.
As an example, the built-in function
randomR :: (Int, Int) → StdGen → (Int, StdGen)
on inputs randomR (a, b) g, returns (x , g 0 ) (an integer x and a new seed g 0 ),
where x is chosen uniformly, with the condition a 6 x 6 b.
To get a random seed, one needs the IO environment.
A complete program to simulate two die rolls and return the sum of the
die-values is given below. Make sure that you understand how this works,
before going on to the rest of the exercise.
import System.Random
die6 :: StdGen → (Int, StdGen)
die6 g = randomR (1, 6) g
twoDie :: StdGen → (Int, StdGen)
twoDie g = let (d1 , g 0 ) = die6 g
(d2 , g 00 ) = die6 g 0
in (d1 + d2 , g 00 )
test :: IO (Int, StdGen)
2
test = do g ← newStdGen
return (twoDie g)
We would like to give a nicer definition for twoDie which does not explic-
itly handle the random seed.
The State-monad libary provides the following functions to wrap and
unwrap functions in the state monad.
state :: (s → (a, s)) → State s a
runState :: State s a → s → (a, s)
You are provided the following stub of a program:
import Control .Monad .State
die6 0 :: State StdGen Int
die6 0 = ⊥
twoDie 0 :: State StdGen Int
twoDie 0 = ⊥
test 0 :: IO (Int, StdGen)
test 0 = do g ← newStdGen
return (runState twoDie 0 g)
1. Using the function state and your definition die6 , provide a definition
of die6 0 .
2. Implement twoDie 0 only referring to die6 0 and the monadic functional-
ity of State Monad.