Common Lisp 中的 lambda 表达式是宏还是标记?

Are lambda expressions macros or markers in Common Lisp?

我正在尝试通过 Common Lisp:对符号计算的简单介绍 来学习 Common Lisp。此外,我正在使用 SBCL、Emacs 和 Slime。

在第 7 章中,作者对 lambda expressions 提出了以下建议:

这让我感到困惑,因为 SBCL 的 REPL returns:

CL-USER> #'lambda
#<CLOSURE (:MACRO LAMBDA) {1000D736DB}>

显然,作者使用了 Lisp Works(不是 100% 确定)。我认为这与上述差异无关。不过,我觉得还是提一下比较好。

我的 SBCL 的 REPL 也 returns macro 用于众所周知的宏,例如 and:

CL-USER> #'and
#<CLOSURE (:MACRO AND) {1000D7365B}>

请注意,append 等“普通”函数的行为是不同的:

CL-USER> #'append
#<FUNCTION APPEND>

这个posthere似乎稍微触及了lambda表达式的非单一性。然而,它没有提及任何关于标记的事情。

我是不是漏掉了 lambda 表达式的本质?

考虑这一点的一种方法是认识到在 CL 中获得词法上明显的函数值的唯一方法是特殊运算符 function:如果你想获得与 foo 在当前的词法环境中你必须说 (function foo):

(flet ((foo (x)
         x))
  (function foo)

比如。所以 function 是一个特殊的运算符,它可以让你看到函数命名空间中的内容。 function 有一些语法糖,即 #',与 quote 的用法相同(我不会在下面使用它)。您可以认为函数应用程序 (f x y) 大致类似于 (<funcall> (function f) x y),其中 <funcall> 是一些神奇的东西,它不会像那样被替换。

但是您还需要匿名函数,而且,您也可以使用 function,其参数是 lambda 表达式:

lambda expression n. a list which can be used in place of a function name in certain contexts to denote a function by directly describing its behavior rather than indirectly by referring to the name of an established function; its name derives from the fact that its first element is the symbol lambda. – CLHS

所以匿名函数表示为(function (lambda (...) ...))。如果愿意,您可以将 (lambda (...) ...) 视为函数的 'name'。 (我认为人们不喜欢这个,因为它干扰了匿名函数的想法,但很明显有一组可数的 (lambda (...) ...) 形式的可能函数 'names' 你甚至可以枚举我认为这个集合(这样做会像枚举有理数一样繁琐)。

由于函数应用程序中的函数位置已经在函数的命名空间中进行了解释,因此 ((lambda (...) ...) ...) 表示匿名函数应用程序:它或多或少与 (funcall (function (lambda (...) ...) ...) 相同(请参阅 CLHS 再次)。

这就是 CL 在 1980 年代某个时候的样子。

然后有一些我不确定的历史混淆:我记得它发生但我不记得顺序。首先,在 Lisp-1 中你不需要任何 function 东西:形式 car 表示获取 car 的函数缺点,不用说(function car)。同样 (lambda (...) ...) 表示一个函数。除此之外还有 was/is 另一个提议的 Lisp 标准,was/is ISLisp,可能是 here。尽管 ISLisp 不是 Lisp-1,但它确实有一种表示函数的形式 (lambda (...) ...)

人们希望 CL 能够与 ISLisp 兼容,这意味着 (lambda (...) ...) 应该表示一个函数。每个人实际上 所做的 是偷偷添加一个定义,如:

(defmacro lambda (args &body forms)
  `(function (lambda ,args ,@forms)))

但是,至关重要的是,您不能在 CL 中以可移植的方式执行此操作,因为 lambdacl:lambda 并且您不允许在 CL 包中重新定义内容。人们还是这样做了,但结果是他们的程序不可移植,而且经常不得不用特殊的魔法装饰来解锁和重新锁定 CL 包。

好吧,解决方案是语言 必须提供这样一个宏。在 CLtL1 定义的语言和最终标准之间的某个时候,这发生了,所以现在 lambda 有一个宏定义,其扩展是(或多或少:实现可能被允许做特殊的事情)明显的扩展:

(lambda (x) x)
 -> #'(lambda (x) x)

所以在现代 CL 中:

  • lambda开头的列表表示('names')形式为(function (lambda (...) ...)的函数并且也在形式的函数位置,所以((lambda (x) x) 1),说.
  • lambda 被定义为扩展为 (function (lambda (...) ...)) 的宏,这样更易​​于使用。

注意:我认为您正在使用的实现允许执行它对宏所做的操作,但您不应该依赖于此:

It is an error to use function on a function name that does not denote a function in the lexical environment in which the function form appears. Specifically, it is an error to use function on a symbol that denotes a macro or special form. An implementation may choose not to signal this error for performance reasons, but implementations are forbidden from defining the failure to signal an error as a useful behavior. – CLHS

你真的应该使用 macro-function 它会告诉你是否有宏定义。