Escaping from the IO monad inside the Continuation monad

Viewed 1287

A confusing title for a confusing question! I understand a) monads, b) the IO monad, c) the Cont monad (Control.Monad.Cont), and d) the ContT continuation transformer monad. (And I vaguely understand monad transformers in general -- though not enough to answer this question.) I understand how to write a program where all the functions are in the Cont monad (Cont r a), and I understand how to write a program where all the functions are in the combined Cont/IO monad (ContT r IO a).

But I'm wondering how I might write a program where some functions are in a combined Cont/IO monad (ContT r IO a) and other functions are just in the Cont monad (Cont r a). Basically, I want to write the whole program in continuation style, but only use the IO monad where necessary (much like in "regular" Haskell code, I only use the IO monad where necessary).

For example consider these two functions, in non-continuation style:

foo :: Int -> IO Int
foo n = do
    let x = n + 1
    print x
    return $ bar x

bar :: Int -> Int
bar m = m * 2

Note that foo requires IO but bar is pure. Now I figured out how to write this code fully using the continuation monad, but I needed to thread IO through bar as well:

foo :: Int -> ContT r IO Int
foo n = do
    let x = n + 1
    liftIO $ print x
    bar x

bar :: Int -> ContT r IO Int
bar m = return $ m * 2

I do want all my code in continuation style, but I don't want to have to use the IO monad on functions that don't require it. Basically, I would like to define bar like this:

bar :: Int -> Cont r Int
bar m = return $ m * 2

Unfortunately, I can't find a way to call a Cont r a monad function (bar) from inside a ContT r IO a monad function (foo). Is there any way to "lift" a non-transformed monad into a transformed one? i.e., how can I change the line "bar x" in foo so that it can correctly call bar :: Int -> Cont r Int?

3 Answers
Related