尽管可能没有办法完全按照您的意愿去做,但有一些方法可以将类似的东西组合在一起。一种选择是定义一个新的绑定形式,with-callable,它允许我们将函数本地绑定到可调用对象。例如我们可以使
(with-callable ((x (make-array ...)))
(x ...))
大致相当于
(let ((x (make-array ...)))
(aref x ...))
这是 with-callable 的可能定义:
(defmacro with-callable (bindings &body body)
"For each binding that contains a name and an expression, bind the
name to a local function which will be a callable form of the
value of the expression."
(let ((gensyms (loop for b in bindings collect (gensym))))
`(let ,(loop for (var val) in bindings
for g in gensyms
collect `(,g (make-callable ,val)))
(flet ,(loop for (var val) in bindings
for g in gensyms
collect `(,var (&rest args) (apply ,g args)))
,@body))))
剩下的就是为 make-callable 定义不同的方法,这些方法返回用于访问对象的闭包。例如,这是一个为数组定义它的方法:
(defmethod make-callable ((obj array))
"Make an array callable."
(lambda (&rest indices)
(apply #'aref obj indices)))
由于这种语法有点难看,我们可以使用宏来使它更漂亮。
(defmacro defcallable (type args &body body)
"Define how a callable form of TYPE should get access into it."
`(defmethod make-callable ((,(car args) ,type))
,(format nil "Make a ~A callable." type)
(lambda ,(cdr args) ,@body)))
现在要使数组可调用,我们将使用:
(defcallable array (obj &rest indicies)
(apply #'aref obj indicies))
好多了。我们现在有一个表单,with-callable,它将定义允许我们访问对象的本地函数,以及一个宏,defcallable,它允许我们定义如何制作其他类型的可调用版本。这种策略的一个缺陷是,每次我们想要使对象可调用时,我们都必须显式地使用 with-callable。
另一个类似于可调用对象的选项是 Arc 的结构访问ssyntax。基本上 x.5 访问 x 中索引 5 处的元素。我能够在 Common Lisp 中实现这一点。您可以在此处和此处查看我为它编写的代码。我也对它进行了测试,所以你可以在这里看到使用它的样子。
我的实现是如何工作的,我编写了一个带有 ssyntax 的宏,它查看正文中的所有符号并为其中一些定义宏和符号宏。例如,x.5 的符号宏是 (get x 5),其中 get 是我定义的访问结构的通用函数。这样做的缺陷是我总是必须在任何我想使用 ssyntax 的地方使用 w/ssyntax。幸运的是,我能够将它隐藏在一个类似于 defun 的宏 def 中。