Pau*_*omé 20 lisp scheme continuations callcc common-lisp
最近,我一直在调查Scheme和Common Lisp之间关于这两种语言对延续的方法的区别.
我注意到Common Lisp方法比Scheme方法更保守.
此外,Scheme提供了一个原始的call-with-current-continuation,通常是缩写的call/cc,它在ANSI Common Lisp规范中没有等价物(尽管有一些库试图实现它们).
有没有人知道为什么决定不在ANSI Common Lisp规范中创建类似的原语?
提前致谢.
Kaz*_*Kaz 22
Common Lisp有一个详细的文件编译模型作为标准语言的一部分.该模型支持在一个环境中将程序编译为目标文件,并将它们加载到另一个环境中的图像中.Scheme没有可比性.不eval-when,或者compile-file,load-time-value或者像什么是可外化对象的概念,编译代码中的语义必须与解释代码一致.Lisp有一种方法可以内联或不内联函数,因此基本上你可以非常精确地控制重新加载编译模块时会发生什么.
相比之下,在最近对Scheme计划进行修订之前,Scheme语言对于如何将Scheme程序分解为多个文件的主题完全保持沉默.没有为此提供任何功能或宏.查看6.6.4系统接口下的R5RS .你所拥有的只是一个非常松散定义的load函数:
可选程序:(加载文件名)
Filename应该是一个字符串,用于命名包含Scheme源代码的现有文件.加载过程从文件中读取表达式和定义,并按顺序对它们进行求值.未指定是否打印表达式的结果.加载过程不会影响current-input-port和current-output-port返回的值.Load返回未指定的值.
基本原理:为了便于携带,加载必须对源文件进行操作.它对其他类型文件的操作必然因实现而异.
因此,如果这是关于如何从模块构建应用程序的愿景的范围,并且除此之外的所有细节都留给实现者来解决,当然天空是发明编程语言语义的限制.请注意部分基本原理部分:如果load定义为对源文件进行操作(其他所有内容都是实现者的奖励)那么它只不过是像#includeC语言一样的文本包含机制,因此Scheme应用程序确实如此只是一个文本体,它被物理地分散到多个文本文件中load.
如果您正在考虑向Common Lisp添加任何功能,您必须考虑它如何适应其详细的动态加载和编译模型,同时保留用户期望的良好性能.
如果您正在考虑的功能需要全局,整个程序优化(系统需要查看所有内容的结构源代码),以便用户的程序运行不良(特别是不使用该功能的程序) )然后它不会真的飞.
特别是关于延续的语义,存在问题.在块作用域的通常语义中,一旦我们离开作用域并执行清理,就会消失; 我们不能及时回到那个范围并恢复计算.Common Lisp就是普通的.我们有一个unwind-protect构造,当一个作用域终止时,它会执行无条件的清理操作.这是为with-open-file块作用域提供打开文件句柄对象的功能的基础,并确保无论块作用域如何终止都关闭它.如果延续从该范围中逃脱,则该延续不再具有有效文件.我们不能简单地不是当我们离开的范围,因为没有保证,将延续以往任何时候都可以使用关闭文件; 也就是说,我们必须假设范围实际上是永远被放弃并及时清理资源.这种问题的创可贴解决方案是dynamic-wind,允许我们在进入和退出块范围时添加处理程序.因此,当通过延续重新启动块时,我们可以重新打开该文件.而且不仅重新打开它,而且实际上将流定位在文件中的完全相同的位置,依此类推.如果流是解码某些UTF-8字符的一半,我们必须将它置于相同的状态.因此,如果Lisp得到延续,要么它们会被with-执行清理(差集成)的各种构造打破,否则这些构造必须获得更多毛茸茸的语义.
有继续的替代方案.连续性的一些用法是非必要的.通过闭包或重启可以获得基本相同的代码组织.此外,还有一个强大的语言/操作系统结构可以与延续竞争:即线程.虽然continuation的方面没有被线程很好地建模(更不用说它们没有在代码中引入死锁和竞争条件),但与线程相比它们也有缺点:比如缺乏使用多个处理器的实际并发性,或者优先级.用延续表达的许多问题几乎可以用线程表达.例如,continuation让我们编写一个递归下降的解析器,它看起来像一个类似于流的对象,它在解析时只返回渐进结果.代码实际上是递归下降解析器,而不是模拟一个的状态机.线程让我们做同样的事情:我们可以将解析器放入一个包含在"活动对象"中的线程中,该线程有一些"获取下一个东西"的方法从队列中提取东西.作为线程解析器,它不是返回延续,而是将对象抛出到队列中(并且可能阻塞其他线程以删除它们).通过恢复该线程来提供继续执行; 它的线程上下文是延续.并非所有线程模型都受到竞争条件的影响(同样多); 例如,协作线程,一次运行一个线程,并且只有当线程对线程内核进行显式调用时才可能发生线程切换.几十年来,主要的Common Lisp实现都有轻量级线程(通常称为"进程"),并逐渐转向更复杂的线程,并支持多处理.对线程的支持减少了对continuation的需求,并且是一个更大的实现优先级,因为没有线程支持的语言运行时间处于技术劣势:无法充分利用硬件资源.
Common Lisp 是对多种实用(应用)Lisp(因此称为“Common”)进行标准化努力的结果。CL 面向现实生活中的应用,因此它handler-bind比call/cc.
call/ccScheme被设计为用于CS教学的小型干净语言,因此它具有可用于实现其他工具的基础。
另请参见call-with-current-continuation 只能使用 lambda 和闭包实现吗?