unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Using funcall on inline functions
@ 2020-12-12 22:49 Eric Abrahamsen
  2020-12-12 23:28 ` Stefan Monnier
  0 siblings, 1 reply; 5+ messages in thread
From: Eric Abrahamsen @ 2020-12-12 22:49 UTC (permalink / raw)
  To: emacs-devel

I might be doing something wrong here, but I'm having trouble using
funcall+inline+general variable. I thought that one of the advantages of
an inline was that they were effectively defined as both macros and
functions.

It's a slightly convoluted situation, bear with me, here's the simplest
I can get it:

(cl-defstruct ebdb-record-cache
  (alt-names nil :type list))

(defclass ebdb-record ()
  ((cache :type ebdb-record-cache))) 

(define-inline ebdb-record-alt-names (record)
  (inline-quote (ebdb-record-cache-alt-names
                 (slot-value,record 'cache))))

(define-inline ebdb-add-to-list (list-var element)
  (inline-quote (when, element
                  (cl-pushnew, element, list-var :test #'equal))))

(let ((listfunc #'ebdb-add-to-list)
      (name-string "Bob's new name"))
  (funcall listfunc
           (ebdb-record-alt-names <some-record>)
           name-string))

Maybe this a bit overboard in terms of indirection, but it ought to
work, yes? What happens is that the funcall form returns the correct
list (the contents of the record's cache's alt-names slot, with the
name-string added) but the cache's actual slot value is not altered.

It wouldn't kill me to duplicate some code and use `ebdb-add-to-list' in
the function position, but I'd like to know why this doesn't work.

Thanks!
Eric




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Using funcall on inline functions
  2020-12-12 22:49 Using funcall on inline functions Eric Abrahamsen
@ 2020-12-12 23:28 ` Stefan Monnier
  2020-12-13 17:48   ` Eric Abrahamsen
  0 siblings, 1 reply; 5+ messages in thread
From: Stefan Monnier @ 2020-12-12 23:28 UTC (permalink / raw)
  To: Eric Abrahamsen; +Cc: emacs-devel

> (define-inline ebdb-add-to-list (list-var element)
>   (inline-quote (when, element
>                   (cl-pushnew, element, list-var :test #'equal))))

This can't work: your argument is named "list-var" but the way you use
it in the body indicates that it's supposed to be a *list* not a *list
variable*.

It may happen to work in some cases when the function gets inlined, but
if so, it's actually showing a misfeature of the `define-inline`
implementation (result of some optimizations).

You can't have it both way: if `ebdb-add-to-list` is a function, then
`list-var` is a local variable and this argument uses the usual
pass-by-value semantics (and hence `cl-pushnew` will only affect that
local variable).  If you want `list-var` to be the name of the *place*
passed by the caller then it has to be a macro.

There is a workaround, then, which is to use a reference:

    (cl-defstruct ebdb-record-cache
      (alt-names nil :type list))
    
    (defclass ebdb-record ()
      ((cache :type ebdb-record-cache))) 
    
    (define-inline ebdb-record-alt-names (record)
      (inline-quote (ebdb-record-cache-alt-names
                     (slot-value,record 'cache))))
    
    (define-inline ebdb-add-to-list (list-ref element)
      (inline-quote
        (when ,element
          (cl-pushnew ,element (gv-deref ,list-ref) :test #'equal))))
    
    (let ((listfunc #'ebdb-add-to-list)
          (name-string "Bob's new name"))
      (funcall listfunc
               (gv-ref (ebdb-record-alt-names <some-record>))
               name-string))

[ BTW, your `ebdb-add-to-list` has a bug in that it will evaluate its
  second argument before its first.  ]


        Stefan




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Using funcall on inline functions
  2020-12-12 23:28 ` Stefan Monnier
@ 2020-12-13 17:48   ` Eric Abrahamsen
  2020-12-13 18:12     ` Stefan Monnier
  0 siblings, 1 reply; 5+ messages in thread
From: Eric Abrahamsen @ 2020-12-13 17:48 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> (define-inline ebdb-add-to-list (list-var element)
>>   (inline-quote (when, element
>>                   (cl-pushnew, element, list-var :test #'equal))))
>
> This can't work: your argument is named "list-var" but the way you use
> it in the body indicates that it's supposed to be a *list* not a *list
> variable*.
>
> It may happen to work in some cases when the function gets inlined, but
> if so, it's actually showing a misfeature of the `define-inline`
> implementation (result of some optimizations).
>
> You can't have it both way: if `ebdb-add-to-list` is a function, then
> `list-var` is a local variable and this argument uses the usual
> pass-by-value semantics (and hence `cl-pushnew` will only affect that
> local variable).  If you want `list-var` to be the name of the *place*
> passed by the caller then it has to be a macro.

Thanks for the detailed explanation; I see what the problem is.

> There is a workaround, then, which is to use a reference:
>
>     (cl-defstruct ebdb-record-cache
>       (alt-names nil :type list))
>     
>     (defclass ebdb-record ()
>       ((cache :type ebdb-record-cache))) 
>     
>     (define-inline ebdb-record-alt-names (record)
>       (inline-quote (ebdb-record-cache-alt-names
>                      (slot-value,record 'cache))))
>     
>     (define-inline ebdb-add-to-list (list-ref element)
>       (inline-quote
>         (when ,element
>           (cl-pushnew ,element (gv-deref ,list-ref) :test #'equal))))
>     
>     (let ((listfunc #'ebdb-add-to-list)
>           (name-string "Bob's new name"))
>       (funcall listfunc
>                (gv-ref (ebdb-record-alt-names <some-record>))
>                name-string))

Hmm, interesting. I guess I would feel a little weird about having to
change all callers specifically for this -- I can see myself six months
from now having no memory of doing this, and causing bugs for myself.
But this definitely helps clarify the problem.

> [ BTW, your `ebdb-add-to-list` has a bug in that it will evaluate its
> second argument before its first. ]

inline-letevals! But, for my information, isn't the bug that ELEMENT is
evaluated twice? Why does the order of evaluation matter?

Thanks,
Eric



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Using funcall on inline functions
  2020-12-13 17:48   ` Eric Abrahamsen
@ 2020-12-13 18:12     ` Stefan Monnier
  2020-12-13 18:27       ` Eric Abrahamsen
  0 siblings, 1 reply; 5+ messages in thread
From: Stefan Monnier @ 2020-12-13 18:12 UTC (permalink / raw)
  To: Eric Abrahamsen; +Cc: emacs-devel

> Hmm, interesting. I guess I would feel a little weird about having to
> change all callers specifically for this -- I can see myself six months
> from now having no memory of doing this, and causing bugs for myself.

Yeah, `gv-ref` is not super popular, indeed.  Can't blame you.

>> [ BTW, your `ebdb-add-to-list` has a bug in that it will evaluate its
>> second argument before its first. ]
> inline-letevals! But, for my information, isn't the bug that ELEMENT is
> evaluated twice?

Oh, yeah, that as well.

> Why does the order of evaluation matter?

Because in ELisp order of evaluation of function arguments is specified
to be "left to right" and code can (and does every once in a while) rely
on it.  It's not super-frequent, but not really rare either (e.g. it's
quite common for things like `delete-region`).


        Stefan




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Using funcall on inline functions
  2020-12-13 18:12     ` Stefan Monnier
@ 2020-12-13 18:27       ` Eric Abrahamsen
  0 siblings, 0 replies; 5+ messages in thread
From: Eric Abrahamsen @ 2020-12-13 18:27 UTC (permalink / raw)
  To: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> Hmm, interesting. I guess I would feel a little weird about having to
>> change all callers specifically for this -- I can see myself six months
>> from now having no memory of doing this, and causing bugs for myself.
>
> Yeah, `gv-ref` is not super popular, indeed.  Can't blame you.

I actually like the mechanism and what it lets you do, but would prefer
it to be buried deep someplace where I can't see it.

>>> [ BTW, your `ebdb-add-to-list` has a bug in that it will evaluate its
>>> second argument before its first. ]
>> inline-letevals! But, for my information, isn't the bug that ELEMENT is
>> evaluated twice?
>
> Oh, yeah, that as well.
>
>> Why does the order of evaluation matter?
>
> Because in ELisp order of evaluation of function arguments is specified
> to be "left to right" and code can (and does every once in a while) rely
> on it.  It's not super-frequent, but not really rare either (e.g. it's
> quite common for things like `delete-region`).

Not something that ever occurred to me. This has been an educational
thread, even though the result will probably be me deciding this inline
isn't necessary at all.

Thanks,
Eric




^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-12-13 18:27 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-12 22:49 Using funcall on inline functions Eric Abrahamsen
2020-12-12 23:28 ` Stefan Monnier
2020-12-13 17:48   ` Eric Abrahamsen
2020-12-13 18:12     ` Stefan Monnier
2020-12-13 18:27       ` Eric Abrahamsen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

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