Call-by-value 2026-10-31
Evaluate argument expressions before invoking a function. A monadic interpreter must also specify the order of those evaluations when its computational monad represents effects.
Continuation monad 2026-10-31
A computational monad for continuation-passing style. Result invokes the supplied continuation immediately; sequencing replaces it with a continuation that runs the next computation.
Continuation monad transformer 2026-10-31
A continuation monad whose final answer is computed in a base computational monad. The answer type is fixed throughout a computation.
Exception monad transformer 2026-10-31
An unlabelled exception can be represented by an algebraic data type with successful and failed alternatives, sequenced inside a base computational monad. Sequencing skips the next computation after failure; a handler runs an alternative computation. The base effects already performed need not be undone.
Identity monad 2026-10-31
The computational monad whose result is the identity function and whose sequencing is ordinary application. It adds no effects and provides a starting point for a stack of monad transformers.
Monadic interpreter 2026-10-31
An interpreter whose evaluator returns computations in a computational monad. Its semantic equations specify effect order by sequencing, while monad laws support proofs of program equivalence independently of the particular effects.
Monad laws 2026-10-31
The left and right identity laws and the associativity law for a computational monad. They justify regrouping sequencing while preserving the order and multiplicity of effects.
Monad transformer 2026-10-31
A construction taking a computational monad to another computational monad , normally with a lift from to . The order of monad transformers can affect which information survives an exception or a control transfer.
Solutions to all three questions of the paper dated 11 June 2015.[ref]
Principles of Programming Languages examination paper by the University of Oxford (2015). Cover and pp. 2–8, A12079W1
In the equations below, and denote the base computational monad's result and sequencing operations. All equalities of computations are extensional: apply both sides to the same remaining arguments. Haskell fragments use result and bind for the paper's and . The FunMem programs use the language's own syntax.Thread the explicit program state through the computation, using the base computational monad to sequence its effects:In Haskell notation, with
resultM and bindM standing for the base operations:result :: Monad m => a -> MemT m a
result x s = resultM (x,s)
bind :: Monad m => MemT m a -> (a -> MemT m b) -> MemT m b
bind p f s = bindM (p s) (\(x,s') -> f x s')This is the state monad transformer. The first computation's final program state, rather than its initial program state, becomes the second computation's input. The base computational monad decides whether that pair is available at all.
Use the three monad laws of the base computational monad. For left identity, at an arbitrary program state ,The last step is the base left identity law. For right identity,The penultimate equality identifies the pair-destructuring function with ; the last uses the base right identity law.
For associativity, expand the two successive uses of sequencing and apply base associativity:No program state is duplicated or reset during these rearrangements: both sides pass to , its output to , and the output of to . By function extensionality,
There is one undifferentiated exception, so use the two alternatives of the Haskell algebraic data type
Maybe: Just x succeeds and Nothing fails. A suitable exception monad transformer is:type ExT m a = m (Maybe a)
result :: Monad m => a -> ExT m a
result x = resultM (Just x)
bind :: Monad m => ExT m a -> (a -> ExT m b) -> ExT m b
bind p f = bindM p (\r -> case r of
Nothing -> resultM Nothing
Just x -> f x)
orelse :: Monad m => ExT m a -> ExT m a -> ExT m a
orelse p q = bindM p (\r -> case r of
Nothing -> q
Just x -> resultM (Just x))
failure :: Monad m => ExT m a
failure = resultM NothingThe distinction between
bindM and bind matters. The handler must inspect the failed alternative using the base computational monad; the transformed bind would propagate failure without running that inspection. The alternative computation q runs only after p returns Nothing; any effects already performed by the base computational monad remain subject to that base's semantics.Left identity follows by selecting
Just x. Right identity follows because either alternative is passed back to resultM. For associativity, base associativity exposes the first alternative: Nothing propagates through both remaining steps; Just x runs f x, with its Nothing and Just alternatives treated identically on both sides. Thus this representation also satisfies the monad laws.Expand the two type synonyms to see what survives an exception:In the first order, a failed computation can still produce
(Nothing,s'). The exception handler therefore runs with , including writes made before failure. In the second order, failure produces Nothing, with no returned computer memory. A handler defined by applying the underlying orelse pointwise runs its alternative from the original input computer memory . This gives rollback of this explicit program state for the failed branch.For example, start with a memory location holding , write , fail, and handle the failure by reading that memory location. The first order returns ; the second returns . This rollback concerns the explicit computer memory only: arbitrary effects in the base computational monad are not thereby reversed.A conventional programming language normally retains writes made before an exception is caught. Its mutable computer memory and exception handling therefore correspond more closely to .
Fix an answer type and let the continuation return its answer inside the base computational monad:
type ContT m a = (a -> m Answer) -> m Answer
result :: Monad m => a -> ContT m a
result x k = k x
bind :: Monad m => ContT m a -> (a -> ContT m b) -> ContT m b
bind p f k = p (\x -> f x k)The continuation monad transformer changes the continuation supplied to
p: it first runs f on the produced value, then uses the original final continuation k. Base sequencing is unnecessary in these two definitions because p already accepts a continuation returning m Answer. The customary lift, if desired, is lift q k = bindM q k.Use programming closures that receive evaluated arguments and return a computation in the chosen computational monad. A sufficient core of the semantic value domain is:
data Value = IntVal Integer
| BoolVal Bool
| Function ([Value] -> M Value)Additional constructors may represent the other built-in values. Assume a semantic environment abstract data type with operations
lookupEnv :: Env -> Ident -> Value and extend :: Env -> [(Ident,Value)] -> Env. The latter simultaneously adds the listed bindings, shadowing old bindings. Parameter names in one binding list are distinct, and calls have the expected arity.Define programming closure construction separately; it does not run the body:
close :: Expr -> Env -> Value
close (Lambda xs body) rho =
Function (\args -> eval body (extend rho (zip xs args)))
apply :: Value -> [Value] -> M Value
apply (Function f) args = f argsThe four expression forms have the following semantic equations. Function position is evaluated first, followed by argument expressions from left to right:
eval :: Expr -> Env -> M Value
eval (Variable x) rho = result (lookupEnv rho x)
eval (Apply e es) rho =
bind (eval e rho) (\f ->
bind (evalArgs es rho) (\args -> apply f args))
eval e@(Lambda xs body) rho = result (close e rho)
eval (Let d body) rho =
bind (defn d rho) (\rho' -> eval body rho')
evalArgs :: [Expr] -> Env -> M [Value]
evalArgs [] rho = result []
evalArgs (e:es) rho =
bind (eval e rho) (\v ->
bind (evalArgs es rho) (\vs -> result (v:vs)))The two definition forms are:
defn :: Defn -> Env -> M Env
defn (Val x e) rho =
bind (eval e rho) (\v -> result (extend rho [(x,v)]))
defn (Rec x e@(Lambda xs body)) rho = result rho'
where
rho' = extend rho [(x,v)]
v = close e rho'The ordinary definition evaluates its right-hand side in the old semantic environment; its own name is not yet bound there. The recursive definition creates a recursive closure in the extended semantic environment containing itself. Because the right-hand side is a lambda abstraction, constructing that programming closure does not evaluate its body. Haskell's lazy evaluation can tie this recursive data structure without requiring any extra fixed-point operation from the arbitrary computational monad.
Lexical scope is explicit: calling a programming closure extends the defining semantic environment, and its parameters shadow bindings in that semantic environment. The six requested constructs are therefore covered by
The equivalence holds for every lawful computational monad with the semantic equations above. In particular, effects in are neither discarded nor evaluated twice. Fix an initial semantic environment , and writeExpanding
Let and Val givesThe two steps use associativity and left identity from the monad laws.For the application, evaluating the lambda abstraction yields
result c, where the programming closure satisfies . The singleton argument evaluator reduces toUse left identity to remove the initial result c, then associativity and left identity again:Consequently,The argument relies on the lambda abstraction itself being effect-free, as specified in part (a), and on using the same semantic environment for on each side. No commutativity of effects is assumed. In particular, the proof applies to state monads, exceptions and continuation monads, not merely to the identity monad.Add
Fixes [Ident] [Expr] to the abstract syntax tree. For the stipulated nonempty, equally long lists of distinct names and lambda abstractions, define all programming closures against one common extended semantic environment:eval (Fixes xs es) rho = result (head vs)
where
rho' = extend rho (zip xs vs)
vs = map (\e -> close e rho') esExpanding the auxiliary function, if ,Every body can therefore call every member of the family, including itself. Constructing the family performs no body computations in the arbitrary computational monad. As in part (c), host lazy evaluation ties the recursive semantic environment; a monadic fixed-point operator is unnecessary.
For example, with two bodies implementing evenness and oddness, respectively, each programming closure sees both names in . Returning the first programming closure still retains access to the second. Building each programming closure in an independently extended old semantic environment would lose this mutual recursion. For , the definition agrees with
Fix. State monad transformer 2026-10-31
A state monad layered over a base computational monad. An unsuccessful base computation may return no final program state; this matters when combining it with an exception monad transformer.