all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / 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; 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.