ooplispcommon-lispclos

Common Lisp: Why does `WITH-SLOTS` allow shorthand names, but `WITH-ACCESSORS` doesn't?


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!


Solution

  • 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.