Consider the following example in Haskell of a function quux along with the definitions of the continuation monad and callCC.
instance Monad (Cont r) where
return x = cont ($ x)
s >>= f = cont $ \c -> runCont s $ \x -> runCont (f x) c
callCC :: ((a -> Cont r b) -> Cont r a) -> Cont r a
callCC f = cont $ \h -> runCont (f (\a -> cont $ \_ -> h a)) h
quux :: Cont r Int
quux = callCC $ \k -> do
let n = 5
k n
return 25
As I understand this example. The do block can be thought of as
k n >>= \_ -> return 25 ==
cont $ \c -> runCont (k n) $ \x -> runCont ((\_ -> return 25) x) c
And we can see from the definition of k which is \a -> cont $ \_ -> h a that in the above we have \x -> runCont ((\_ -> return 25) x) c being passed into the argument that is ignored with underscore. Ultimately the return 25 is effectively "ignored" because the underscore argument is never used so from lazy evaluation its never evaluated.
So as far as I can tell this implementation of callCC strongly fundamentally depends on lazy evaluation. How would this callCC be done in a strict (non-lazy) functional language?
No. This implementation of
callccdoesn't depend upon lazy evaluation. To prove this I'll implement it in a strict functional language and show that anything afterk nis not executed at all.The strict functional language I'll be using is JavaScript. Since JavaScript is not statically typed you don't need to declare a
newtype. Hence we start by defining thereturnand>>=functions of theContmonad in JavaScript. We'll call these functionsunitandbindrespectively:Next we define
callccas follows:Now we can define
quuxas follows:Note that I added an
alertinside the second argument tobindto test whether or not it's executed. Now if you callquux(alert)it will display5but it won't display"Hello World!". This proves that the second argument tobindwas never executed. See the demo for yourself.Why does this happen? Let's start backwards from
quux(alert). By beta reduction it's equivalent to:By beta reducing it again it becomes:
Next by the beta reduction of
bindwe get:Now we can see why
"Hello World!"was never displayed. By beta reduction we're executingfunction () { alert(5); }. It's the job of this function to call its argument, but it never does. Because of this execution stops and"Hello World!"is never displayed. In conclusion:The
callccfunction doesn't depend upon lazy evaluation.The function created by
callccterminates afterkis called not because of lazy evaluation but because callingkbreaks the chain by not calling it's first argument and hence returns immediately.This brings me back to your question:
You're mistaken. As you can see
kis(\a -> cont $ \_ -> h a)and the function(\x -> runCont ((\_ -> return 25) x) c)is passed into the the argument that's ignored byk. You were correct until then. However this doesn't mean thatreturn 25is not evaluated because of lazy evaluation. It's simply not evaluated because the function(\x -> runCont ((\_ -> return 25) x) c)is never called. I hope that cleared things up.