Friday, January 05, 2007

Going Further with the Primitives

#|

Delimited Continuations in MzScheme

A Tour - Part 1

Prompts
  1. Going further with the Primitives

Our journey continues into the deep waters of delimited continuations. The last time we talked on this matter we saw some interesting behavior exhibited from the code of our very last example. You may remember that I mentioned at the time that this behavior came about because we had put our primitive operators together in such a way as to resemble that of the operators on which PLT's delimited continuations are modeled, which exhibit similar behavior. You should expect to learn the names of these higher level abstraction operators by the end of this post as well as the name of their author. However, for bonus points, if you take a look at ../PLT/collects/mzlib/control.ss, you already have enough knowledge to figure out which operators we were mimicking on the last example of the previous post on this tour. That file, control.ss, is the file that defines all the higher level abstraction operators; the ones published in journal articles. If you are adventurous, and I suspect you are or you wouldn't be reading my blog, when you look at control.ss you should notice that these higher level abstraction operators are all defined in terms of the primitive operators. That is precisely why we are going to spend this post, revisiting those primitives, since we hardly did more than get introduced in the last installment, and see what other gems of insight are laying around for the picking for anyone who will spend a little time experimenting with them. There is another reason to have the primitives well established in our tool box, as will see today; you are simply more flexible and have more options if you know, and aren't afraid to use, the primitive operators. So, with this as prelude, lets do some experiments.

First off, lets setup our support syntax, so that you can simply cut and paste this entire post to try out the code from this post by yourself in DrScheme. Speaking of DrScheme, it will be imperative that you have up to the minute versions of DrScheme to ensure that this code runs as advertised. You may pickup the latest binary installers from PLT Nightly Builds. This is because DrScheme has just made some fairly exhaustive changes to their system to further integrate delimited continuations into the REPL among other changes. I'm afraid if you don't use the most current version of DrScheme you'll be disappointed when you attempt to run the code from this post and especially from future posts, where I will intentionally rely on this integration to be available for the examples to work.

|#
(module support mzscheme

 (provide (all-defined)
          (all-from (lib "78.ss" "srfi")))

 (require (lib "78.ss" "srfi"))

 ;(call-with-continuation-prompt
 ;thunk [prompt-tag handler-proc-or-false]
 (define call/cc/prompt
   call-with-continuation-prompt)
 ;(default-continuation-prompt-tag)
 (define default/prompt/tag
   default-continuation-prompt-tag)
 ;(make-continuation-prompt-tag
 ;[symbol]
 (define make/prompt/tag
   make-continuation-prompt-tag)
 ;(abort-current-continuation
 ;prompt-tag obj ...)
 (define abort/cc
   abort-current-continuation)
 ;call-with-composable-continuation
 ;proc [prompt-tag])
 (define call/wcc
   call-with-composable-continuation)
 ;(continuation-prompt-available?
 ;prompt-tag [cont])
 (define prompt/available?
   continuation-prompt-available?)


 (define-syntax let/wcc
  (lambda (stx)
   (syntax-case stx ()
    ((_ k tag . body )
     #'(call-with-composable-continuation
        (lambda (k) . body) tag)))))

 (define-syntax @@
  (lambda (stx)
   (syntax-case stx ()
    ((_ tag handler . body)
     #'(call-with-continuation-prompt
        (lambda () . body) tag handler)))))

 (define-syntax !!
  (lambda (stx)
   (syntax-case stx ()
    ((_ tag . args)
     #'(abort-current-continuation tag . args))))) 

 (define-syntax @
  (lambda (stx)
   (syntax-case stx ()
    ((_ )
     #'(default-continuation-prompt-tag)))))

 (define-syntax cout
  (lambda (stx)
   (syntax-case stx ()
    ((_ e1 e2 ...)
     #'(begin (printf "~s " e1) (cout e2 ...)))
    ((_ e1 ) #'(printf "~s~n" e1))
    ((x ) (identifier? #'x) #'(newline)))))

 (define-syntax couta
  (lambda (stx)
   (syntax-case stx ()
    ((_ e1 e2 ...)
     #'(begin (printf "~a " e1) (couta e2 ...)))
    ((_ e1 ) #'(printf "~a~n" e1))
    ((x ) (identifier? #'x) #'(newline)))))

 (define (hr)
   (cout '______________________________________________))

 (define-syntax cp
  (lambda (stx)
   (syntax-case stx ()
    ((_ str e)
     (with-syntax
         ([val (datum->syntax-object stx 'val)])
                    #'(let ([val e])
                        (cout str val)
                        val))))))

 (define-syntax display->result/string
  (lambda (stx)
   (syntax-case stx ()
    ((_ e1 e2 ...)
     #'(parameterize ((current-output-port
                       (open-output-string)))
       (values ((lambda () e1 e2 ...))
       (get-output-string (current-output-port))))))))

 (define-syntax test-case/result
  (lambda (stx)
   (syntax-case stx (=>)
    ((_ test-name (c1 c2 ...) =>
     expected-value => display-value)
     #'(begin (printf "~n~a~n" test-name)
     (let-values ([(result display-result)
                   (display->result/string (c1 c2 ...))])
                   (check result => expected-value)
                   (check display-result => display-value)
                   (couta display-result '=> result)
                   (hr))))
    ((_ test-name (c1 c2 ...) => expected)
     #'(begin (printf "~n~a~n" test-name)
           (let ([result (c1 c2 ...)])
             (check result => expected)
             (couta result)(hr)))))))
 )
(require support)
(require-for-syntax support)

(check-set-mode! 'report-failed)
#|

Lets start right off with that last example from the previous installment. The code has been changed to allow more tests to be run on the same core code, which is essentially what we had before. Except now the prompts have been parameterized, and the function passed to the default handler has been designed to display the availability of all three prompt tags within the handler. This gives us better insight into why control flow is the way it is.

|#
(let ([tag1 (make/prompt/tag '=1)]
     [tag2 (make/prompt/tag '=2)]
     [tag3 (make/prompt/tag '=3)]
     [let/wcc-tag (make-parameter 0)]
     [target-tag (make-parameter 0)])

 (define (display/available?)
   (cout 'tag1-avail? (prompt/available? tag1))
   (cout 'tag2-avail? (prompt/available? tag2))
   (cout 'tag3-avail? (prompt/available? tag3)))     

 (define (handler x)
   (@@ (@) handler (x)))

 (define (triple-tag)
  (@@ (@) #f
   (cp tag1
    (* 32
     (@@ tag1 #f
      (cp tag2
       (* 16
        (@@ tag2 #f
         (cp tag3
          (* 8
           (@@ tag3 #f
            (cp 'let/wcc
             (* 4
              (let/wcc k (let/wcc-tag)
               (!! (target-tag)
                (lambda () (display/available?) (k 2))
                )))))))))))))))

 (test-case/result 'tag1
                   (parameterize ([let/wcc-tag tag3]
                                  [target-tag tag1])
                     (triple-tag))
                   => 256
                   => "tag1-avail? #t
tag2-avail? #f
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=1> 256
")


 (test-case/result 'tag2
                   (parameterize ([let/wcc-tag tag3]
                                  [target-tag tag2])
                     (triple-tag))
                   => 4096
                   => "tag1-avail? #t
tag2-avail? #t
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=2> 128
#<continuation-prompt-tag:=1> 4096
")
 (test-case/result 'tag3
                   (parameterize ([let/wcc-tag tag3]
                                  [target-tag tag3])
                     (triple-tag))
                   => 32768
                   => "tag1-avail? #t
tag2-avail? #t
tag3-avail? #t
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
#<continuation-prompt-tag:=1> 32768
")
#|

What happens is that when a prompt is targeted, all the prompts proximal to the targeted prompt become unavailable, and consequently the continuation does not include the code enclosed by those no-longer available prompts. In our example above, when tag1 is targeted, the default handler calls our thunk, which is arranged to display the available tags first and then apply the continuation from the let/wcc (call-with-composable-continuation) to the number two (2.) This results in the value two (2) being returned as the value of the let/wcc expression, and it is then multiplied by four (4) to give us eight (8). Then it prints let/wcc 8. Now, since the next available prompt tag is tag1, it does a jump to the prompt tagged with tag1, where it is multiplied by 32 to give us 256 and it displays continuation-prompt-tag:=1 256. Similarly, when tag2 is targeted, let/wcc takes the value of two (2) and then is multiplied by four (4) to give us eight (8), and let/wcc 8 is displayed. This time prompt tag1 and tag2 are available, so the continuation takes us to the prompt tagged with tag2, where it is multiplied by 16 and it displays continuation-prompt-tag:=2 128. It then continues through prompt tag1 being multiplied by 32 to yield 4096, and it displays continuation-prompt-tag:=1 4096. Finally, when tag3 is targeted, all tags are available, and the process goes right up being multiplied at each step to finally yield (* 2 4 8 16 32) = 32768, and it displays continuation-prompt-tag:= 32768.

Note that regardless of which tag was available, it always passed through the multiplication between prompt tag3 and let/wcc. That is it always executed the body of the most proximal tag. The jumps are made to the prompts themselves, and not their bodies. Lets see what happens when we change the tag specified in the let/wcc statement.

|#
 (test-case/result 'tag1->tag1
                   (parameterize ([let/wcc-tag tag1]
                                  [target-tag tag1])
                     (triple-tag))
                   => 32768
                   => "tag1-avail? #t
tag2-avail? #f
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
#<continuation-prompt-tag:=1> 32768
")
 (test-case/result 'tag2->tag1
                   (parameterize ([let/wcc-tag tag2]
                                  [target-tag tag1])
                     (triple-tag))
                   => 2048
                   => "tag1-avail? #t
tag2-avail? #f
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=1> 2048
")
 (test-case/result 'tag2->tag2
                   (parameterize ([let/wcc-tag tag2]
                                  [target-tag tag2])
                     (triple-tag))
                   => 32768
                   => "tag1-avail? #t
tag2-avail? #t
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
#<continuation-prompt-tag:=1> 32768
")
#|

In both cases we targeted tag1, but in the first case we had let/wcc reference tag1, and in the second it referenced tag2. You can see that nothing changed with regard to what prompts are available, given that tag1 is targeted, i.e., only tag1 is available. However, the continuation of let/wcc has been changed dramatically by changing which tag it references. When it references tag2, the second case, the continuation takes the computation through prompt tag3, even though it is not available. The tag referenced in let/wcc is in effect setting a lower-upper bound, while the targeted tag of the abort (!!) sets the upper-lower bound of the functional geography. Thus when in the last case we set both to tag2 the lower-upper bound takes us all the way up to tag2 and the upper-lower bound takes us to tag2 and beyond, yielding 32768. The same thing occurs when we set both to tag1. And we have previously shown the case where both were set to tag3.

Now what would happen if we were to reference and/or target the default prompt tag. Lets try some new scenario's.

|#
 (test-case/result '@->@
                   (parameterize ([let/wcc-tag (@)]
                                  [target-tag (@)])
                     (triple-tag))
                   => 32768
                   => "tag1-avail? #f
tag2-avail? #f
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
#<continuation-prompt-tag:=1> 32768
")
 (test-case/result 'tag1->@
                   (parameterize ([let/wcc-tag tag1]
                                  [target-tag (@)])
                     (triple-tag))
                   => 1024
                   => "tag1-avail? #f
tag2-avail? #f
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
")
 (test-case/result 'tag2->@
                   (parameterize ([let/wcc-tag tag2]
                                  [target-tag (@)])
                     (triple-tag))
                   => 64
                   => "tag1-avail? #f
tag2-avail? #f
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
")
 (test-case/result 'tag3->@
                   (parameterize ([let/wcc-tag tag3]
                                  [target-tag (@)])
                     (triple-tag))
                   => 8
                   => "tag1-avail? #f
tag2-avail? #f
tag3-avail? #f
let/wcc 8
")
#|

This is behavior we haven't seen before, but we can still explain it with our lower-upper bound and upper-lower bound terminology, while taking into account which prompts are available. In the first case, we are referencing and targeting the default prompt tag. Not surprisingly, given our previous results where we have referenced and targeted the same tag, we get a single full execution of the functional topography, yielding 32768. Now for those still with me, I have a special bit of information to pass along. You may have noticed that triple-tag, the parameterized function we've been using throughout this installment has four (4) tags. It has tag1, tag2 and tag3, which are all we've talked about so far, but it also starts of by installing a default tag. We'll the reason we haven't mentioned the default tag until now, is that while writing this post, up until this part in the post, there wasn't a default tag installed at the start of triple-tag. It was only after referencing a named tag while targeting the default tag that the requirement for a default tag to be installed at the beginning of the function became apparent. At first thought, since there is always a default tag around, it hadn't seemed necessary to explicitly install one. But the code would prove me wrong. Without it, the strangest things started to happen: it would execute a tag1->@ (reference tag1->target default) function call and the next thing you know it's executing code a page down in the source file. I never really did figure out what was going on, I just inferred that the function needed to have a more proximal default prompt installed, and thus we now have a default prompt installed at the beginning of triple-tag (I didn't bother renaming it to quarto-tag, since only three are named. and I'm a lazy typist.) That's all it took, and the behavior becoming steady and predictable once again. The other thing to note is that absolutely nothing in the results changed as a result of installing the default prompt tag at the beginning of the function. I was very glad that I am in the habit of using test-cases on everything I post at that point, because that meant nothing I had said earlier had to be changed, since the data was identical.

Let's start with the tag1->@ case. In this case, as in all three of these examples where we target the default prompt, none of the named tags are available. So that leaves us with only the continuation created by the referenced tag in the let/wcc statement, which serves as a lower-upper bound on how far up the computational tree we will execute before jumping to the targeted tag. In the case of tag1->@ our lower-upper bound is tag1, so it's going to take two (2) and multiply it by 4, 8 and 16 to yield 1024. For the case of tag2->@, tag2 is our lower-upper bound, so the product will be (* 2 4 8) to yield 64. And finally, in the case of tag3->@ we get a product of (* 2 4) to yield 8. Since no computation occurs beyond the default prompt tag installed at the beginning of the function, in all three cases, despite jumping to the default prompt tag at the end, the result of the application is determined completely by the computation done during the initial backtracking due to the continuation of let/wcc.

We get a bit more interesting behavior when we reference the default prompt tag and target a named tag, as in @->tag3, our first example below.

|#
 (test-case/result '@->tag3
                   (parameterize ([let/wcc-tag (@)]
                                  [target-tag tag3])
                     (triple-tag))
                   => 134217728
                   => "tag1-avail? #t
tag2-avail? #t
tag3-avail? #t
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
#<continuation-prompt-tag:=1> 32768
#<continuation-prompt-tag:=3> 262144
#<continuation-prompt-tag:=2> 4194304
#<continuation-prompt-tag:=1> 134217728
")
 (test-case/result '@->tag2
                   (parameterize ([let/wcc-tag (@)]
                                  [target-tag tag2])
                     (triple-tag))
                   => 16777216
                   => "tag1-avail? #t
tag2-avail? #t
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
#<continuation-prompt-tag:=1> 32768
#<continuation-prompt-tag:=2> 524288
#<continuation-prompt-tag:=1> 16777216
")
 (test-case/result '@->tag1
                   (parameterize ([let/wcc-tag (@)]
                                  [target-tag tag1])
                     (triple-tag))
                   => 1048576
                   => "tag1-avail? #t
tag2-avail? #f
tag3-avail? #f
let/wcc 8
#<continuation-prompt-tag:=3> 64
#<continuation-prompt-tag:=2> 1024
#<continuation-prompt-tag:=1> 32768
#<continuation-prompt-tag:=1> 1048576
")
) 
#| 

This all fits within our model of the continuation being structured by a lower-upper bound from the referenced prompt tag, and the upper-lower bound coming from the targeted tag. In all three of these cases we are referencing the default prompt tag, but it is installed at the very beginning of the function, so our lower-upper bound is the beginning of the function itself. Thus, in each case, regardless of the targeted tag, we get an initial computation of 32768 from backtracking all the way to the top of the function. Then depending on the individual case, the targeted tag determines how far back up we go before we recommence with the computation.

Starting with the case of @->tag3 we start off with 32768 from our referenced tag, and proceed through the entire computational tree once again, because all prompts are available. So we get the product (* 32768 8 16 32) => 134217728. And likewise for the next two examples, for @->tag2 we have (* 32768 16 32) => 16777216. and finally for @->tag1 (* 32768 32) => 1048576. It would seem that our computational model of the interplay between referenced and targeted tags is sound, having been born out in all combinations of our functional prototype. Still, when dealing with first class continuations, your intuition can often be wrong, so we would prefer formal mathematical proof. Actually, that's a paraphrase of a quote from the very gifted Oleg Kiselyov, as he set out to prove (call/cc call/cc) was self-application, or put another way, the fixed-point of call/cc is (lambda (x) (x x)). I had actually spent some time at trying to see if the fixed point of (@@ (@) let/wcc k), which roughly behaves as call/cc, would turn out to be the same. The function I actually tested was (define (call/cc% f) (call-with-continuation-prompt (lambda () (call-with-composable-continuation f) I can report that it was an ill conceived venture from the beginning. Hopefully, by the end of this installment it will be clear why the continuation of call/wcc could never mimic that of call/cc. For your reference, the paper is at http://okmij.org/ftp/Scheme/callcc-calc-page.html, "Normal-order direct-style beta-evaluator with syntax-rules, and the repeated applications of call/cc", The presentation at the Workshop ``Daniel P. Friedman: A Celebration.'' December 4, 2004. Bloomington, IN.

There are just a couple other things I'd like to demonstrate in this installment before we close. We've been working with let/wcc hand-in-hand with !!. That is, in our prototype function, we have always been aborting the continuation with abort-current-continuation immediately after capturing it with call-with-composable-continuation. This need not be the case. After all, let/wcc provides us with a perfectly fine working continuation in a manner similar to call/cc, and we can go about exploiting that continuation all on its own, without ever resorting to calling abort-current-contination. In these next examples we leave triple-tag behind us and explore just this topic. Read carefully and you'll catch the difference that doomed my attempt at mimicking call/cc that I mentioned above.

|#

(let ()

 (define (dhandler x)
   (@@ (@) dhandler (x)))

 (test-case/result 'let/wcc-k-tag1
  (cp 'expr=
   (let ([d 2])
    (let ([tag1 (make/prompt/tag '=1)])
     (* 16
      (cp tag1
       (@@ tag1 dhandler
        (cp 'inside-tag1
         (* 4
          (cp 'let/wcc=
           (let/wcc k tag1
            (* 1 (k d) 3)))))))))))
                   => 1536
                   => "let/wcc= 2
inside-tag1 8
let/wcc= 24
inside-tag1 96
#<continuation-prompt-tag:=1> 96
expr= 1536
") 
#|

In this example we have a single prompt tag1. This time, however, when we get to the let/wcc statement, which incidentally is referencing our single prompt tag1, it is not followed by an abort, but rather by an expression, part of which applies the continuation, in k, to the variable d, which has been bound to the number two (2) in a let statement enclosing the whole application. We see that upon k being applied to 2, the control transfers immediately back to the let/wcc clause which assumes that value, and let/wcc= 2 is displayed. It continues, being multiplied by four (4) and inside-tag1 8 is displayed. This is really beautiful how it shows the natural continuation of let/wcc (call-with-composable-continuation), because the next thing it does is return to finish the expression inside the let/wcc body, having encountered the referenced tag1. Here the 8 is multiplied by three (3) to yield 24 and let/wcc= 24 is displayed. Here we have let/wcc in isolation of abort-current-continuation faithfully executing on that lower-upper bound paradigm we established while working with triple-tag. Now it simply continues up the computation unimpeded by a targeted tag, with the product (* 24 4 16) => 1536 yielding the result of the application.

|#
(test-case/result 'let/wcc-k-@
(let ([d 2])
 (let ([tag1 (make/prompt/tag '=1)])
  (cp tag1
   (* 16
    (@@ tag1 dhandler
     (cp 'inside-tag1
      (* 4
       (@@ (@) dhandler
        (cp 'let/wcc=
         (let/wcc k (@)
          (* 1 (k 2) 4)))))))))))
     => 512
     => "let/wcc= 2
let/wcc= 8
inside-tag1 32
#<continuation-prompt-tag:=1> 512
")
#|

Here we are referencing the default tag once again, and it was indeed necessary to explicitly set a default prompt tag, this time directly behind the let/wcc statement. I think you have the gist of this by now. K is applied to 2 and let/wcc= 2 is displayed. We have reached our referenced tag, so control returns to complete the expression where it is multiplied by 4 and we have let/wcc= 8 displayed. Finally, the product (* 8 4 16) => 512 is displayed as the value of the entire application.

|#
(test-case/result 'let/cc-k
(let ([d 2])
 (let ([tag1 (make/prompt/tag '=1)])
  (cp tag1
   (* 16
    (@@ tag1 dhandler
     (cp 'inside-tag1
      (* 4
       (cp 'let/cc=
        (let/cc k
         (* 1 (k d) 5))))))))))
 => 128
 => "let/cc= 2
inside-tag1 8
#<continuation-prompt-tag:=1> 128
")
#|

This example is simply to differentiate the behavior of let/cc from let/wcc. We see k is applied to two (2) and let/cc= 2 is displayed, but unlike let/wcc, let/cc has abandoned the expression from which it first jumped out of completely and merely completes the product (* 2 4 16) => 128.

|#
 (test-case/result 'let/wcc-k-tag2
  (let ([d 2])
   (let ([tag1 (make/prompt/tag '=1)]
         [tag2 (make/prompt/tag '=2)])
         (cp tag2
          (@@ tag2 dhandler
           (* 16
            (cp tag1
             (@@ tag1 dhandler
              (cp 'inside-tag1
               (* 4
                (cp 'let/wcc=
                 (let/wcc k tag2
                  (* 1 (k d) 6))))))))))))
     => 49152
     => "let/wcc= 2
inside-tag1 8
#<continuation-prompt-tag:=1> 8
let/wcc= 768
inside-tag1 3072
#<continuation-prompt-tag:=1> 3072
#<continuation-prompt-tag:=2> 49152
")
#|

There is nothing particularly different about this example, except we're using two (2) prompt tags. I'll leave this one for you to work through.

|#
 (test-case/result 'let/wcc-k-@
  (let ([d 2])
   (cp '@-outer
    (@@ (@) dhandler
     (* 16
      (cp '@-inner
       (@@ (@) dhandler
        (cp 'inside-inner-@
         (* 4
          (cp 'let/wcc=
           (let/wcc k (@)
            (* 1 (k d) 7)))))))))))
     => 3584
     => "let/wcc= 2
inside-inner-@ 8
let/wcc= 56
inside-inner-@ 224
@-inner 224
@-outer 3584
")
#|

We've not seen this before. We've got two (2) default prompt tags installed in this application. So, really, it's all about what happens in a situation where we are referencing a tag which has been installed more than once in the application. Your intuition, is probably like mine, in that it will use the most proximal, that is the first encountered referenced tag as its lower-upper bound. Lets see. We have k applied to 2 and let/wcc= 2 is displayed. There is nothing in the way so it clearly must be multiplied by four (4) yielding 8, and inside-inner-@ 8 is displayed. Next, just as we assumed, it encounters the referenced tag and returns to complete the expression, being multiplied by seven and let/wcc= 56 is displayed. Now, with no targeted tags to worry about it just completes the product (* 56 4 16) => 3584 to yield the final value of the application.

I'm sure by now you feel confident that you could predict and explain the behavior of the primitive functions in similar settings without much difficulty. Without formal proof, I would say we have pretty good evidence that our working paradigm on how the continuations of call-with-composable-continuation and abort-current-continuation work is a reliable model on which to base the assessment of code. However, I should warn you, and remind you of Oleg's comment about first class continuations. There are interactions with continuations at all levels of DrScheme or MzScheme, error handlers being one of them. You have to be willing to accept that not all that is known about these functions has been revealed, and more importantly, not all that these functions are capable of may yet be known. Keep it in your mind the example I gave today about running into trouble with default prompts which were not explicitly installed, and the weird behavior that came from that simple problem. Another warning is that the behavior of these functions relies heavily on the definition of any custom handler, as you will see below. Finally, if you do run into anything that seems beyond explanation, please forward it on to me, as I am trying to build my own database of areas to avoid or investigate further. With that let me make good on my promise at the beginning of the post and introduce you to one of the derived, or non-primitive, sets of operators on which the delimited continuations of DrScheme are said to have been based.

This is the definition of Sitaram's % operator, think (@@ (@) #f f), pronounced, prompt, as put forward in the library file, control.ss. Note, % can take a custom prompt tag and handler, or if not specified will use the default-prompt-tag and default-handler. This would be a useful feature except that fcontrol, think (let/wcc k (@) (!! (@) f k), as written in control.ss, has no way of specifying a thunk as an argument to %'s handler, which, if you remember, the default handler requires.

 (define-syntax %
   (syntax-rules ()
     [(_ expr handler)
      (call-with-continuation-prompt
       (lambda () expr)
       (default-continuation-prompt-tag) handler)]
     [(_ expr)
      (call-with-continuation-prompt
      (lambda () expr))])) 

This is the definition of Sitaram's fcontrol operator as put forward in the library file control.ss. Notice that the continuation from call/wcc is being passed back as an argument to the function f. This could not be used with the default prompt handler, which expects a thunk.

 (define (fcontrol f)
   (call-with-composable-continuation
    (lambda (k)
    (abort-current-continuation
     (default-continuation-prompt-tag) f k))))

These are my alternate definitions of Sitaram's % and fcontrol operators, named %% and fc respectively. We need these alternate definitions because the ones defined in control.ss, while correct if used with the right custom handler, will not work with the default handler, so we remove that option, and install the handler that Sitaram used in his paper.

|#
(let ([tag1 (make/prompt/tag '=1)])
   (define-syntax %%
     (syntax-rules ()
       ((_ tag . body)
        (call-with-continuation-prompt
         (lambda () . body) tag (lambda (r k) r)))))
  
   (define (fc tag f)
     (call-with-composable-continuation
      (lambda (k)
        (abort-current-continuation tag f k)) tag))
#| 

What follows is the final example of the post, and represents a simple non-local exit use of continuations for which we are all familiar, and would normally use let/ec to handle the task. let/ec is still the answer to this type of problem, by the way. The example only gives you a simple taste of what is in store for the next post when we go over some very interesting examples, and where delimited continuations really come into their own as a unique set of tools no one should be without.

|#
   (define product
     (lambda (s)
       (%% tag1
         (let loop ([s s])
           (if (null? s)
               1
              (let ([a (car s)])
               (display a)
               (if (= a 0)
                   (fc tag1 0)
                   (* a (loop (cdr s)))))))
           (lambda (r k) r))))
  
   (test-case/result 'product
                     (product '(1 2 3 4 0 6 7 8 9 20))
                     => 0
                     => "12340")
)
)

(let ([n 38])
 (printf "~nPassed all ~a tests? ~a" n (check-passed? n)))

(check-report)
#|
Passed all 38 tests? #t
; *** checks *** : 38 correct, 0 failed.

And we get the expected result on this use of fcontrol to execute a simple escape from a loop. BTW, this example is a modified version of an example from Dorai Sitaram's, "Handling Control", 1993. The only change is that I'm using the prompt enabled versions of the functions, that I defined earlier. In fairness to Sitaram, he saw the need for a hierarchy of prompts years before this paper, and works with prompted versions of his functions later on in the paper itself. This would be a good paper for you to read, because the next installment will feature a couple of non-trivial examples from his paper, and the more you are familiar with the algorithms the better. The examples involve working with concurrency through the use of delimited continuations. One example looks at nested engines. This is definitely a topic worthy of some self study before the next installment. I can suggest, in addition to reading Sitaram's paper, that you look at the final problem in "The Scheme Programming Language" 3rd ed., Kent Dybvig. I also believe Sitaram devotes some time to this topic in his book, Teach Yourself Scheme in FIXNUM Days.

Have fun.

--kyle