lambda 表达式中的球拍和未绑定标识符,与 r5rs 对比

Racket and unbound identifier in lambda expression, contrast with r5rs

在 DrRacket 中,当我将语言设置为 R5RS 并且 运行 以下代码时:

(lambda (x) z)

它 运行 没有错误并且 returns #<procedure>。这对我来说很有意义; lambda 形式定义了一个过程,其主体尚未被评估,因此返回该过程。

或者,当我使用 Racket 语言方言时,出现以下错误:

z: unbound identifier in module in: z

我不明白为什么 Racket 会产生这个错误。我的意思是,当然我看到 z 是未定义的,但我对评估模型的理解是函数体在函数定义时不被评估。这与 R5RS 结果一致,但与 Racket 结果不一致。 Racket 到底在做什么?是不是在代码体中以某种方式 "peeking" 来查看变量是否被定义?导致这种不同行为的 R5RS 评估模型有何不同?

Racket 中的源文件

#lang racket
(define f (lambda (x) z))

结果:

z: unbound identifier in module in: z

REPL 交互:

Welcome to DrRacket, version 6.1.1 [3m].
Language: racket; memory limit: 128 MB.
> (define f (lambda (x) z))
> 

没有错误。

定义了 z 的源文件:

#lang racket
(define f (lambda (x) z))
(define z 5)

没有错误。

因此,Racket 确保源文件中的所有变量都已定义,而不对 REPL 代码做出相同的保证。我只能认为这是一件好事,因为它可以防止源文件中出现错误。

一个#lang文件是模块。文档中详细描述了如何扩展和评估模块的规范。经过一番挖掘,我发现了这条注释:

No identifier can be imported or defined more than once at any phase level within a single module. Every exported identifier must be imported or defined. No expression can refer to a top-level variable.

最后一句"No expression can refer to a top-level variable."表示必须绑定所有变量

相反,在repl中输入的表达式不是模块,而是"outside"任何模块的表达式。对未绑定变量的引用成为对顶级变量的引用。计算表达式时,将在当前命名空间中查找顶级变量的值。如果在查找时,变量没有关联值,则会发出错误信号。

repl 使用这个复杂的规则是为了允许定义相互递归函数,一次定义一个。

有关 REPL 的更多信息,请参阅: https://gist.github.com/samth/3083053