Common Lisp 中的“+”是如何使用 Special Operators/Forms 实现的

How is "+" Implemented in Common Lisp Using Special Operators/Forms

On Lisp(第9页)中,可以找到以下声明:

Functions are the building-blocks of Lisp programs. They are also the building-blocks of Lisp. In most languages the + operator is something quite different from user-defined functions. But Lisp has a single model, function application, to describe all the computation done by a program. The Lisp + operator is a function, just like the ones you can define yourself. In fact, except for a small number of operators called special forms, the core of Lisp is a collection of Lisp functions. What’s to stop you from adding to this collection? Nothing at all: if you think of something you wish Lisp could do, you can write it yourself, and your new function will be treated just like the built-in ones.

我的问题是 + 运算符之类的东西究竟如何使用以下特殊运算符来实现?还是实际上使用了更多的运算符,而 Graham 只是不够精确和戏剧化?

block      let*                  return-from      
catch      load-time-value       setq             
eval-when  locally               symbol-macrolet  
flet       macrolet              tagbody          
function   multiple-value-call   the              
go         multiple-value-prog1  throw            
if         progn                 unwind-protect   
labels     progv                                  
let        quote    

有没有办法查看这些函数的源代码?

这是 src/code/numbers.lisp

中 sbcl 的来源
(macrolet ((define-arith (op init doc)
             `(defun ,op (&rest numbers)
                (declare (explicit-check))
                ,doc
                (if numbers
                    (let ((result (the number (fast-&rest-nth 0 numbers))))
                      (do-rest-arg ((n) numbers 1 result)
                        (setq result (,op result n))))
                    ,init))))
  (define-arith + 0
    "Return the sum of its arguments. With no args, returns 0.")
  (define-arith * 1
    "Return the product of its arguments. With no args, returns 1."))

他并不是说每个功能都是根据这些特殊形式实现的。

他说 +(和其他函数一样)是一个函数:

  • 可以使用正常语法 (+ x y z) 调用它(其中 + 是函数,xyz 是参数)。
  • 将首先评估参数:(f (g) (h)) 将在调用 f 之前同时调用 gh,即使 f 恰好是 +.
  • 您可以像任何其他函数一样传递它并通过 funcallapply 调用它:(let ((x #'+)) (funcall x 1 2 3)) 是 6。

重点是这些属性不一定适用于特殊形式。 if 不会首先评估其所有参数;您不能引用 let 并间接调用它;等等

当然,这仍然为 "compiler magic" 敞开大门。在某些时候 + 必须执行依赖于例如关于如何实施数字。根据您的 Lisp 编译器,细节看起来会有所不同。

在实际层面上,您观察到仅使用核心特殊形式而不使用任何算术感知的低级实用程序(因为 CPU 往往是算术很好

Graham 的观点,我相信,更多的是 Common Lisp 中的 + 函数没有任何固有的东西会阻止您自己定义它。在大多数语言中(C++ 是另一个例外),+ 将以与用户定义函数根本不同的方式实现。两个常见的原因是因为 + 通常使用中缀表示法,而函数调用使用 func(arg1, arg2) 表示法;并且通常不可能使用加号等随机符号来定义新的用户创建函数。

另一方面,Common Lisp 没有这个限制。 + 使用与任何其他函数相同的句法结构,因此该原因不适用;同样,Common Lisp 允许您在函数名称中使用许多不同的字符。

例如,这在 GNU clisp 中工作正常(有警告),而在 JavaScript:[=18= 中不可能做同样的事情]

(defun + (a b) (- a (- 0 b))) ; using '- to avoid having to look up other forms

cl:+ 只是表现得像一个函数,并且被当作函数对待。

您不必自己定义它,也可以只使用其他已定义的 CL 标准运算符来定义它。 cl:+ 的定义很可能使用特定于实现的内部功能。

Common Lisp 有三种运算符:函数特殊运算符

标准并没有过多说明它们是如何实现的。有理由相信 Common Lisp 实现将具有用于实现标准运算符的额外内部运算符。

  • 有固定数量的 special operators - 虽然 CL 实现可能会提供额外的特殊运算符。特殊运算符不是函数,用户无法定义特殊运算符。

  • CL 定义了一堆标准宏和用户定义新宏的方法。未指定标准宏的定义方式。

  • CL定义了一堆标准函数和用户定义新函数的方式。未指定标准函数的定义方式。

在其他语言中,Algol 是最常见的,APL 可能是最明显的,使用户定义的抽象与语言附带的功能和形式无法区分是非常特别的。

所以我认为你误读了文本。想象一下,您想要制作一个数字库,它可以使用区间来扩展数字的语言概念。您创建一个库并定义一种方法来创建间隔并使您的库在数字和间隔上工作。如果你有一个做数学的库,你可以在库中使用你的 class 并且它会以间隔开箱即用,因为你已经定义了你需要的所有函数。现在在大多数 Algol 语言中 +, /, ... 不是函数而是运算符,它们有特殊的规则,所以即使 C++ 使运算符重载,也没有多少其他语言支持制作 + 适用于您的新间隔类型。解决方案将是显然看起来不像语言自己的原语的函数,因为它们是运算符。在 APL 中,所有原语都有特殊符号,而所有用户定义的都没有,所以所有用户定义的抽象都很容易看到,因为它们不是花哨的符号。

今天我们经常推出 Java、JavaScript、C++ 等的新修订版。它是必需的,因为这些语言没有办法在语言中抽象出它们自己的语法。 Java今天的脚本有 babel,它基本上为您提供了一种将一种语法转换为另一种语法的方法,但它还没有转换一种功能,可以让您从语言中做到这一点。这些是 Lisp 已经存在了几十年的东西,现代语言正在获得 lisp 特性,但有些仍然很少。

我制作了一种 lisp 语言,它与第一个 lisp 一样缺乏数字,因为我对它不感兴趣。我还必须用那种语言制作 99 瓶啤酒。我使用列表和符号并制作和定义 +-zerop。当您查看实际程序时,您看不到它缺少数字。

我喜欢 Guy L. Steele 在 98 发表的关于 extending programming languages 的演讲。他是 Scheme 的原作者,参与过多种语言,包括 Java 和 Common Lisp。