Common Lisp:为何WITH-SLOTS支持简写语法,WITH-ACCESSORS却不支持?
Common Lisp中
WITH-SLOTS与WITH-ACCESSORS的语法差异设计缘由 问题背景
在Common Lisp的CLOS体系中,WITH-SLOTS和WITH-ACCESSORS都是用于简化实例属性访问的宏,但两者存在一处明显的语法差异:
WITH-SLOTS支持简写语法,直接列出槽名即可绑定同名变量,也可按需显式指定变量名:(with-slots (slot1 (var-name slot2)) instance ...)WITH-ACCESSORS则始终要求显式指定变量名与访问器的配对——哪怕变量名和访问器名完全一致,也不能省略任何一项:(with-accessors ((var-name accessor-name)) instance ...)
既然两者的核心目的都是减少样板代码,那WITH-ACCESSORS为何不支持(accessor1 accessor2)这类简写语法?这背后存在哪些设计考量?
设计缘由分析
语义定位的本质差异
槽与访问器在CLOS中的语义定位截然不同:
- 槽是类实例的直接存储单元,每个槽在类定义中是唯一的标识,槽名本身就直接对应实例的某个属性,用槽名作为变量名直观自然,不会产生歧义。
- 访问器是通用函数,它的语义是实现"获取/设置某个逻辑属性"的操作,而非直接指向存储结构。一个访问器可能被多个类共享(通过继承或通用方法定义),甚至访问器的命名常带有函数式前缀(比如
get-、-of),这类名称作为变量名并不符合Lisp的变量命名习惯,显式绑定变量名能让代码逻辑更清晰。
历史设计的明确选择
在Keene所著《Object-Oriented Programming in Common Lisp》第74页有明确提及:
[关于WITH-ACCESSORS] 尽管你可以指定变量与访问器使用相同的符号,但没有对应的简写语法;你必须始终同时列出变量名和访问器名。
WITH-SLOTS宏则具备简写语法:只需列出要访问的槽,即可通过其名称访问它们。
这说明CLOS的设计者刻意让WITH-ACCESSORS保持显式语法,以此强调访问器作为独立通用函数的特性,避免隐含绑定可能带来的混淆。
自定义简化实现
实际上,我们可以通过一个简单的宏实现WITH-ACCESSORS的简写支持,且完全兼容原有语法,不会破坏向后兼容性:
(defmacro with-accessors* (accessors instance &body body) "Simplified WITH-ACCESSORS that supports shorthand (variable names and accessor names identical) or explicit (var accessor) pairs." `(with-accessors ,(mapcar #'(lambda (entry) (if (consp entry) entry (list entry entry))) accessors) ,instance ,@body)) ;; 使用示例:同时支持简写与显式配对 (with-accessors* (accessor1 accessor2 (custom-var custom-accessor)) instance ...)
总结
WITH-SLOTS与WITH-ACCESSORS的语法差异,本质是CLOS对槽(存储单元)和访问器(操作函数)两种不同概念的语义区分。标准库选择让WITH-ACCESSORS保持显式语法,是为了强调访问器的通用函数属性,提升代码的可读性和严谨性;而自定义宏的可行性也说明,这种设计更多是风格与语义考量,而非技术限制。
内容的提问来源于stack exchange,提问作者Sebastian Carlos
相关产品推荐
相关产品推荐

