shift/reset 以外にも部分継続を扱うオペレータとして control/prompt というのがあります。この違いをすぐに忘れるのでメモ。
以下のコードは Olivier Danvy's puzzle と呼ばれているらしいです *1 。ちなみに Olivier Danvy は shift/reset 提唱者。
x = reset do shift {|k| [1] + k.call } shift {|k| [2] + k.call } shift {|k| [3] + k.call } [] end p x #=> [1, 2, 3]
x = prompt do control {|k| [1] + k.call } control {|k| [2] + k.call } control {|k| [3] + k.call } [] end p x #=> [3, 2, 1]
つまり reset のブロックが終わったとき、後に部分継続を呼び出した方から再開するのが shift/reset で、先に部分継続を呼び出した方から再開するのが control/prompt 。なんとなく、shift/reset がスタックで control/prompt がキューみたいな印象。というか control/prompt はバグってるぽいですね。
以下、control/prompt の実装 (元ネタ: http://okmij.org/ftp/Scheme/delim-control-n.scm) 。なんかややこしいですね。
require "continuation" $ctns = [] def prompt callcc do |c| $ctns << [c, true] v = yield $ctns.pop.first.(v) end end def control callcc do |c1| cs = [] cs.unshift $ctns.pop until $ctns.last.last k = proc do |v| callcc do |c2| $ctns += [[c2, false]] + cs # 上の false を true にすると shift/reset になる c1.(v) end end v = yield(k) $ctns.pop.first.(v) end end