没有自由变量的语言

Language without free variables

阅读 "Why the FUNARG Problem Should be Called the Environment Problem" 之后我问了自己一个问题:如果我们禁止定义一个无法创建闭包的函数,会发生什么,即

(define (foo x) (+ a x))

不会在顶层允许(因为没有环境可以关闭过来,包含a),而

(define (bar a) (lambda (x) (+ a x))) 

是允许的,因为返回的 lambda 可以创建闭包。 这里有两个问题:
1. 这会如何影响语言的表达能力?是否有某些功能因此类限制而变得不可用?有人可以举个例子,用自由变量定义函数时有用吗?
2. 这是否意味着闭包中的所有环境变量现在都具有静态和可预测的偏移量?

正如 Will Nesstfb 所说,存在一个全球环境。在您的表达式中, +a 都是自由变量。重要的是当您定义 f 时,a 是否存在于您的全局环境中。 此外,"free"或"bound"是一个相对概念。

引用未在任何环境中绑定的词法变量没有什么意义。我能想到的唯一示例与元编程相关,但这并不真正相关,因为在那种情况下您只是将代码作为数据进行操作。当你最终生成一个表单并编译或评估它时,你仍然有一个词法范围,然后你的所有符号都必须解析为一个已知变量。 使用 Common Lisp 中的 special 变量,或 Emacs 默认动态作用域变量,引用自由变量是有意义的。在 Emacs 中,您甚至可以在没有警告的情况下引用未声明的变量。

How this may affect the expressiveness of the language?

因此,如果您不允许自由变量(未绑定在任何词法范围内),您基本上就是不允许具有动态范围的变量(在 Common Lisp 中,它们被定义为具有 无限范围 动态范围)。你失去了表达能力。例如,OCaml 就是这种情况。但是,您仍然可以定义一个库来模拟它们,如 Delimited Dynamic Binding and its implementation.

中所示

OCaml 提供了一个 hyper-static global environment,它不仅使用词法范围而且禁止更改现有绑定。

# let a = 10;;
val a : int = 10

# let f () = a;;
val f : unit -> int = <fun>

# let a = 20;;
val a : int = 20

# f ()
- : int = 10

这里上面的第二个 a 隐藏了前一个,但是 f 仍然指的是前一个 afunctions也是如此,这就是为什么有rec关键字来定义递归和互递归函数的原因。 这是一种不同于 Lisp 的方法,值得注意的是它允许在运行时重新定义大多数东西。

Does this mean that all environment variables in closures have static and predictable offsets now?

词法作用域允许将变量编译到固定位置。这是否完成取决于您的工具。例如,在解释器中,您的环境可能保存在运行时数据结构中。