Cont

Cont

runCont

The Cont monad in the popular Haskell monad transformer library does not satisfy the requirements. The captured continuation has the type of a monadic function. The Cont documentation (through the current version 2.0.1.0) confirms that view, saying ``Computations are built up from sequences of nested continuations, terminated by a final continuation (often id) which produces the final result. Since continuations are functions which represent the future of a computation...'' We have already argued that undelimited continuations are not functions and that one should forget about the ``final result.'' It behoves for a typed language to statically enforce the forgetting. Incidentally, in the presence of first-class undelimited continuations, computations can no longer be viewed as ``sequences of nested continuations.'' As Peter Landin said, ``You can enter a room once, and yet leave it twice.'' Our code will show a simple example.

Recall that monad is a technique of embedding an impure, object language, into a pure functional (meta)language. For an object language with control effects, the well-known embedding technique is continuation-passing style (CPS) -- which is what all our monads implement. In CPS, an object language expression of the base object type a is represented as a Haskell value of the type (a -> w) -> w where w is a so-called `answer-type', to be discussed shortly. An object-language a->b function is represented as Haskell's a -> ((b->w) -> w) function. An object-language co-value of the type a , that is, the continuation consuming a value is represented in Haskell as the value of the type a->w . We stress that although the representation of an object language continuation is a Haskell function, it differs, in type, from the representation of any object language function. CPS operations for building primitive computations, extending them, capturing an undelimited continuation and invoking it have the following form:

return x = \k -> k x m >>= f = \k -> m (\v -> f v k) callCC f = \k -> f k k throw k x = \_ -> k x

All our monads share the above code, modulo newtype -induced conversions. The monads differ in their treatment of the answer-type w . An object-language computation cannot know what w is; to it, w is arbitrary. This fact gives us the first implementation, with the following types for monadic values and continuations:

newtype CPS1 a = CPS1{unCPS1:: forall w. (a -> w) -> w} type K1 a = forall w. a -> w

callCC

callCC1 :: (K1 a -> CPS1 a) -> CPS1 a callCC1 f = CPS1 $ \k -> unCPS1 (f k) k

callCC

w

CPS1

w

K1

CPS1

K1

callCC

callCC1

K1

CPS1

callCC1

A different way to introduce a type whose values cannot be used is to define an algebraic data type with no constructors -- an empty type (disregarding bottom). Monadic values and continuations get the following types:

data Falsum -- no constructors newtype CPS2 a = CPS2{unCPS2:: (a -> Falsum) -> Falsum} type K2 a = a -> Falsum

Falsum

CPS2 a

a -> Falsum

Falsum

Cheating is always an option. We can run the computation and obtain its result through a side-channel. Since the side-channel is an effect, we need another monad:

newtype CPS3 m a = CPS3{unCPS3:: (a -> m Falsum) -> m Falsum} type K3 m a = a -> m Falsum

run

m

ST s

ST s

Either a

runCPS3 :: (forall m. CPS3 m a) -> a runCPS3 m = runST (do res <- newSTRef (error "Continuation has escaped!") unCPS3 m (\v -> writeSTRef res v >> return undefined) readSTRef res)

runCPS3

m

runCPS3

m

m

The implementation CPS3 looks suspiciously like Cont in the monad transformer library MTL, with the side-channel monad m playing the role of the answer-type w . Neither should be looked at by computations. We obtain the final implementation

newtype Cont w a = Cont{unCont:: (a -> w) -> w} type K w a = a -> w callCC :: (K w a -> Cont w a) -> Cont w a callCC f = Cont $ \k -> unCont (f k) k throw :: K w a -> a -> Cont w b throw k x = Cont $ \ k_ignored -> k x runCont :: (forall w. Cont w a) -> a runCont m = unCont m id