什么是延续,(a->r)->r 事物或只是 (a->r) 事物(没有包装)?
我会说a -> r 位是延续,(a -> r) -> r 是“延续传递风格”或“是延续单子的类型。
我要对延续的历史进行长时间的题外话了,这与这个问题并不真正相关......所以请注意。
我相信第一篇发表的关于延续的论文是 Strachey 和 Wadsworth 的“Continuations: A Mathematical Semantics for Handling Full Jumps”(尽管这个概念已经成为民间传说)。我认为那篇论文的想法非常重要。命令式程序的早期语义试图将命令建模为状态转换函数。例如,考虑以下 BNF 给出的简单命令式语言
Command := set <Expression> to <Expression>
| skip
| <Command> ; <Command>
Expression := !<Expression>
| <Number>
| <Expression> + <Expression>
这里我们使用表达式作为指针。最简单的指称函数将状态解释为从自然数到自然数的函数:
S = N -> N
我们可以将表达式解释为从状态到自然数的函数
E[[e : Expression]] : S -> N
和命令作为状态传感器。
C[[c : Command]] : S -> S
这个指称语义可以很简单地拼写出来:
E[[n : Number]](s) = n
E[[a + b]](s) = E[[a]](s) + E[[b]](s)
E[[!e]](s) = s(E[[e]](s))
C[[skip]](s) = s
C[[set a to b]](s) = \n -> if n = E[[a]](s) then E[[b]](s) else s(n)
C[[c_1;c_2]](s) = (C[[c_2] . C[[c_1]])(s)
这种语言的简单程序可能看起来像
set 0 to 1;
set 1 to (!0) + 1
这将被解释为将状态函数 s 转换为类似于 s 的新函数,除了它将 0 映射到 1 和 1 到 2。
这一切都很好,但是你如何处理分支?好吧,如果你想了很多,你可能会想出一种方法来处理 if 和循环的确切次数......但是一般的 while 循环呢?
Strachey 和 Wadsworth 向我们展示了如何做到这一点。首先,他们指出这些“状态转换器函数”非常重要,因此决定将它们称为“命令延续”或仅称为“延续”。
C = S -> S
他们由此定义了一个新的语义,我们将暂时这样定义
C'[[c : Command]] : C -> C
C'[[c]](cont) = cont . C[[c]]
这里发生了什么?好吧,观察一下
C'[[c_1]](C[[c_2]]) = C[[c_1 ; c_2]]
还有更多
C'[[c_1]](C'[[c_2]](cont) = C'[[c_1 ; c_2]](cont)
我们可以内联定义,而不是这样做
C'[[skip]](cont) = cont
C'[[set a to b]](cont) = cont . \s -> \n -> if n = E[[a]](s) then E[[b]](s) else s(n)
C'[[c_1 ; c_2]](cont) = C'[[c_1]](C'[[c_2]](cont)
这给我们带来了什么?嗯,一种解释while的方法,就是这样!
Command := ... | while <Expression> do <Command> end
C'[[while e do c end]](cont) =
let loop = \s -> if E[[e]](s) = 0 then C'[[c]](loop)(s) else cont(s)
in loop
或者,使用固定点组合器
C'[[while e do c end]](cont)
= Y (\f -> \s -> if E[[e]](s) = 0 then C'[[c]](f)(s) else cont(s))
无论如何......这是历史,并不是特别重要......除了它展示了如何以数学方式解释程序,并设置“延续”的语言。
此外,“1. 根据旧的 2. 内联 3. 利润定义一个新的语义函数”的指称语义方法的效果出人意料地频繁。例如,让您的语义域形成一个格(想想,抽象解释)通常很有用。你怎么知道的?好吧,一种选择是获取域的幂集,并通过将您的函数解释为单例来注入其中。如果你内联这个 powerset 构造,你会得到一些可以模拟非确定性的东西,或者在抽象解释的情况下,除了关于它做什么的确切确定性之外,关于一个程序的各种信息量。
随后进行了各种其他工作。在这里,我跳过了许多伟大的文章,例如 lambda 论文……但是,也许最值得注意的是 Griffin 的具有里程碑意义的论文“A Formulae-as-Types Notion of Control”,它展示了延续传递风格和经典逻辑之间的联系。这里强调“延续”和“评估上下文”之间的联系
也就是说,E 表示在评估 N 之后剩余的计算。在求值序列的这一点上,上下文 E 被称为 N 的延续(或控制上下文)。正如我们将在下面看到的,评估上下文的符号允许对操作延续的操作符的操作语义进行简明规范(实际上,这是它的预期用途 [3,2,4,1])。
明确“延续”是“只是a -> r 位”
这一切都是从语义的角度看待事物,并将延续视为函数。问题是,作为函数的延续给你的权力比你通过类似方案的 callCC 获得的更多。因此,关于延续的另一个观点是,它们是程序中的变量,将调用堆栈内部化。 Parigot 的想法是让连续变量成为一个单独的句法类别,从而在“λμ-演算:经典自然演绎的算法解释”中产生了优雅的 lambda-mu 演算。
observable/observer 的措辞是否正确?
我认为它是 Eric Mejier 所使用的。这是学术 PL 中的非标准术语。
上面的引用真的是矛盾的吗,有共同的真理吗?
让我们再看看引文
延续是计算机程序控制状态的抽象表示。
在我的解释(我认为这是相当标准的)中,延续模拟程序下一步应该做什么。我认为维基百科与此一致。
Haskell 延续具有以下类型:
这有点奇怪。但是,请注意,Gabriel 在后面的帖子中使用了更标准的语言并支持我使用的语言。
这意味着如果我们有一个有两个延续的函数:
(a1 -> r) -> ((a2 -> r) -> r)