unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* 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; 4+ 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] 4+ 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; 4+ 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] 4+ 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; 4+ 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] 4+ 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
  0 siblings, 0 replies; 4+ 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] 4+ messages in thread

end of thread, other threads:[~2012-08-27 20:28 UTC | newest]

Thread overview: 4+ 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).