Obviously MonadCont
s is more restricted and gives more power than plain Monad
s, thanks to its callCC
. This means less instances of it, and more you can do with it.
When look at defined instances of MonadCont
, it looks like everything listed there require either Cont
or ContT
or an already existing MonadCont
instance. That means we have to start with some Cont
or ContT
and in particular cannot turn IO
into a MonadCont
.
However, I believe it makes sense to use callCC
in a IO
context, so we can simplify the following (adjusted from the official Hackage page callCC
example):
whatsYourName :: IO ()
whatsYourName = do
name <- getLine
let response = flip runCont id $ do
callCC $ \exit -> do
when (null name) (exit "You forgot to tell me your name!")
return $ "Welcome, " ++ name ++ "!"
print response
into
whatsYourName' :: IO ()
whatsYourName' = do
name <- getLine
response <- callCC $ \exit -> do
when (null name) (exit "You forgot to tell me your name!")
return $ "Welcome, " ++ name ++ "!"
print response
to use callCC
in a do block in a cleaner way.
Of cause, to make IO
an instance of MonadCont
we must have some magic, since callCC
for IO
means "call the given function with the future computation specifies what to happen next in the real world", so only the interpreter or the compiler can actually know what this mean. On the other hand, I didn't see any theoretical reason that this is importable, since Scheme already have it for a long time, and making such an instance requires no language change at all.
Possible issue
One factor I can think of is that the semantic of callCC
is conflict with proper cleanup guarantee. A lot of languages provides "try...finally" control for proper cleanup, and C++'s destructor is also garantee that. I am not sure what is it in Haskell, but if callCC
is available for IO
one can then use it to escape from any IO
involved context that requires cleanup, so providing sush a garantee will become impossible, as you can see what happens in Ruby.
Discussion of opinions
The answer from @jozefg is very good. I just want to write down my opinions here.
It is true that
MonadCont
come from mtl. But that does not means GHC or other compiler cannot define aunsafeCallCC
and define the instance ifMonadCont
with the correct definition is in scope of the compiling module and-XIOMonadCont
being set.I already talked about exception safety and it looks hard to be sure about that. However, Haskell already have
unsafePerformIO
, which basically even more unsafe thanunsafeCallCC
, in my opinion.Of cause
callCC
is, in most case, too powerful and should be avoid when possible. However, in my opinion, continuation passing style can be used to make lazy evaluation explicit, which can help better understand the program and thus easier to find possible optimizations. Of cause CPS is notMonadCont
, but it is a natural step to use it and convert the deep nested inner functions into do notations.