trying to understand Continuation monad examples implemented in Haskell.
Question:
where does fn in 1st line of goto on the right – in out fn part comes from? Is it some omitted argument or some destructuring of out [same with out (fn, num) in gotoC]?
Imagine you explain this for a mainstream language coder (JS, Java, Python)
{-# LANGUAGE ScopedTypeVariables #-}
import qualified Control.Monad.Trans.Cont as C
goto = C.callCC $ \out -> let fn = out fn
in return fn
gotoC = C.callCC $ \out -> let fn num = out (fn, num)
in return (fn, 0)
thanks for help
>Solution :
Imagine you explain this for a mainstream language coder (JS, Java, Python)
You can’t. You really can’t, because you really couldn’t write that line of code in Java.
let fn = out fn
in return fn
fn isn’t a hidden variable or anything. It’s declared right there. Its value is out fn. The variable fn is defined in terms of itself. Sounds ridiculous, but we do this all the time in Haskell. For instance,
let xs = 1 : xs in ...
this is an infinite list. The variable xs is defined to be itself with a 1 prepended to the front. The only[1] value for which this is true is the list of an infinite number of ones. Effectively, we’ve found a fixed point.
So back to your example,
goto = C.callCC $ \out -> let fn = out fn
in return fn
The type of callCC is
callCC :: ((a -> ContT r m b) -> ContT r m a) -> ContT r m a
We’re not doing anything crazy with monad transformers here, so let’s drop the m.
callCC :: ((a -> Cont r b) -> Cont r a) -> Cont r a
Therefore, the type of out must be out :: a -> Cont r b (for some r, a, and b`). Now, we wrote
let fn = out fn in return fn
and this whole expression has type Cont r a. Hence, return fn has type Cont r a, and return :: Monad m => a -> m a, so fn :: a.
We apply out to fn, so
out :: a -> Cont r b
fn :: a
out fn :: Cont r b
And fn = out fn, hence a ~ Cont r b. So fn :: Cont r b. That is, fn is a continuation which is defined to be out applied to itself.
If you don’t like the recursion, you can use fix instead.
let fn = fix out in return fn
In Haskell, in general but especially with advanced monads like Cont, you have to trust your types a bit. Cont is going to involve a lot of fancy recursive shenanigans like this, so being able to identify a value by its type (and trust that it works consistently with its type) is important.
[1] Well, the least-defined value. In this case, I believe it is truly the only fixed point, but in some situations there can be multiple. See denotational semantics for more.