* defining a setter function with gv.el @ 2012-08-27 5:56 Ivan Kanis 2012-08-27 12:54 ` Stefan Monnier 0 siblings, 1 reply; 6+ messages in thread From: Ivan Kanis @ 2012-08-27 5:56 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs help Stefan, I am very new to setf. I would like to write a function that provides a place for setf to work on an alist. It would be wonderful if the function also worked as a getter. (setq foo '((oak . tree))) (defun my-function (&optional value) (if value (?) (cdr (assoc 'oak tree)))) (my-function) => tree (my-function 'shrub) foo => '((oak . shrub)) gv.el seems to have what I need to implement the setf portion. It doesn't provide example and I couldn't find doc in the elisp info. Could you, pleas, help me? Take care, -- Ivan Kanis http://ivan.kanis.fr Success is counted sweetest By those who ne'er succeed. To comprehend a nectar Requires sorest need. -- Emily Dickinson ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: defining a setter function with gv.el 2012-08-27 5:56 defining a setter function with gv.el Ivan Kanis @ 2012-08-27 12:54 ` Stefan Monnier 2012-08-27 18:10 ` Ivan Kanis 0 siblings, 1 reply; 6+ messages in thread From: Stefan Monnier @ 2012-08-27 12:54 UTC (permalink / raw) To: Ivan Kanis; +Cc: emacs help > I am very new to setf. I would like to write a function that provides a > place for setf to work on an alist. It would be wonderful if the > function also worked as a getter. The idea of setf is that you can define a getter (foo ARGS) which return the data you want and then you can explain to setf how (setf (foo ARGS) VAL) should work such that after that (foo ARGS) returns VAL. > (defun my-function (&optional value) > (if value > (?) > (cdr (assoc 'oak tree)))) > (my-function) => tree > (my-function 'shrub) > foo => '((oak . shrub)) So I think what you mean is that you want (my-function) => tree (setf (my-function) 'shrub) (my-function) => shrub So the "setf expander" for `my-function' will tell `setf' what (setf (my-function) 'shrub) should macro-expand to. > gv.el seems to have what I need to implement the setf portion. > It doesn't provide example and I couldn't find doc in the elisp info. gv.el itself has plenty of examples, I think. As for Elisp doc, they indeed haven't been updated for it yet, but you might like to look at the CL documentation and then read the beginning of gv.el to understand how they 2 differ. As for you specific question, I suspect that it is answered in the last few lines of gv.el: ;;; Vaguely related definitions that should be moved elsewhere. ;; (defun alist-get (key alist) ;; "Get the value associated to KEY in ALIST." ;; (declare ;; (gv-expander ;; (lambda (do) ;; (macroexp-let2 macroexp-copyable-p k key ;; (gv-letplace (getter setter) alist ;; (macroexp-let2 nil p `(assoc ,k ,getter) ;; (funcall do `(cdr ,p) ;; (lambda (v) ;; `(if ,p (setcdr ,p ,v) ;; ,(funcall setter ;; `(cons (cons ,k ,v) ,getter))))))))))) ;; (cdr (assoc key alist))) If you uncomment the above, then (alist-get KEY ALIST) will do the obvious lookup and (setf (alist-get KEY ALIST) VAL) will do the expected in-place modification of ALIST so that (alist-get KEY ALIST) then returns VAL. Stefan ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: defining a setter function with gv.el 2012-08-27 12:54 ` Stefan Monnier @ 2012-08-27 18:10 ` Ivan Kanis 2012-08-27 20:28 ` Stefan Monnier 0 siblings, 1 reply; 6+ messages in thread From: Ivan Kanis @ 2012-08-27 18:10 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs help Stefan Monnier <monnier@iro.umontreal.ca> wrote: > So I think what you mean is that you want > > (my-function) => tree > (setf (my-function) 'shrub) > (my-function) => shrub Yup exactly. > As for you specific question, I suspect that it is answered in the last > few lines of gv.el: > > ;;; Vaguely related definitions that should be moved elsewhere. > > ;; (defun alist-get (key alist) > ;; "Get the value associated to KEY in ALIST." > ;; (declare > ;; (gv-expander > ;; (lambda (do) > ;; (macroexp-let2 macroexp-copyable-p k key > ;; (gv-letplace (getter setter) alist > ;; (macroexp-let2 nil p `(assoc ,k ,getter) > ;; (funcall do `(cdr ,p) > ;; (lambda (v) > ;; `(if ,p (setcdr ,p ,v) > ;; ,(funcall setter > ;; `(cons (cons ,k ,v) ,getter))))))))))) > ;; (cdr (assoc key alist))) > Good grief, does it have to be so complicated for such a simple need? -- Ivan Kanis http://ivan.kanis.fr O Love, O fire! Once he drew With one long kiss my whole soul through My lips, as sunlight drinketh dew. -- Alfred, Lord Tennyson ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: defining a setter function with gv.el 2012-08-27 18:10 ` Ivan Kanis @ 2012-08-27 20:28 ` Stefan Monnier 2012-08-30 16:05 ` Ivan Kanis 0 siblings, 1 reply; 6+ messages in thread From: Stefan Monnier @ 2012-08-27 20:28 UTC (permalink / raw) To: Ivan Kanis; +Cc: emacs help >> ;; (defun alist-get (key alist) >> ;; "Get the value associated to KEY in ALIST." >> ;; (declare >> ;; (gv-expander >> ;; (lambda (do) >> ;; (macroexp-let2 macroexp-copyable-p k key >> ;; (gv-letplace (getter setter) alist >> ;; (macroexp-let2 nil p `(assoc ,k ,getter) >> ;; (funcall do `(cdr ,p) >> ;; (lambda (v) >> ;; `(if ,p (setcdr ,p ,v) >> ;; ,(funcall setter >> ;; `(cons (cons ,k ,v) ,getter))))))))))) >> ;; (cdr (assoc key alist))) >> > Good grief, does it have to be so complicated for such a simple need? I think so, yes. CL's define-expander-macro would be a bit worse, but otherwise comparable. The expander macro itself above is: (macroexp-let2 macroexp-copyable-p k key (gv-letplace (getter setter) alist (macroexp-let2 nil p `(assoc ,k ,getter) (funcall do `(cdr ,p) (lambda (v) `(if ,p (setcdr ,p ,v) ,(funcall setter `(cons (cons ,k ,v) ,getter))))))))))) and the code we want to generate for a `setf' is along the lines of: (let* ((k KEY) (a ALIST) (p (assoc k (alist-getter a))) (v VAL)) (if p (setcdr p v) (alist-setter a (cons (cons k v) (alist-getter a))))) so it's not too far from the optimum (all those let-binding of KEY to `k' etc... are needed if KEY is an expression that could have side-effects so we need to make sure it's evaluated exactly once). Note that the above expander can be used for things like (push VAL (alist-get KEY ALIST)) as well, where we want to generate code like (let* ((k KEY) (a ALIST) (p (assoc k (alist-getter a))) (v VAL)) (if p (setcdr p (cons v (cdr p))) (alist-setter a (cons (cons k (cons v (cdr p))) (alist-getter a))))) Note that it doesn't work quite right for `letf', where (letf (((alist-get KEY ALIST) VAL)) BODY) gets expanded to: ELISP> (macroexpand '(letf (((alist-get KEY ALIST) VAL)) BODY)) (let* ((p (assoc KEY ALIST)) (vnew VAL) (old (cdr p))) (unwind-protect (progn (if p (setcdr p vnew) (setq ALIST (cons (cons KEY vnew) ALIST))) BODY) (if p (setcdr p old) (setq ALIST (cons (cons KEY old) ALIST))))) So to be correct for `letf' we could use something like: (defun alist-get (key alist) "Get the value associated to KEY in ALIST." (declare (gv-expander (lambda (do) (macroexp-let2 macroexp-copyable-p k key (gv-letplace (getter setter) alist (macroexp-let2 nil p `(assoc ,k ,getter) (funcall do `(cdr ,p) (lambda (v) `(if ,p (setcdr ,p ,v) ,(funcall setter `(cons (setq ,p (cons ,k ,v)) ,getter))))))))))) (cdr (assoc key alist))) But for most uses this extra `setq' would be at best useless and might make the code less efficient (a var that's never `setq'd can be captured into a closure much more cheaply because we can just keep a copy of its value, rather than keeping a reference to the variable itself and looking up its value everytime). -- Stefan ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: defining a setter function with gv.el 2012-08-27 20:28 ` Stefan Monnier @ 2012-08-30 16:05 ` Ivan Kanis 2012-08-31 0:59 ` Stefan Monnier 0 siblings, 1 reply; 6+ messages in thread From: Ivan Kanis @ 2012-08-30 16:05 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs devel Hi Stefan, As I was trying to make sense of it I tried the following in my *scratch* buffer: (setq lexical-binding t) (defun alist-get (key alist) "Get the value associated to KEY in ALIST." (declare (gv-expander (lambda (do) (macroexp-let2 macroexp-copyable-p k key (gv-letplace (getter setter) alist (macroexp-let2 nil p `(assoc ,k ,getter) (funcall do `(cdr ,p) (lambda (v) `(if ,p (setcdr ,p ,v) ,(funcall setter `(cons (cons ,k ,v) ,getter)))))))))))) (setq foo '((bar . foo) (baz . qux))) (alist-get 'bar foo) => "Get the value associated to KEY in ALIST." It's as if everything in declare was not evaluated. Where did I err? Stefan Monnier <monnier@IRO.UMontreal.CA> wrote: > and the code we want to generate for a `setf' is along the lines of: > > (let* ((k KEY) > (a ALIST) > (p (assoc k (alist-getter a))) > (v VAL)) > (if p (setcdr p v) > (alist-setter a (cons (cons k v) (alist-getter a))))) Yes this is what needs to be generated. I don't understand why there are two lambdas in alist-get. There is no map type function so I don't understand what they do. Is there any chance you would uncomment this function so that I can use it in my code? Take care, -- Ivan Kanis http://ivan.kanis.fr History is a gallery of pictures in which there are few original and many copies. -- Alexis de Tocqueville ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: defining a setter function with gv.el 2012-08-30 16:05 ` Ivan Kanis @ 2012-08-31 0:59 ` Stefan Monnier 0 siblings, 0 replies; 6+ messages in thread From: Stefan Monnier @ 2012-08-31 0:59 UTC (permalink / raw) To: Ivan Kanis; +Cc: emacs devel > (setq lexical-binding t) BTW, don't do that: instead, set it as a file-local variable with "-*- lexical-binding: t -*-". > (defun alist-get (key alist) > "Get the value associated to KEY in ALIST." > (declare > (gv-expander > (lambda (do) > (macroexp-let2 macroexp-copyable-p k key > (gv-letplace (getter setter) alist > (macroexp-let2 nil p `(assoc ,k ,getter) > (funcall do `(cdr ,p) > (lambda (v) > `(if ,p (setcdr ,p ,v) > ,(funcall setter > `(cons (cons ,k ,v) ,getter)))))))))))) The (declare ...) form is used to give auxiliary info about the function you're defining (such as info about how to indent it, how Edebug needs to instrument it, or in our case how `setf' or `push' can "set" it). The function itself ignores it. So the definition above is like (defun alist-get (key alist) "Get the value associated to KEY in ALIST.") I.e. you removed the body of the function (which was the last line: (cdr (assoc key alist)) ). > Yes this is what needs to be generated. I don't understand why there > are two lambdas in alist-get. See C-h f gv-define-expander RET and `gv-get'. > Is there any chance you would uncomment this function so that I can use > it in my code? There's a chance, but if you could explain why (push (cons KEY VAL) ALIST) doesn't work for you, that would help, Stefan ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2012-08-31 0:59 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-08-27 5:56 defining a setter function with gv.el Ivan Kanis 2012-08-27 12:54 ` Stefan Monnier 2012-08-27 18:10 ` Ivan Kanis 2012-08-27 20:28 ` Stefan Monnier 2012-08-30 16:05 ` Ivan Kanis 2012-08-31 0:59 ` Stefan Monnier
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.