GNU bug report logs - #61179
lambda inside interactive form of around advice isn't a closure

Previous Next

Package: emacs;

Reported by: Jonas Bernoulli <jonas <at> bernoul.li>

Date: Mon, 30 Jan 2023 15:49:01 UTC

Severity: normal

Done: Stefan Monnier <monnier <at> iro.umontreal.ca>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 61179 in the body.
You can then email your comments to 61179 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Mon, 30 Jan 2023 15:49:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Jonas Bernoulli <jonas <at> bernoul.li>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Mon, 30 Jan 2023 15:49:01 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Jonas Bernoulli <jonas <at> bernoul.li>
To: bug-gnu-emacs <at> gnu.org
Cc: Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: lambda inside interactive form of around advice isn't a closure
Date: Mon, 30 Jan 2023 16:48:13 +0100
A function used as an around advice may advise the advised function's
interactive form, using (interactive (lambda (spec) ...)).

Unfortunately this inner lambda expression is not turned into a closure
as demonstrated by this simple example:

  ;; -*- lexical-binding: t -*-
  (let ((var :value))
    (lambda (fn &rest args)
      (interactive
       (lambda (spec)
         (message "interactive: %s" var)
         (advice-eval-interactive-spec spec)))
      (message "body: %s" var)
      (apply fn args)))

Or if you want to observe the failure when trying to use such an advice:

  (defun -make-advice ()
    (let ((var :value))
      (lambda (fn &rest args)
        (interactive
         (lambda (spec)
           (message "interactive: %s" var)
           (advice-eval-interactive-spec spec)))
        (message "body: %s" var)
        (apply fn args))))

  (defun -command (arg)
    (interactive (list (read-string ": "))))
  (advice-add '-command :around (-make-advice) '((name . "-advice")))

Could this be changed in the next Emacs release?  Even if this should
make it into 29.1 (which I doubt), it would still be very useful for me
if this could somehow be rewritten to also work in older Emacs releases.
I have tried throwing an eval or two in there, but with limited success.

This gives me an inner closure, but the outside closure does not capture
the same environment it seems:

  (let ((var :value))
    (eval `(lambda (fn &rest args)
             (interactive
              ,(lambda (spec)
                 (message "interactive: %s" var)
                 (advice-eval-interactive-spec spec)))
             (message "body: %s" var)
             (apply fn args))
          t))

I got desperate and also tried things like:

  (call-interactively
   (let ((lex '((var . :value))))
     (eval `(lambda ()
              (interactive
               ,(eval '(lambda (spec)
                        (message "interactive: %s" var)
                        nil)
                      lex))
              (message "body: %s" var))
           lex)))

Thanks for your help!
Jonas




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Tue, 31 Jan 2023 23:34:02 GMT) Full text and rfc822 format available.

Message #8 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Jonas Bernoulli <jonas <at> bernoul.li>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Tue, 31 Jan 2023 18:33:18 -0500
> Unfortunately this inner lambda expression is not turned into a closure
> as demonstrated by this simple example:
>
>   ;; -*- lexical-binding: t -*-
>   (let ((var :value))
>     (lambda (fn &rest args)
>       (interactive
>        (lambda (spec)
>          (message "interactive: %s" var)
>          (advice-eval-interactive-spec spec)))
>       (message "body: %s" var)
>       (apply fn args)))

Hmm... in Emacs-29, we fixed a part of this problem.
E.g.

    (call-interactively
     (let ((x 42))
       (lambda (f)
         (interactive (list (lambda () x)))
         (+ (funcall f) 1))))

should return 43 (even when byte-compiled).

But indeed, the fix currently fails to account for such
"interactive advice" :-(

> Could this be changed in the next Emacs release?  Even if this should
> make it into 29.1 (which I doubt), it would still be very useful for me
> if this could somehow be rewritten to also work in older Emacs releases.

The above fix is non-trivial, I'm afraid (e.g. it relies on OClosures
internally).  For your above code, I think I'd try something like:

    (let* ((var :value)
           (interactive-advice
            (lambda (spec)
             (message "interactive: %s" var)
             (advice-eval-interactive-spec spec)))
           (advice-body
            (lambda (fn &rest args)
              (message "body: %s" var)
              (apply fn args))))
      (eval `(lambda (&rest args)
               (interactive ,interactive-advice)
               (apply ',advice-body args))
            t))

where the last 4 lines are "generic" and could be turned into
a helper function if you end up using it several times.
This should also minimize the amount of code that's hidden from the
compiler by the backquote.


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Wed, 01 Feb 2023 01:35:02 GMT) Full text and rfc822 format available.

Message #11 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Jonas Bernoulli <jonas <at> bernoul.li>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Wed, 01 Feb 2023 02:33:55 +0100
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

> I think I'd try something like:
>
>     (let* ((var :value)
>            (interactive-advice
>             (lambda (spec)
>              (message "interactive: %s" var)
>              (advice-eval-interactive-spec spec)))
>            (advice-body
>             (lambda (fn &rest args)
>               (message "body: %s" var)
>               (apply fn args))))
>       (eval `(lambda (&rest args)
>                (interactive ,interactive-advice)
>                (apply ',advice-body args))
>             t))

I'm having troubles falling asleep because my brain insists on rewording
the following (even though --or perhaps exactly because-- there is not
really any need to mention it at all).  So I might as well try if typing
it out helps. ;)

I actually though of that, and the initial poc worked.  I did that late
at night also updated transient to use it.  In the morning I noticed
that there actually were many errors, and while I suspected that the
failure had nothing to do with this part of my change, it still lost
confidence in that approach, and since I felt some pressure to get
things done before the Emacs pre-release I stopped pursuing it.
Somehow I forgot about it when I wrote the above.

Now that you have suggested the same, my confidence is back.  And now
that I have written the above, I should also be able to fall asleep. ;P

> where the last 4 lines are "generic" and could be turned into
> a helper function if you end up using it several times.
> This should also minimize the amount of code that's hidden from the
> compiler by the backquote.

I'll only need it once; would you recommend using a helper in that case
too?

>
>
>         Stefan

    Good night!
    Jonas

    zzzzZZz




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Wed, 01 Feb 2023 22:30:02 GMT) Full text and rfc822 format available.

Message #14 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Jonas Bernoulli <jonas <at> bernoul.li>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Wed, 01 Feb 2023 23:29:44 +0100
Stefan,

Could you please have a look at bug#61176 too?  The reason I am
looking into this around advice business in the first place, is
that `post-command-hook' isn't quite working as I expected, as
described in that issue.

     Thanks!
     Jonas




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Wed, 01 Feb 2023 22:36:01 GMT) Full text and rfc822 format available.

Message #17 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Jonas Bernoulli <jonas <at> bernoul.li>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Wed, 01 Feb 2023 17:34:43 -0500
> I'm having troubles falling asleep because my brain insists on rewording
> the following (even though --or perhaps exactly because-- there is not
> really any need to mention it at all).  So I might as well try if typing
> it out helps. ;)
>
> I actually though of that, and the initial poc worked.  I did that late
> at night also updated transient to use it.  In the morning I noticed
> that there actually were many errors, and while I suspected that the
> failure had nothing to do with this part of my change, it still lost
> confidence in that approach, and since I felt some pressure to get
> things done before the Emacs pre-release I stopped pursuing it.
> Somehow I forgot about it when I wrote the above.
>
> Now that you have suggested the same, my confidence is back.  And now
> that I have written the above, I should also be able to fall asleep. ;P

I hope your slept well.  The code I sent is, as usual, 100% guaranteed
untested, but it *should* work modulo silly errors.

> I'll only need it once; would you recommend using a helper in that case too?

Either way works.


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Thu, 02 Feb 2023 14:40:01 GMT) Full text and rfc822 format available.

Message #20 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Jonas Bernoulli <jonas <at> bernoul.li>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Thu, 02 Feb 2023 15:39:26 +0100
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

>> And now that I have written the above, I should also be able to fall
>> asleep. ;P
>
> I hope your slept well.

My brain no longer saw a need to keep me awake, so yes.  ;)

> The code I sent is, as usual, 100% guaranteed untested, but it
> *should* work modulo silly errors.

I haven't gotten around to play with this approach again yet.
The quote inside the function body seems a bit weird.

     Jonas




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Sat, 04 Feb 2023 16:27:02 GMT) Full text and rfc822 format available.

Message #23 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Jonas Bernoulli <jonas <at> bernoul.li>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: lambda inside interactive form of around advice isn't a closure
Date: Sat, 04 Feb 2023 11:26:15 -0500
> A function used as an around advice may advise the advised function's
> interactive form, using (interactive (lambda (spec) ...)).
>
> Unfortunately this inner lambda expression is not turned into a closure
> as demonstrated by this simple example:

I installed a patch in `master` which should fix this problem, both for
the case where the code is interpreted and for the case where it is
byte-compiled.

Could you confirm that it works for you?


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Sat, 04 Feb 2023 23:49:01 GMT) Full text and rfc822 format available.

Message #26 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of
 text editors" <bug-gnu-emacs <at> gnu.org>
Cc: Jonas Bernoulli <jonas <at> bernoul.li>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>, 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Sun, 05 Feb 2023 00:47:58 +0100
[Message part 1 (text/plain, inline)]
Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of
text editors" <bug-gnu-emacs <at> gnu.org> writes:

> I installed a patch in `master` which should fix this problem, both for
> the case where the code is interpreted and for the case where it is
> byte-compiled.

Very good.

This seems to break some of my private code.  Didn't look into your
change so far.

My code generates function advices with the following semantics: You
specify an alternative function and a condition (as a predicate
function).  When the advice is enabled, whenever the function is called
the predicate is first checked for a non-nil result.  When non-nil, the
alternative function that had been specified is called (recursive calls
are bot affected however).  When nil, the original function is called.

I'm using this hack only in my init file for convenience, I like the
semantics for this purpose.

Here is a recipe for emacs -Q:

[int-lambda.el (application/emacs-lisp, inline)]
[Message part 3 (text/plain, inline)]

Worked as I wanted until now.  But with your patch installed when
compiling the above snipped I get a *Compile Log* saying:

| Compiling internal form(s) at Sun Feb  5 00:36:11 2023
| int-lambda.elc: Error: ‘lambda’ defined after use in (lambda (old-spec3) (let ((test-result0 (#[0 "\300\207" [t] 1]))) (cons (if test-result0 'mel-ad-run-replacement 'mel-ad-run-original) (advice-eval-interactive-spec (if (not test-result0) old-spec3))))) (missing ‘require’ of a library file?)
| int-lambda.elc: Error: ‘lambda’ used as function name is invalid

Who is to blame?


TIA,

Michael.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Sat, 04 Feb 2023 23:49:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Sun, 05 Feb 2023 14:06:01 GMT) Full text and rfc822 format available.

Message #32 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Michael Heerdegen <michael_heerdegen <at> web.de>
Cc: Jonas Bernoulli <jonas <at> bernoul.li>, 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Sun, 05 Feb 2023 09:05:29 -0500
> | Compiling internal form(s) at Sun Feb  5 00:36:11 2023
> | int-lambda.elc: Error: ‘lambda’ defined after use in (lambda (old-spec3)
> | (let ((test-result0 (#[0 "\300\207" [t] 1]))) (cons (if test-result0
> | 'mel-ad-run-replacement 'mel-ad-run-original)
> | (advice-eval-interactive-spec (if (not test-result0) old-spec3)))))
> | (missing ‘require’ of a library file?)
> | int-lambda.elc: Error: ‘lambda’ used as function name is invalid
>
> Who is to blame?

You, of course.
But I think the patch below (which I just pushed to `master`) will help,


        Stefan


diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index e4268c2fb88..e8d639903c1 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -488,7 +488,7 @@ cconv-convert
             (_ (pcase cif
                  ('nil nil)
                  (`#',f
-                  (setf (cadr (car bf)) (if wrapped (nth 2 f) f))
+                  (setf (cadr (car bf)) (if wrapped (nth 2 f) cif))
                   (setq cif nil))
                  ;; The interactive form needs special treatment, so the form
                  ;; inside the `interactive' won't be used any further.





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Sun, 05 Feb 2023 16:44:02 GMT) Full text and rfc822 format available.

Message #35 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Jonas Bernoulli <jonas <at> bernoul.li>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: lambda inside interactive form of around advice isn't a closure
Date: Sun, 05 Feb 2023 17:43:28 +0100
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

>> A function used as an around advice may advise the advised function's
>> interactive form, using (interactive (lambda (spec) ...)).
>>
>> Unfortunately this inner lambda expression is not turned into a closure
>> as demonstrated by this simple example:
>
> I installed a patch in `master` which should fix this problem, both for
> the case where the code is interpreted and for the case where it is
> byte-compiled.
>
> Could you confirm that it works for you?

Thanks a lot!  I'll try it out tomorrow (it doesn't seem my brain feels
like work today).

     Jonas




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Sun, 05 Feb 2023 18:56:01 GMT) Full text and rfc822 format available.

Message #38 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Jonas Bernoulli <jonas <at> bernoul.li>
Cc: 61179 <at> debbugs.gnu.org
Subject: Re: lambda inside interactive form of around advice isn't a closure
Date: Sun, 05 Feb 2023 13:54:45 -0500
> Thanks a lot!  I'll try it out tomorrow (it doesn't seem my brain feels
> like work today).

Don't worry: mine is slowly thawing now, so there's no hurry,


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#61179; Package emacs. (Sun, 05 Feb 2023 22:25:02 GMT) Full text and rfc822 format available.

Message #41 received at 61179 <at> debbugs.gnu.org (full text, mbox):

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: Jonas Bernoulli <jonas <at> bernoul.li>, 61179 <at> debbugs.gnu.org
Subject: Re: bug#61179: lambda inside interactive form of around advice
 isn't a closure
Date: Sun, 05 Feb 2023 23:23:58 +0100
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

> > Who is to blame?
> You, of course.

Oh.

> But I think the patch below (which I just pushed to `master`) will help,
> [...]

Yes, it did, thank you.


Michael.




Reply sent to Stefan Monnier <monnier <at> iro.umontreal.ca>:
You have taken responsibility. (Wed, 08 Feb 2023 14:39:02 GMT) Full text and rfc822 format available.

Notification sent to Jonas Bernoulli <jonas <at> bernoul.li>:
bug acknowledged by developer. (Wed, 08 Feb 2023 14:39:02 GMT) Full text and rfc822 format available.

Message #46 received at 61179-done <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: 61179-done <at> debbugs.gnu.org
Subject: Re: lambda inside interactive form of around advice isn't a closure
Date: Wed, 08 Feb 2023 09:38:16 -0500
> Could you confirm that it works for you?
[ Confirmed.  ]

Thanks, closing,


        Stefan





bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Thu, 09 Mar 2023 12:24:09 GMT) Full text and rfc822 format available.

This bug report was last modified 2 years and 159 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.