Answers:
这时显而易见的问题是:如果CPS非常出色,那为什么我们不一直使用它呢?为什么大多数专业开发人员从未听说过它,或者,有的开发人员认为它只是那些疯狂的Scheme程序员所做的事情?
首先,对于大多数习惯于思考子例程,循环,try-catch-finally等的人来说,很难以这种方式将委托用于控制流。我现在正在审查CS442上有关CPS的笔记,我看到1995年我写下了一个profQUOTE:“继续下去,您必须站着头,将自己拉出来。” 杜根教授(*)的说法是绝对正确的。回想一下前几天,我们对C#表达式M(B()?C():D())进行CPS转换的小例子涉及四个lambda。并非每个人都擅长阅读使用高阶函数的代码。它施加了很大的认知负担。
此外:将特定的控制流语句嵌入语言中的好处之一是,它们使您的代码在隐藏机制的同时清晰地表达了控制流的含义-调用堆栈和返回地址以及异常处理程序列表和受保护区域等等。连续性使控制流的机制在代码的结构中明确。对机制的所有强调可能会使代码的含义不知所措。
在接下来的文章中,他解释了异步和延续是如何完全等同于彼此,越过采取简单的,(但阻塞)同步网络操作和asyncronous风格重写它的演示,并确保覆盖所有的隐藏必须正确处理的陷阱。它变成了大量的代码。最后他的总结:
天哪,我们制造了多么可怕的混乱。我们将两行完全清晰的代码扩展为您所见过的十二行最令人难以置信的意大利面。当然,它仍然无法编译,因为标签不在范围内,并且我们有明确的赋值错误。我们仍然需要进一步重写代码以解决这些问题。
还记得我在说CPS的利弊吗?
- 优点:可以用简单的零件构建任意复杂而有趣的控制流程-检查。
- 缺点:很难通过连续性来确定控制流,很难推理。
- 缺点:代表控制流机制的代码完全淹没了代码的含义–检查。
- 缺点:将普通代码控制流转换为CPS是编译器擅长的事情,几乎没有人可以检查。
这不是一些智力练习。现实中的人们在处理异步问题时,总是会在道德上等同于上面的代码。而且,具有讽刺意味的是,即使处理器变得越来越快,越来越便宜,我们也花费越来越多的时间等待不受处理器限制的事物。在许多程序中,处理器花费的大部分时间都固定为零,以等待网络数据包从英国到日本再返回,或者磁盘旋转,等等。
而且,我什至都没有谈到如果要进一步编写异步操作会发生什么情况。假设您要使ArchiveDocuments成为接受委托的异步操作。现在所有调用它的代码也必须用CPS编写。污点刚刚蔓延。
正确实现异步逻辑很重要,将来只会变得 越来越重要,正如杜根教授明智地说的那样,我们为您提供的工具可以使您 “站稳脚跟,将自己彻底摆脱困境”。
延续传递样式很强大,是的,但是有一种比上面的代码更好地利用这种力量的更好的方法。
这两篇文章以及有关新的C#语言功能的以下系列文章(将所有这些混乱转移到编译器中,并允许您使用特殊关键字将某些部分标记为异步)作为常规控制流编写代码,即使您不是C#开发人员。我不是,但是当我第一次遇到它时,它仍然是一个很有启发性的体验。