Search code examples
emacslispelispadvising-functions

Add new variable designator for setf?


I think this is possible in Common Lisp (through overloading setf), but not sure about Emacs Lisp.

What I want to do would be something like:

(setf (local variable) value)

where the form (local ...) would call some code I've written and return the symbol to use for setting its value slot.

EDIT: From looking at gv.el it seems like advising gv-get would do the job, but maybe there's a better procedure for it?

EDIT2: Tried the suggestion from Stefan, close, but not good :(

(gv-define-setter local (value varname &optional buffer)
  `(with-current-buffer (or ,buffer (current-buffer))
     (setq ,varname ,value)))

which expands to:

(let* ((v bar))
 (with-current-buffer (or nil (current-buffer))
  (setq v 42)))

Obviously, not what I wanted. I was trying to do it with advising as mentioned above and with gv-define-expander, but there's just too much quoting going on, and I can't find a way to loose the let part of the expression with the redundant substitution.

EDIT3:

OK, unless I messed something seriously in the scope or the evaluation sequence of the value the variable will be set to, this seems to work:

(gv-define-expander local
  (lambda (do &rest args)
    (let ((varname (first args))
          (buffer (or (second args) '(current-buffer)))
          (value (funcall do nil #'identity)))
      `(with-current-buffer ,buffer
         (setq ,varname ,value)))))

Solution

  • There's gv-define-expander, gv-define-setter, and gv-define-simple-setter (in order of growing simplicity of use).

    You might want to try the 100% guaranteed untested code (note how I pass the with-current-buffer+setq to do rather than calling do with dummy arguments, so that this things has a chance to work when used with push).

    (gv-define-expander local
      (lambda (do &rest varname &optional buffer)
        (macroexp-let2 nil b buffer
          (funcall do `(buffer-local-value ',varname ,b)
                   (lambda (value)
                    `(with-current-buffer ,b
                       (setq ,varname ,value)))))))