I've noticed an interesting difference between WITH-SLOTS
and WITH-ACCESSORS
in Common Lisp:
WITH-SLOTS
allows a shorthand syntax:
(with-slots (slot1 (var-name slot2)) instance
...)
But WITH-ACCESSORS
always requires explicit variable names:
(with-accessors ((var-name accessor-name)) instance
...)
I'm wondering about the rationale behind this design choice.
Since both macros are intended to reduce boilerplate, wouldn't it be convenient it this was also allowed:
(with-accessors (accessor1 accessor2) instance
...)
Anyone knows why Common Lisp chose not to support the shorthand syntax forWITH-ACCESSORS
? Or was there a practical or historical context?
And actually, I think a quick macro would improve this, which make me wonder why the CLOS folks avoided this (as it shouldn't affect backwards compatibility AFAICT)
(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) instance
...)
The "Object-Oriented Programming in Common Lisp" book by Keene briefly says (page 74):
[About WITH-ACCESSORS] Although you can specify that the variable should be the same symbol as the accessor, there is no brief syntax for it; you always have to list both the variable and the accessor names.
The WITH-SLOTS macro does have a brief syntax: You list the slots you want to access, and then you access them by their names.
Curious to hear your thoughts!
There is a possibility that this was an overlooked part of the standard, though it is hard to tell for sure now.
I agree that there are many libraries whose classes define accessors using a name similar to the slots (e.g. %foo
slot and foo
accessor).
In that case it does feel redundant to have the following in your code:
(with-accessors ((x x) (y y)) p ...)
Thanks to the macro capabilities of Lisp, you can write your own macro, as you did, to have a shorter syntax.
Personally when this happens, I directly write function calls, (x p)
and that's good enough, because I do not gain much when using WITH-ACCESSORS
here. An exception to this case would be when I am working with more than one instance at the same time, in which case I want distinct names anyway (e.g. xa
for point a
and xb
for point b
); the same applies when using with-slots
here.
More generally, I think accessors are named like functions, which can make it weird when using the name of the function as a variable (especially if the accessors are named like y-of
where the subject is important). This is different from slot names, which are more easily usable as variables to me.
Finally, accessor names are typically longer, and I think that the intent of with-accessors
is to introduce (relatively) short names to the result of applying long function calls:
(with-accessors ((methods c2mop:generic-function-methods)) function
...)
In that regard, maybe the original writers did not expect the shorter syntax to be useful, but it's only a guess.