在 Common Lisp 的插槽说明符中使用和不使用 `:` 是否有技术原因?或者它只是一个纯粹的惯例?

Is there a technical reason for the use and absence of `:` in Common Lisp's slot specifiers? Or is it just a pure convention?

我正在阅读 Sonja Keene 的书 Common Lisp 中的面向对象编程

在第 9 章中,作者给出了以下示例:

(defclass window ()
    ((x :initarg :x-position :accessor x-position)
     (y :initarg :y-position :accessor y-position)
     (height :initarg :height :accessor window-height)
     (width :initarg :width :accessor window-width)
     (exposed-p :initform nil :accessor exposed-p))
   (:documentation "Foundation of all windows"))

我知道在谈论 packages 时,::: 的使用或它的缺失超出了 Common Lisp 中的纯符号。 其使用背后有一个逻辑

但是,我看不出在插槽 options/specifiers 上使用 : 背后的逻辑。例如,考虑上面的例子。

看第一个插槽,x。存在与 :x-position 关联的插槽选项 :initarg。好的,两者都包括 :.

现在,在同一个插槽上,查看 :accessor 插槽选项。它包括双点 :。但是,与之关联的 x-position 包括 :.

这个语法背后有什么逻辑吗?

我最好的假设是语法设计是用来反映处理实例时的差异。因此,没有双点表示使用与 pos-initialization 相关,包含双点表示正在进行的初始化过程:

CL-USER> (x-position  (make-instance 'window :x-position 3))
3

还有什么事吗?我错过了什么?是纯粹的语法约定,我说的只是巧合吗?

谢谢

:accessor选项用于定义方法名。函数名称应始终位于应用程序的包中,这样您就不会在定义同名插槽的两个包之间发生冲突。

如果你使用关键字:x-position,而其他一些包做同样的事情,它们会相互冲突。

记住,单个冒号表示符号 :bar

  • 是关键字
  • 并且在关键字包中
  • 并从关键字包中导出
  • 并且具有自身的常量值 (-> :bar)。

尝试

(defclass foo ()
  ((x
    :initarg   x-position
    :accessor  x-position)))

* (make-instance 'foo 'x-position 3)
#<FOO {7007741DD3}>

* (x-position *)
3

(defclass foo ()
  ((x
    :initarg   x-position
    :accessor :x-position)))

* (make-instance 'foo 'x-position 4)
#<FOO {700778D473}>
* (:x-position *)
4

初始化参数是函数make-instance(及相关)的关键字参数。访问器是函数的名称和setf 访问器的名称.

可以使 initarg 成为任何符号,而不是关键字符号。它只是一个普通的关键字,因为函数关键字通常带有关键字符号。

也可以尝试使访问器名称成为一个符号,而不是具体的关键字符号。一个实现可能支持也可能不支持。这意味着实现需要支持关键字作为函数名称。

风格与惯例

通常我们把initarg选项写成关键字符号,accessor选项写成非关键字符号。

正如 Barmar 所解释的那样,使用关键字符号定义访问器函数将是冲突的根源。访问器函数是通用函数,因此在不同情况下重复使用相同的符号似乎仍然有效。例如,两个不同的不相关的 classes c1c2:x-position 定义为访问器可以如下使用,对于 [=36 的任何对象 o =] c1c2:

(:x-position o)

唯一通用函数 :x-position 的动态调度会起作用。但是由于只有一个泛型函数,所以该泛型函数的其他方法或属性也被共享,即使这是不需要的。这包括例如泛型函数的方法组合机制、:around 方法等。此外,如果任何库为标准类型(如数字的 :x-position)定义了方法,则行为是共享的与其他图书馆。 这就是访问函数总是被定义为普通包符号的原因。

另一方面,:initarg 参数不会出现同样的问题。两个不相关的 class 具有相同的 :x-position initarg 作为它们各自的插槽之一不是问题,因为每个关键字的使用上下文不同。

调用 make-instance 时,代码会分派到使用不同 initargs 的特定初始化代码。关键字 initarg 可能成为问题的唯一情况是 class 继承自两个依赖相同关键字的超级 classes,如以下示例所示:

USER> (defclass foo () ((x :initarg :x)))                                                                                                                                                     
#<STANDARD-CLASS FOO>                                                                                                                                                            

USER> (defclass bar () ((z :initarg :x)))                                                                                                                                                     
#<STANDARD-CLASS BAR>                                                                                                                                                            

USER> (defclass zot (foo bar) ())                                                                                                                                                             
#<STANDARD-CLASS ZOT>                                                                                                                                                            

USER> (make-instance 'zot :x 10)                                                                                                                                                              
#<ZOT {10252705D3}>                                                                                                                                                                           

USER> (describe *)                                                                                                                                                                            
#<ZOT {10252705D3}>                                                                                                                                                                           
  [standard-object]                                                                                                                                                                           
                                                                                                                                                                                              
Slots with :INSTANCE allocation:                                                                                                                                                              
  Z                              = 10                                                                                                                                                         
  X                              = 10                                                                                                                                                         
; No values                                                                                                                                                                                   

这里上面,同样的:xinitarg被用来初始化两个slot。幸运的是,如果这是一个很少见的问题,可以通过向插槽添加更多 initargs 来缓解:

USER> (defclass zot () ((x :initarg :foo-x) (z :initarg :bar-x)))                                                                                                                             
#<STANDARD-CLASS ZOT> 
                                                                                                                                                           
USER> (describe (make-instance 'zot :foo-x 5 :bar-x 10))                                                                                                                                      
#<ZOT {10253A5373}>                                                                                                                                                                           
  [standard-object]                                                                                                                                                                           
                                                                                                                                                                                              
Slots with :INSTANCE allocation:                                                                                                                                                              
  X                              = 5                                                                                                                                                          
  Z                              = 10