续体

在计算机科学中，续体（continuation，也译作计算续体、续延、延续性），是对计算机程序的控制状态的一种抽象表示。续体实化了程序控制状态。可以理解为，续体是一种数据结构，它表示在进程执行中给定点上的计算过程，所建立的数据结构可以被编程语言访问，而不是被运行时环境所隐藏掉。这对实现编程语言的某些控制机制，比如例外处理、协程、生成器非常有用。
“当前续体”从运行代码的角度看来，是可以从程序执行的当前点导出的续体。续体还被用来提及“头等续体”，它是一种构造，赋予编程语言保存在任何点上的执行状态的能力，并在程序中后来的点上可能多次返回到这一点。
历史.
Gerald J. Sussman和Guy L. Steele Jr.在1976年论文《Lambda: The Ultimate Imperative》中，认定Alonzo Church在1941年著作《The Calculi of Lambda-Conversion》里，已经清晰的理解了“续体”的使用，这是用纯λ演算彻底完成所有事情即邱奇编码的唯一方式，并举出其中有序对定义所对应的Scheme表示为例
  codice_1 means codice_2
  codice_3 means codice_4
  codice_5 means codice_6
where codice_3 e.g. selects the first element of a pair. (Note that these functions are isomorphic to codice_8, codice_9, and codice_10!) ”
</ref>：
<syntaxhighlight lang="scheme">
</syntaxhighlight>
在1993年的论文《The Discoveries of Continuations》中给出了发现续体的完整历史。Reynolds认为续体的最早描述由Adriaan van Wijngaarden在1964年9月作出。Van Wijngaarden在奥地利的维也纳附近巴登召开的关于“形式语言描述语言”的工作会议上，在关于ALGOL 60预处理器的的论文《Recursive Definition of Syntax and Semantics》中，倡导通过将真正（proper）过程变换成续体传递风格而消除标签和goto语句，但是他没有使用“续体”这个名字。
Christopher Strachey、Christopher P. Wadsworth和，在指称语义领域的工作中突出了术语“续体”，此间广泛利用续体来允许使用函数式编程语义来分析顺序程序。
Steve Russell为他给IBM 704的LISP实现的一个用户，发明了续体来解决问题，但是他没有为其命名。
头等续体.
头等续体对程式语言而言是能完全控制指令执行次序的能力。它们可以用来跳转到产生对当前函数调用的那个函数，或者跳转到此前已经退出了的函数。人们可以认为头等体保存了程序执行状态，注意到真正的头等续体不同于是很重要的，它不保存程序数据，只保存执行上下文。这经常采用“续体三明治”譬喻来说明：
假想你正站在厨房冰箱之前，想要一个三明治。你就在这里将一个续体放入口袋里。接着在从冰箱里拿出火鸡肉和面包自己做了一个三明治，然后坐到桌案前。你启用了口袋里的续体，这时发现自己再次站到了冰箱之前，想要一个三明治。幸运的是，在桌案上就有一个三明治，而用于做三明治的材料都没有了。你可以吃三明治了。
在这个譬喻中，三明治是一部份程序数据，比如在分配堆上一个对象，并非去调用“制做三明治”例程并等它返回，这里调用“以当前续体制做三明治”例程，它创建一个三明治并在已脱离执行的续体所保存的地方继续执行。
Scheme是支持续体的第一个完全的生产系统，它最初提供了源自Maclisp的用于例外处理的命名codice_11/codice_12</ref>，后来提供了头等续体算子（简写为codice_13）。
Bruce Duba将codice_14介入到了SML/NJ：
<syntaxhighlight lang="sml" inline>val callcc : ('a cont -> 'a) -> 'a</syntaxhighlight>，这里的codice_15是接受类型为codice_16的参数的续体的类型；codice_17应用codice_18到当前续体，如果codice_18以参数codice_20调用这个续体，则如同codice_21返回codice_20作为结果。
编程语言支持.
很多编程语言以各种名义体现出头等续体，特别是：
在支持闭包和适当尾调用的任何编程语言中，都可能以续体传递风格书写程序并手工实现codice_13。在续体传递风格下，codice_13成为了可以用lambda表达式书写的一个简单函数。需要支持适当尾调用，因为在续体传递风格下没有函数会返回，所有调用都是尾调用。
续体传递风格.
续体被用于计算模型，包括λ演算、指称语义、演员模型
We shall assume that the reader is familiar with advanced pattern matching languages such as SNOBOL4, CONVERT, PLANNER-71, QA4, and POPLER.
We shall use codice_49 to indicate sending the message M to the actor A. We shall use codice_50 to denote the finite squence s1, s2, … sn. ……
Identifiers can be created by the prefix operator codice_51. ……
An expression codice_52 is an abbreviation for codice_53 where receive is a more general actor that is capable of bindinq elements of the action in addition to the message.
Evaluating 
  codice_54, i.e. sending
  codice_55,
will attempt to match the-message against pattern, If the-message is not of the form specified by pattern, then the actor is codice_56 to the-message. If the-message matches pattern, the body is evaluated.……
Conceptually at least a new actor is created every time a message is sent. Consider sending to a target T a message M and a continuation C.
<syntaxhighlight lang="clojure">
(send T
The transmission codice_57 is an abbreviation for the above where C is defaulted to be the caller. If target T is the following:
<syntaxhighlight lang="clojure">
(receive
then the the-body is evaluated in an environment where the-message is bound to M and the-continuation is bound to C.……
Many actors who are executing in parallel can share the same continuation. They can all send a message codice_58 to the same continuation. ”
</ref>和进程演算。这些模型仰仗于编程者或语义工程师书写数学函数时采用“续体传递风格”（continuation-passing style，简写为CPS）。这意味着每个函数都消费一个表示有关于这个函数调用的余下计算的函数。要返回一个值，这个函数以此返回值调用这个续体函数；要中止这个计算，它返回一个值。以续体传递风格书写程序的函数式编程者，获得了以任意方式操纵控制流程的表达能力。代价是他们必须手工维护控制和续体的不变条件，这通常是高度复杂的任务。
以续体传递风格（CPS）书写的函数接受一个额外的实际参数：显式的续体，它是有一个实际参数的函数。当CPS函数已经计算出来其结果值的时候，它通过以这个值作为实际参数调用续体函数来“返回”它。这意味着在调用CPS函数的时候，调用者函数需要提供一个过程接受这个子例程的“返回”值。以这种形式表达代码使得在直接风格中隐含的一些事物显露出来。这包括了：过程返回变成了对续体的调用，中间值都有了给定名字，实际参数求值的次序变为显见，而尾调用成为简单的将传递给这个调用者的续体不修改的传递给被调用过程。
CPS的关键在于：所有函数都接受称为其续体的一个额外实际参数，并且在函数调用中的所有实际参数必须要么是一个变量，要么是一个λ表达式而非更复杂的表达式。这具有将表达式“从内至外”翻转的效果，由于表达式最内部份必须首先求值，因此CPS使得求值次序以及控制流程变得明确了。下面是在Scheme语言中仅使用其基本形式的几个例子，对比了直接风格代码和对应的CPS代码，这里约定了将续体函数表示为名为codice_59的形式参数：
注意在CPS版本的代码中，使用的函数原语（functional primitive）如这里的codice_60、codice_61、codice_62、codice_63和codice_64自身也是CPS而非直接风格，所以要使得上述例子在Scheme系统中运行，就必须写出这些函数原语的CPS版本，例如codice_63定义为：
<syntaxhighlight lang=scheme>
</syntaxhighlight>
更通用的方式是写一个转换例程：
<syntaxhighlight lang=scheme>
</syntaxhighlight>
要在直接风格函数中调用CPS函数，必须提供接收CPS函数计算结果的一个续体。上述例子在所需CPS函数原语均已提供的条件下，可以调用为：
<syntaxhighlight lang=scheme>
</syntaxhighlight>
一般的编程经常不采用CPS函数原语，比如将前面的阶乘函数写为：
<syntaxhighlight lang=scheme>
</syntaxhighlight>
以当前续体调用.
Scheme编程语言包括了控制流程算子（简写为codice_13），Scheme程序可以通过它操纵控制流程。在计算机科学中，使这种类型的隐含程序状态可见为对象，术语上叫做实化。在Scheme中，续体对象是头等值并被表示为函数，具有作为其唯一的运算。Scheme在应用续体和函数二者之间不做语法上的区分。
在Scheme中，出现在一个表达式中的<syntaxhighlight lang="scheme" inline>(call/cc f)</syntaxhighlight>，接受一个函数codice_18作为它的唯一实际参数，并把它应用到这个表达式的当前续体上。当一个续体对象被应用于一个实际参数的时候，现存的续体被消除，而应用的续体在其所在位置上被恢复，所以程序流程将在续体被捕获的那一点上继续，而“续体的实际参数”则变成codice_13调用的“返回值”。codice_13建立的续体可以被多于一次调用，甚至可以从这个codice_13应用的动态时效范围（extent）的外部来调用。
例如在表达式<syntaxhighlight lang="scheme" inline>((call/cc f) g)</syntaxhighlight>中，在概念上给出codice_18所应用到的当前续体的方式，是通过将<syntaxhighlight lang="scheme" inline>(call/cc f)</syntaxhighlight>替代为以lambda抽象绑定的一个变量比如叫做codice_72，故而它的当前续体是<syntaxhighlight lang="scheme" inline>(lambda (cc) (cc g))</syntaxhighlight>。对它应用函数codice_18，得到最终结果<syntaxhighlight lang="scheme" inline>(f (lambda (cc) (cc g)))</syntaxhighlight>。而在表达式<syntaxhighlight lang="scheme" inline>(g (call/cc f))</syntaxhighlight>中，子表达式的<syntaxhighlight lang="scheme" inline>(call/cc f)</syntaxhighlight>的续体是<syntaxhighlight lang="scheme" inline>(lambda (cc) (g cc))</syntaxhighlight>，故而整个表达式等价于<syntaxhighlight lang="scheme" inline>(f (lambda (cc) (g cc)))</syntaxhighlight>。
立即返回.
下面的例子中，使用codice_13来模拟C风格语言中的return语句：
<syntaxhighlight lang="scheme">
(define (f return) 
=> 3
=> 2
</syntaxhighlight>
首先以正规函数实际参数<syntaxhighlight lang="scheme" inline>(lambda (x) x)</syntaxhighlight>调用codice_18，它将绑定到形式参数codice_76上的这个函数应用于值codice_77，接着执行下一个表达式返回codice_78。但是，在codice_18被传递给codice_13的时候，将绑定了续体的形式参数应用于codice_77，将程序执行强制为跳转到调用codice_13那一点上，并导致codice_13返回值codice_77。这个结果接着被顶层续体用codice_85函数打印出来。
生成器.
在下面的生成器代码中，codice_13被使用了两处：一处用于生成立即返回续体，而另一处用于遍历一个成员列表的迭代在暂停时保存恢复位置：
<syntaxhighlight lang="scheme">
</syntaxhighlight>
上述代码的简单用例： 
<syntaxhighlight lang="scheme">
(define generate-digit
(display-two-digits) ;; 分两行打印 0 和 1
(display-two-digits) ;; 分两行打印 2 和 fell-off-the-end
</syntaxhighlight>
在定义变量codice_87之时，将其定义为函数codice_88应用于列表codice_89上而得到的一个闭包codice_90。这个codice_90被定义为<syntaxhighlight lang="scheme" inline>(call/cc control-state)</syntaxhighlight>。
在第一次执行codice_92之时，执行<syntaxhighlight lang="scheme" inline>(call/cc control-state)</syntaxhighlight>，进而执行codice_93，它将用于立即返回的续体绑定到形式参数codice_76之上；然后开始遍历列表的元素进行迭代的<syntaxhighlight lang="scheme" inline>(for-each (lambda (element) ……) lst)</syntaxhighlight>，在求值<syntaxhighlight lang="scheme" inline>(set! return (……))</syntaxhighlight>的第二个实际参数之时，进行每一次迭代步骤<syntaxhighlight lang="scheme" inline>(call/cc (lambda (resume-here) ……))</syntaxhighlight>，其中的<syntaxhighlight lang="scheme" inline>(set! control-state resume-here)</syntaxhighlight>，将绑定到变量codice_95上的当前续体，重新绑定到函数名字codice_96之上用于以后恢复执行，最后执行codice_97返回当前列表元素。
在下一次执行codice_92之时，<syntaxhighlight lang="scheme" inline>(call/cc control-state)</syntaxhighlight>将codice_96所绑定的续体应用当前续体上，从而在迭代的<syntaxhighlight lang="scheme" inline>(set! return (call/cc ……))</syntaxhighlight>之处恢复执行，它将传入的立即返回续体绑定到变量codice_76上，此后继续这一次的迭代步骤。在遍历了列表的元素之后迭代结束，最终执行codice_101，返回一个约定的常量。
协程.
codice_13还可以表达其他复杂的原始运算。下面的代码使用续体达成协作式多任务的用户级线程，亦称为协程：
<syntaxhighlight lang="scheme">
</syntaxhighlight>
上述代码的简单用例：
<syntaxhighlight lang="scheme">
</syntaxhighlight>
其输出为：
<syntaxhighlight lang="console">
This is AAA 0
Hello from BBB 0
This is AAA 1
Hello from BBB 1
This is AAA 2
Hello from BBB 2
</syntaxhighlight>