我试图评估这个:(define lambda (lambda (x) x))
。MIT方案11.2给出了一个错误:;Unbound variable: x
。Chez方案9.5也给出了一个错误:Exception: variable x is not bound
。为什么x
没有绑定?我认为define
会将(lambda (x) x)
求值为一个匿名函数,然后将lambda
定义为该匿名函数。x
在哪里参与?
我在Racket 7.2和Guile 3.0.1中没有任何错误。
这不是有效的方案代码:
(define lambda (lambda (x) x))
R7RS在第5.4节关于语法定义的中似乎非常清楚地禁止了这一点
但是,定义一个标识符是错误的,该标识符的绑定必须是已知的,以便确定定义本身或属于同一组内部定义的任何先前定义的含义。
在发布的代码中,标识符lambda
被重新定义,但必须知道lambda
本身的绑定,才能确定新定义的含义;上述语言禁止这样做。
R6RS在关于扩展过程的第10章中有一些类似的语言,但R6RS语言与扩展过程的技术细节结合得更紧密。我非常确信它也适用于这种情况,但不是100%确定。
表单序列中的定义不得定义任何标识符,其绑定用于确定定义的未错误部分或表单序列中位于其之前的任何定义的含义。
OP注意到错误消息Exception: variable x is not bound
并询问:">
考虑到(define lambda (lambda (x) x))
格式不正确,因此不是有效的Scheme,尝试解释这种行为是没有意义的。然而,这种行为似乎可以通过尝试以类似的方式重新定义其他句法关键字来触发。考虑:
> (define if (if x y z))
Exception: variable z is not bound
这里似乎很明显z
是未绑定的,所以这个错误似乎并不合理。但现在:
> (define if (if #t 1 2))
Exception: variable if is not bound
即使是if
在define
形式的右侧表达式中也是未绑定的!如果我们假设类似的事情正在(define lambda (lambda (x) x))
中发生,那么lambda
在右侧表达式中是未绑定的,如果是这样的话,那么(lambda (x) x)
不是,而是作为一个特殊形式,而是作为普通过程调用进行评估。过程调用中参数的求值顺序是未指定的,因此在这种情况下,x
可以在lambda
之前报告为未绑定是完全合理的。我们可以去掉未结合的x
s的出现,看看lambda
是否被结合:
> (define lambda (lambda))
Exception: variable lambda is not bound
> (define lambda lambda)
Exception: variable lambda is not bound
因此,这里的问题似乎是,试图使用一个依赖于该关键字含义的表达式来重新定义语法关键字,可能会导致语法关键字的绑定变得不可访问。
在任何情况下,都要对最后一点持保留态度,因为最终这种重新定义都不是有效的代码,不应该期望它有任何特定的行为