Are you using delimited continuations?

Recently, I used delimited continuations to do some "scope herding" in a little language I am implementing. In short, I wrote a pair of combinators enterBlock and bindLocal that use shift and reset (and Reader-monad features) to provide automatic scoping for block-local bindings.

Through the magic of delimited continuations, code like this:

enterBlock $ do
    bindLocal "x" 2
    x <- evalVar "x" 
    bindLocal "y" (x + 1)
    x <- evalVar "x" 
    y <- evalVar "y" 
    return (x + y)

is (in effect) translated into code like this:

local (Map.insert "x" 2) $ do
    x <- evalVar "x" 
    local (Map.insert "y" (x + 1)) $ do
        x <- evalVar "x" 
        y <- evalVar "y" 
        return (x + y)

In the latter form, the scope of each local binding is expressed via the Reader monad's local combinator, which assures me that the binding's effects will not stray outside of the block established by enterBlock, regardless of how the block is exited.

Given the number of recent papers on delimited continuations that have been discussed here on LtU, I was wondering what other nifty uses our readers may have found for them.

Do you have a delimited-continuation trick or story to share? If so, what is it? Please do let us know!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Zipper and XML differencing

tmoertel wrote:
Do you have a delimited-continuation trick or story to share?
Perhaps you might be interested in zipper for comparison of two SXML trees (i.e., XML documents) side-by-side and annotation of the paragraphs that differ. The comparison algorithm is simple -- but it wasn't the point. Accessing a tree as if it were a stream and ``updating'' in-place without mutation was. The combination of zipper and of an XSLT-like enumerator let us easily ignore elements and structural differences postulated insignificant for comparison.

Joint processing of two immutable SXML documents with update cursors

Similar processing is possible in Haskell (see links in the message).

Regarding your example: dynamic scope is indeed intimately related with delimited continuations. In fact, the dynamic scope facility is macro-expressible in terms of shift/reset -- both in untyped setting (Scheme) and the typed one (Haskell). That fact permits an implementation of dynamic scoping that is robust with regards to any control effect.

What if I use mapCont?

Maybe I'm using delimited continuations. I'm not sure.

Is Haskell's Control.Monad.Cont.mapCont + callCC equivalent to delimited continuations, or is the expressive power different? callCC provides partially delimited continuations, but isn't as general as shift and reset. And you can do some very nice things with mapCont that you can't do with callCC.