为什么Scheme不支持一级环境?

19

我一直在阅读SICP(计算机程序的构造和解释),很高兴发现这个精彩的特殊形式:“make-environment”,他们在“包”章节4.3的摘录中展示了如何与eval结合使用,作为编写模块化代码的一种方式。

(define scientific-library
  (make-environment
   ...
   (define (square-root x)
    ...)))

他们随后演示了它的工作原理。
((eval 'square-root scientific-library) 4)

在他们的示例中,他们继续展示了我想要的精简、优雅的“OO”风格的Scheme使用方式...他们将一个“类型”(实际上是由“make-environment”特殊形式返回的vtable)和一个参数(“状态”)“cons”在一起...

我非常兴奋,因为这正是我一直在寻找的,在Scheme中通过符号执行多态调度的方法,而不必编写大量显式代码或宏。

也就是说,我想创建一个“对象”,它有两个函数,我在不同的上下文中调用它们...但我不想用“car”和“cdr”来引用它们,我想通过它们的符号名称同时进行声明和评估。

无论如何,当我读到这篇文章时,我迫不及待地想回家尝试一下。

然而,当我在PLT Scheme和Chez Scheme中都遇到以下问题时,我感到非常失望:

> (make-environment (define x 3))
Error: invalid context for definition (define x 3).
> (make-environment)
Error: variable make-environment is not bound.

在SICP中提到的"make-environment"发生了什么?它看起来非常优雅,正是我想要的,但似乎没有任何现代Scheme解释器支持它?

这是什么原因?仅仅是因为"make-environment"有不同的名称吗?

稍后发现更多信息

我查看了在线版本:

https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/full-text/book/book-Z-H-28.html#%_sec_4.3

我正在阅读SICP的第一版。第二版似乎用一个关于非确定性编程和"amp"运算符的部分替换了有关软件包的讨论。
4个回答

13

经过更多的调查,我发现了这个信息丰富的帖子

"R5RS EVAL 和环境说明符是在那些强烈反对一级环境并希望有受限 EVAL 的人们,以及那些无法接受/理解没有第二个参数即环境的 EVAL 的人们之间的折衷方案。"

此外,还找到了这个“解决方法”:

(define-syntax make-environment 
  (syntax-rules () 
    ((_ definition ...) 
     (let ((environment (scheme-report-environment 5))) 
       (eval '(begin definition 
                     ...) 
             environment) 
       environment)))) 


(define arctic 
  (make-environment 
    (define animal 'polarbaer))) 

(来自这里

然而,最终我采用了一种类似第一个人建议的“消息传递”风格——我返回一组函数的alist,并具有通用的“发送”方法以通过名称调用特定的函数...即像这样:

(define multiply
  (list
    (cons 'differentiate (...))
    (cons 'evaluate (lambda (args) (apply * args)))))

(define lookup
  (lambda (name dict)
    (cdr (assoc name dict))))

; Lookup the method on the object and invoke it
(define send
  (lambda (method arg args)
    ((lookup method arg) args)))

((send 'evaluate multiply) args)

我继续阅读了一些资料,意识到如果我真的想采用完全面向对象的风格,可以使用全部CLOS - 但我认为即使上面的也有点过头了。


1
make-environment宏非常好,但我仍然缺少一件事:我希望有一种方法来验证一个符号是否在环境中绑定,只使用标准的R5RS,并且据我所知,这是不可能的,除非使用丑陋的技巧,比如存储一个定义名称的表等。 - Jay
1
哦,我刚刚注意到在Scheme实现中,make-environment技巧可能有效也可能无效:scheme-report-environment可能是不可变的,并且宏内部的LET可能不会复制环境。似乎没有一种便携式的方式来创建Scheme环境(至少在R5RS中没有)。 - Jay

5
他们这样写是因为MIT Scheme确实具有一流环境,而且很可能这就是作者计划用来教授他们的课程的(因为这本书是在MIT编写的)。
请查看http://groups.csail.mit.edu/mac/projects/scheme/ 但是,我注意到MIT Scheme虽然仍在积极开发中,但缺少许多现代Scheme应该具备的功能,如外部函数接口或GUI支持。你可能不想仅使用它进行严肃的软件开发项目。

4

Scheme没有一流的环境,这是因为性能问题。当Scheme被创建时,它并不是最快的语言,因为有一些花哨的东西,比如一流函数、续延等等。添加一流的环境会进一步降低性能。所以这是在早期Scheme时期做出的权衡。


4
请问您有相关的参考资料吗? - mnicky

1

一个经典的调度函数会起作用吗?我认为这与你所寻找的类似。

(define (scientific-library f)
  (define (scientific-square-root x) (some-scientific-square-root x))
  (cond ((eq? f 'square-root) scientific-square-root)
        (else (error "no such function" f))))
(define (fast-library f)
  (define (fast-square-root x) (some-fast-square-root x))
  (cond ((eq? f 'square-root) fast-square-root)
        (else (error "no such function" f))))

((scientific-library 'square-root) 23)
((fast-library 'square-root) 23)

你甚至可以将这些科学和快速库的示例组合成一个大的调度方法:

(define (library l f)
  (define (scientific-library f)
    ...)
  (define (fast-library f)
    ...)
  (cond ((eq? l 'scientific) (scientific-library f))
        ((eq? l 'fast) (fast-library f))
        (else (error "no such library" l))))
(library 'fast 'square-root)

是的,但写上述内容太冗长繁琐了! - Paul Hollingsworth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接