* bug#40968: 28.0.50; (apply nil) [not found] ` <<83pnahctad.fsf@gnu.org> @ 2020-06-02 17:10 ` Drew Adams 2020-06-02 18:41 ` Pip Cet 0 siblings, 1 reply; 44+ messages in thread From: Drew Adams @ 2020-06-02 17:10 UTC (permalink / raw) To: Eli Zaretskii, Drew Adams; +Cc: mattiase, stefan, 40968, npostavs, pipcet > We will not require minimum 2 args because that would be backward > incompatible. I'm quite sure I already said that before. Yes, you did, as I indicated: "The only good case presented was to say that this would be an incompatible change." In expressing my non-inclusion in the purported "consensus", that's all I did. And "because XYZ would be backward incompatible" has not stopped Emacs from sometimes breaking backward compatibility. That's a good reason, as I said, but there are also (IMO) good reasons not to go with that one good reason. My suggestion is to not only "recommend always passing 2 or more arguments" but to issue a warning when that's not the case. And to deprecate that use (letting users know that at some point it might no longer be supported). And (IMO) the reason given to users for the recommendation shouldn't be just because ("as") "the function works faster in that case". ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-06-02 17:10 ` bug#40968: 28.0.50; (apply nil) Drew Adams @ 2020-06-02 18:41 ` Pip Cet 0 siblings, 0 replies; 44+ messages in thread From: Pip Cet @ 2020-06-02 18:41 UTC (permalink / raw) To: Drew Adams; +Cc: mattiase, stefan, 40968, npostavs Drew Adams <drew.adams@oracle.com> writes: >> We will not require minimum 2 args because that would be backward >> incompatible. I'm quite sure I already said that before. > > Yes, you did, as I indicated: "The only good > case presented was to say that this would be > an incompatible change." > > In expressing my non-inclusion in the purported > "consensus", that's all I did. Thank you for doing so. I think it's important to have a record of whether a decision was reached by consensus or not, and I had wrongly supposed this to be a case of the former. Sorry. > My suggestion is to not only "recommend always > passing 2 or more arguments" but to issue a > warning when that's not the case. And to > deprecate that use (letting users know that at > some point it might no longer be supported). A first step towards that would be to change our existing Lisp code not to use single-argument apply, which we could do in a follow-up patch. > And (IMO) the reason given to users for the > recommendation shouldn't be just because ("as") > "the function works faster in that case". I agree. I'll make another suggestion. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) @ 2020-04-29 18:26 Pip Cet 2020-04-29 18:35 ` Pip Cet ` (3 more replies) 0 siblings, 4 replies; 44+ messages in thread From: Pip Cet @ 2020-04-29 18:26 UTC (permalink / raw) To: 40968 I'm very confused by the behavior of `apply' when given only a single argument: when it's a nonempty list, the argument gets spread out and passed to Ffuncall (I guess this might be useful sometimes). When it's nil, Ffuncall gets called with nargs == 0 and Emacs crashes, at least sometimes. Should we fix the special case or raise apply's minimum argument count to 2? ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-04-29 18:26 Pip Cet @ 2020-04-29 18:35 ` Pip Cet 2020-05-06 1:51 ` Stefan Kangas ` (2 subsequent siblings) 3 siblings, 0 replies; 44+ messages in thread From: Pip Cet @ 2020-04-29 18:35 UTC (permalink / raw) To: 40968 On Wed, Apr 29, 2020 at 6:27 PM Pip Cet <pipcet@gmail.com> wrote: > Should we fix the special case or raise apply's minimum argument count to 2? The byte compiler also gets single-argument `apply' "wrong", so the latter would seem to be the best option, to me. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-04-29 18:26 Pip Cet 2020-04-29 18:35 ` Pip Cet @ 2020-05-06 1:51 ` Stefan Kangas 2020-05-06 7:26 ` Pip Cet 2020-05-06 10:18 ` Mattias Engdegård 2020-05-06 18:42 ` Mattias Engdegård 3 siblings, 1 reply; 44+ messages in thread From: Stefan Kangas @ 2020-05-06 1:51 UTC (permalink / raw) To: Pip Cet; +Cc: 40968 Pip Cet <pipcet@gmail.com> writes: > I'm very confused by the behavior of `apply' when given only a single > argument: when it's a nonempty list, the argument gets spread out and > passed to Ffuncall (I guess this might be useful sometimes). When it's > nil, Ffuncall gets called with nargs == 0 and Emacs crashes, at least > sometimes. > > Should we fix the special case or raise apply's minimum argument count to 2? I'm personally not a big fan of raising the minimum argument count, thereby changing the function signature and making it harder to understand. We should just fix the bug, IMHO. Best regards, Stefan Kangas ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 1:51 ` Stefan Kangas @ 2020-05-06 7:26 ` Pip Cet 2020-05-06 11:24 ` Stefan Kangas 2020-09-29 3:00 ` Stefan Monnier 0 siblings, 2 replies; 44+ messages in thread From: Pip Cet @ 2020-05-06 7:26 UTC (permalink / raw) To: Stefan Kangas; +Cc: 40968 On Wed, May 6, 2020 at 1:51 AM Stefan Kangas <stefan@marxist.se> wrote: > Pip Cet <pipcet@gmail.com> writes: > > I'm very confused by the behavior of `apply' when given only a single > > argument: when it's a nonempty list, the argument gets spread out and > > passed to Ffuncall (I guess this might be useful sometimes). When it's > > nil, Ffuncall gets called with nargs == 0 and Emacs crashes, at least > > sometimes. > > > > Should we fix the special case or raise apply's minimum argument count to 2? > > I'm personally not a big fan of raising the minimum argument count, > thereby changing the function signature and making it harder to > understand. Thanks. I disagree that it would be harder to understand, though: "the first argument is a function to call, the last argument is a list of arguments" is easy to understand when there are >= 2 arguments, but for a single argument they're in contradiction, aren't they? Indeed, I'd read Fapply's current docstring: Call FUNCTION with our remaining args, using our last arg as list of args. as implying that (apply FUNCTION) is equivalent to (funcall FUNCTION) > We should just fix the bug, IMHO. I don't think we can "just" fix the bug. We need to fix Fapply, change the byte compiler to accept single-argument apply, reformulate the docstring to describe this case, and probably fix the Lisp manual as well. I don't think all that is worth it, to be honest, to save someone the trouble of having to write (apply (car LIST) (cdr LIST)) rather than (apply LIST) (if that is, indeed, what they meant). ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 7:26 ` Pip Cet @ 2020-05-06 11:24 ` Stefan Kangas 2020-05-06 11:49 ` Pip Cet 2020-09-29 3:00 ` Stefan Monnier 1 sibling, 1 reply; 44+ messages in thread From: Stefan Kangas @ 2020-05-06 11:24 UTC (permalink / raw) To: Pip Cet; +Cc: 40968 Pip Cet <pipcet@gmail.com> writes: > Thanks. I disagree that it would be harder to understand, though: "the > first argument is a function to call, the last argument is a list of > arguments" is easy to understand when there are >= 2 arguments, but > for a single argument they're in contradiction, aren't they? Indeed, > I'd read Fapply's current docstring: I have a feeling I'm missing something obvious here. The current function signature is: (apply FUNCTION &rest ARGUMENTS) How would your proposal change that? Best regards, Stefan Kangas ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 11:24 ` Stefan Kangas @ 2020-05-06 11:49 ` Pip Cet 2020-05-06 13:02 ` Stefan Kangas ` (2 more replies) 0 siblings, 3 replies; 44+ messages in thread From: Pip Cet @ 2020-05-06 11:49 UTC (permalink / raw) To: Stefan Kangas; +Cc: 40968 On Wed, May 6, 2020 at 11:24 AM Stefan Kangas <stefan@marxist.se> wrote: > Pip Cet <pipcet@gmail.com> writes: > I have a feeling I'm missing something obvious here. The current > function signature is: > > (apply FUNCTION &rest ARGUMENTS) > > How would your proposal change that? (apply FUNCTION ARGUMENT &rest ARGUMENTS), I guess. I missed it in the first patch. Note that the old signature suggests (apply FUNCTION) is equivalent to (funcall FUNCTION), which it isn't. (For example, that's what ElDoc indicates the first argument to apply is). Again, I'm no longer sure what the right thing to do here is. I think I'm up to five different interpretations of (apply ARG) here, depending on whether ARG is interpreted as FUNCTION, the last of the ARGUMENTS, an ARGUMENT but not the last one, or a combination thereof, and whether ARG is required to be a list or not... ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 11:49 ` Pip Cet @ 2020-05-06 13:02 ` Stefan Kangas 2020-05-06 13:55 ` Pip Cet ` (2 more replies) 2020-05-06 17:46 ` Drew Adams 2020-05-06 20:32 ` Phil Sainty 2 siblings, 3 replies; 44+ messages in thread From: Stefan Kangas @ 2020-05-06 13:02 UTC (permalink / raw) To: Pip Cet; +Cc: 40968 Pip Cet <pipcet@gmail.com> writes: > (apply FUNCTION ARGUMENT &rest ARGUMENTS), I guess. I missed it in the > first patch. Thanks. FWIW, I still think we should avoid changing the function signature if at all possible. apply is fundamental to Lisp, and has been defined like this for a long time. See the definition of "The Universal S-Function apply" in John McCarthy's paper: [1] apply[f;args] =eval[cons[f;appq[args]];NIL], Also note that it is still defined like this elsewhere: Scheme: (apply function argument-list) Common Lisp: apply function &rest args+ => result* Clojure: (apply f args) Best regards, Stefan Kangas Footnotes: [1] http://www-formal.stanford.edu/jmc/recursive.pdf ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 13:02 ` Stefan Kangas @ 2020-05-06 13:55 ` Pip Cet 2020-05-06 15:28 ` Stefan Kangas 2020-05-06 14:03 ` Eli Zaretskii 2020-05-06 18:00 ` Drew Adams 2 siblings, 1 reply; 44+ messages in thread From: Pip Cet @ 2020-05-06 13:55 UTC (permalink / raw) To: Stefan Kangas; +Cc: 40968 On Wed, May 6, 2020 at 1:02 PM Stefan Kangas <stefan@marxist.se> wrote: > Pip Cet <pipcet@gmail.com> writes: > > (apply FUNCTION ARGUMENT &rest ARGUMENTS), I guess. I missed it in the > > first patch. > > Thanks. > > FWIW, I still think we should avoid changing the function signature if > at all possible. Is the function signature relevant for anything but eldoc? > apply is fundamental to Lisp, and has been defined like this for a > long time. I don't know about that. Anything but the two-argument form of apply strikes me as rather dialect-dependent, but I may be wrong. > See the definition of "The Universal S-Function apply" in > John McCarthy's paper: [1] > > apply[f;args] =eval[cons[f;appq[args]];NIL], I must admit I don't know how appq is defined. > Also note that it is still defined like this elsewhere: I may be misreading your examples, but they seem to me to have different signatures from the one hitherto used in Emacs. > Scheme: (apply function argument-list) Precisely two args, right? > Common Lisp: apply function &rest args+ => result* At least two args. > Clojure: (apply f args) Precisely two args again? ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 13:55 ` Pip Cet @ 2020-05-06 15:28 ` Stefan Kangas 2020-05-06 18:06 ` Pip Cet 0 siblings, 1 reply; 44+ messages in thread From: Stefan Kangas @ 2020-05-06 15:28 UTC (permalink / raw) To: Pip Cet; +Cc: 40968 Pip Cet <pipcet@gmail.com> writes: > Is the function signature relevant for anything but eldoc? Besides the docstring, the manual documents it. The suggested form is unusual and makes it harder to understand, IMHO. >> apply is fundamental to Lisp, and has been defined like this for a >> long time. > > I don't know about that. Anything but the two-argument form of apply > strikes me as rather dialect-dependent, but I may be wrong. If I understand correctly, you propose a three argument form: (apply FUNCTION ARGUMENT &rest ARGUMENTS) This is what I find unusual. It should really be either (apply FUNCTION &rest ARGUMENTS) or (apply FUNCTION ARGUMENTS) But since we already have the former, we are better to stick with that. Maybe there's a case to be made for a syntactic alternative to "&rest" which disallows nil, which I guess is the issue here? But we can also just signal an error in this case. Racket does the latter, as one data point: > (define foo (lambda () 1)) > (apply foo nil) ; nil: undefined; ; cannot reference an identifier before its definition ; in module: top-level ; [,bt for context] BTW, I don't see a big difference conceptually between '&rest ARGUMENTS' and 'ARGUMENTS'. The former is just syntactic sugar, right? >> See the definition of "The Universal S-Function apply" in >> John McCarthy's paper: [1] >> >> apply[f;args] =eval[cons[f;appq[args]];NIL], > > I must admit I don't know how appq is defined. My point is mainly that it has two arguments: f and args. Best regards, Stefan Kangas ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 15:28 ` Stefan Kangas @ 2020-05-06 18:06 ` Pip Cet 2020-05-06 19:26 ` Drew Adams 0 siblings, 1 reply; 44+ messages in thread From: Pip Cet @ 2020-05-06 18:06 UTC (permalink / raw) To: Stefan Kangas; +Cc: 40968 On Wed, May 6, 2020 at 3:28 PM Stefan Kangas <stefan@marxist.se> wrote: > Pip Cet <pipcet@gmail.com> writes: > > Is the function signature relevant for anything but eldoc? > > Besides the docstring, the manual documents it. The suggested form is > unusual and makes it harder to understand, IMHO. I think it's hard to understand Elisp apply from a standard signature, because it's really (apply FUNCTION &rest INDIVIDUAL-ARGUMENTS ARGUMENT-LIST) or (apply FUNCTION-AND-ARGUMENT-LIST) The latter (which takes a single argument) is not a special case of the former (which takes 2,3,4,... arguments). > >> apply is fundamental to Lisp, and has been defined like this for a > >> long time. > > > > I don't know about that. Anything but the two-argument form of apply > > strikes me as rather dialect-dependent, but I may be wrong. > > If I understand correctly, you propose a three argument form: > > (apply FUNCTION ARGUMENT &rest ARGUMENTS) That's a 2,3,4...-argument form. > This is what I find unusual. It should really be either > > (apply FUNCTION &rest ARGUMENTS) That's a 1,2,3...-argument form. > or > > (apply FUNCTION ARGUMENTS) That's a 2-argument form. > But since we already have the former, we are better to stick with > that. > > Maybe there's a case to be made for a syntactic alternative to "&rest" > which disallows nil, which I guess is the issue here? But we can also > just signal an error in this case. > > Racket does the latter, as one data point: > > > (define foo (lambda () 1)) > > (apply foo nil) > ; nil: undefined; > ; cannot reference an identifier before its definition > ; in module: top-level > ; [,bt for context] Doesn't that just say that "nil" isn't a valid Racket identifier? (apply foo '()) works fine, and Racket's apply requires at least two arguments if I'm reading the error message correctly. > BTW, I don't see a big difference conceptually between '&rest > ARGUMENTS' and 'ARGUMENTS'. The former is just syntactic sugar, > right? Not really, no. > >> See the definition of "The Universal S-Function apply" in > >> John McCarthy's paper: [1] > >> > >> apply[f;args] =eval[cons[f;appq[args]];NIL], > > > > I must admit I don't know how appq is defined. (It turns out my PDF viewer just refused to highlight the definition right there on the page). > My point is mainly that it has two arguments: f and args. I think we're all in agreement about 2-argument apply. 3,4,...-argument apply is an unfortunate legacy but one we're stuck with now. 1-argument apply is the issue here. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 18:06 ` Pip Cet @ 2020-05-06 19:26 ` Drew Adams 0 siblings, 0 replies; 44+ messages in thread From: Drew Adams @ 2020-05-06 19:26 UTC (permalink / raw) To: Pip Cet, Stefan Kangas; +Cc: 40968 > > > Is the function signature relevant for anything but eldoc? It should be relevant in terms of raising an error if the signature is not respected. > > Besides the docstring, the manual documents it. The suggested form > > is unusual and makes it harder to understand, IMHO. > > I think it's hard to understand Elisp apply from a standard signature, > because it's really > > (apply FUNCTION &rest INDIVIDUAL-ARGUMENTS ARGUMENT-LIST) > or > (apply FUNCTION-AND-ARGUMENT-LIST) > > The latter (which takes a single argument) is not a special case of > the former (which takes 2,3,4,... arguments). (apply FUNCTION) and (apply) should raise an error, IMO (as in Common Lisp). Is there a good use case for either? > > If I understand correctly, you propose a three argument form: > > (apply FUNCTION ARGUMENT &rest ARGUMENTS) > > That's a 2,3,4...-argument form. It's what Common Lisp prescribes. > > This is what I find unusual. It should really be either > > (apply FUNCTION &rest ARGUMENTS) > > or (apply FUNCTION ARGUMENTS) > > That's a 2-argument form. That second form is the same as (apply FUNCTION ARGUMENT). And in that second form it's fine for ARGUMENT to be nil. The first form should raise an error if ARGUMENTS is nil. > > Maybe there's a case to be made for a syntactic alternative to > > "&rest" which disallows nil, which I guess is the issue here? An &rest which must not be nil is written as: ARGUMENT &rest MORE-ARGS &rest is just a list. It can always be nil. > > My point is mainly that it has two arguments: f and args. > > I think we're all in agreement about 2-argument apply. > 3,4,...-argument apply is an unfortunate legacy but one we're stuck > with now. 1-argument apply is the issue here. I'm not in agreement, FWIW. The behavior and its description should be as for Common Lisp: require at least 2 args: FUNCTION and its first ARGUMENT. Is there some use case for (apply f) and (apply)? ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 13:02 ` Stefan Kangas 2020-05-06 13:55 ` Pip Cet @ 2020-05-06 14:03 ` Eli Zaretskii 2020-05-06 17:54 ` Pip Cet 2020-05-06 18:00 ` Drew Adams 2 siblings, 1 reply; 44+ messages in thread From: Eli Zaretskii @ 2020-05-06 14:03 UTC (permalink / raw) To: Stefan Kangas; +Cc: 40968, pipcet > From: Stefan Kangas <stefan@marxist.se> > Date: Wed, 6 May 2020 06:02:45 -0700 > Cc: 40968@debbugs.gnu.org > > Pip Cet <pipcet@gmail.com> writes: > > > (apply FUNCTION ARGUMENT &rest ARGUMENTS), I guess. I missed it in the > > first patch. > > Thanks. > > FWIW, I still think we should avoid changing the function signature if > at all possible. I agree. I also think it isn't enough to check only here and in ELPA: 'apply' is a very popular function, and is used very widely. I won't be surprised if there were more of these usage cases that would be broken by such a change in the signature. Can we instead identify the problematic usage and signal an error? ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 14:03 ` Eli Zaretskii @ 2020-05-06 17:54 ` Pip Cet 2020-05-06 18:09 ` Eli Zaretskii 0 siblings, 1 reply; 44+ messages in thread From: Pip Cet @ 2020-05-06 17:54 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Stefan Kangas, 40968 [-- Attachment #1: Type: text/plain, Size: 509 bytes --] On Wed, May 6, 2020 at 2:04 PM Eli Zaretskii <eliz@gnu.org> wrote: > 'apply' is a very popular function, and is used very widely. I won't > be surprised if there were more of these usage cases that would be > broken by such a change in the signature. > Can we instead identify the problematic usage and signal an error? Yes. What we also have to do is fix the documentation, and fix the byte optimizer. Here's a first suggestion. I'd particularly appreciate hints on better wording for the documentation. [-- Attachment #2: 0001-Handle-single-argument-apply-consistently-bug-40968.patch --] [-- Type: text/x-patch, Size: 3926 bytes --] From fa819045766f50c55f9bb588ecf7e0bef6a56d41 Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet@gmail.com> Date: Wed, 6 May 2020 17:46:56 +0000 Subject: [PATCH] Handle single-argument `apply' consistently (bug#40968) --- doc/lispref/functions.texi | 9 +++++++++ lisp/emacs-lisp/byte-opt.el | 29 ++++++++++++++++------------- src/eval.c | 5 +++++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index bc8ec0ef1b..054bcc169e 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -761,6 +761,10 @@ Calling Functions @dfn{spreads} this list so that each individual element becomes an argument. +@code{apply} with a single argument is special: the first element of +the argument, which must be a non-empty list, is called as a function +with the remaining elements as individual arguments. + @code{apply} returns the result of calling @var{function}. As with @code{funcall}, @var{function} must either be a Lisp function or a primitive function; special forms and macros do not make sense in @@ -788,6 +792,11 @@ Calling Functions (apply 'append '((a b c) nil (x y z) nil)) @result{} (a b c x y z) @end group + +@group +(apply '(+ 3 4)) + @result{} 7 +@end group @end example For an interesting example of using @code{apply}, see @ref{Definition diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 4f72251aed..32e53bb7e9 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -1098,19 +1098,22 @@ byte-optimize-funcall (defun byte-optimize-apply (form) ;; If the last arg is a literal constant, turn this into a funcall. ;; The funcall optimizer can then transform (funcall 'foo ...) -> (foo ...). - (let ((fn (nth 1 form)) - (last (nth (1- (length form)) form))) ; I think this really is fastest - (or (if (or (null last) - (eq (car-safe last) 'quote)) - (if (listp (nth 1 last)) - (let ((butlast (nreverse (cdr (reverse (cdr (cdr form))))))) - (nconc (list 'funcall fn) butlast - (mapcar (lambda (x) (list 'quote x)) (nth 1 last)))) - (byte-compile-warn - "last arg to apply can't be a literal atom: `%s'" - (prin1-to-string last)) - nil)) - form))) + (if (= (length form) 2) + ;; single-argument `apply' is special (bug#40968) + (byte-optimize-apply `(apply #'funcall ,(cadr form))) + (let ((fn (nth 1 form)) + (last (nth (1- (length form)) form))) ; I think this really is fastest + (or (if (or (null last) + (eq (car-safe last) 'quote)) + (if (listp (nth 1 last)) + (let ((butlast (nreverse (cdr (reverse (cdr (cdr form))))))) + (nconc (list 'funcall fn) butlast + (mapcar (lambda (x) (list 'quote x)) (nth 1 last)))) + (byte-compile-warn + "last arg to apply can't be a literal atom: `%s'" + (prin1-to-string last)) + nil)) + form)))) (put 'funcall 'byte-optimizer 'byte-optimize-funcall) (put 'apply 'byte-optimizer 'byte-optimize-apply) diff --git a/src/eval.c b/src/eval.c index 014905ce6d..77f54ad7b1 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2361,6 +2361,8 @@ eval_sub (Lisp_Object form) DEFUN ("apply", Fapply, Sapply, 1, MANY, 0, doc: /* Call FUNCTION with our remaining args, using our last arg as list of args. Then return the value FUNCTION returns. +With a single argument, call the argument's first element using the +other elements as args. Thus, (apply \\='+ 1 2 \\='(3 4)) returns 10. usage: (apply FUNCTION &rest ARGUMENTS) */) (ptrdiff_t nargs, Lisp_Object *args) @@ -2373,6 +2375,9 @@ DEFUN ("apply", Fapply, Sapply, 1, MANY, 0, ptrdiff_t numargs = list_length (spread_arg); + if (numargs == 0 && nargs == 1) + wrong_type_argument (Qconsp, spread_arg); + if (numargs == 0) return Ffuncall (nargs - 1, args); else if (numargs == 1) -- 2.26.2 ^ permalink raw reply related [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 17:54 ` Pip Cet @ 2020-05-06 18:09 ` Eli Zaretskii 0 siblings, 0 replies; 44+ messages in thread From: Eli Zaretskii @ 2020-05-06 18:09 UTC (permalink / raw) To: Pip Cet; +Cc: stefan, 40968 > From: Pip Cet <pipcet@gmail.com> > Date: Wed, 6 May 2020 17:54:23 +0000 > Cc: Stefan Kangas <stefan@marxist.se>, 40968@debbugs.gnu.org > > > Can we instead identify the problematic usage and signal an error? > > Yes. What we also have to do is fix the documentation, and fix the > byte optimizer. > > Here's a first suggestion. I'd particularly appreciate hints on better > wording for the documentation. The documentation parts of the patch look fine to me, thanks. (The rest also looks fine.) Let's wait for a few days so others could comment. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 13:02 ` Stefan Kangas 2020-05-06 13:55 ` Pip Cet 2020-05-06 14:03 ` Eli Zaretskii @ 2020-05-06 18:00 ` Drew Adams 2020-05-06 18:28 ` Noam Postavsky 2 siblings, 1 reply; 44+ messages in thread From: Drew Adams @ 2020-05-06 18:00 UTC (permalink / raw) To: Stefan Kangas, Pip Cet; +Cc: 40968 > > (apply FUNCTION ARGUMENT &rest ARGUMENTS), I guess. I missed it in > > the first patch. > > FWIW, I still think we should avoid changing the function signature if > at all possible. > > apply is fundamental to Lisp, and has been defined like this for a > long time. See the definition of "The Universal S-Function apply" in > John McCarthy's paper: [1] > > apply[f;args] =eval[cons[f;appq[args]];NIL], > > Also note that it is still defined like this elsewhere: > > Scheme: (apply function argument-list) > Common Lisp: apply function &rest args+ => result* > Clojure: (apply f args) Hm. CLTL2 shows the signature for Common Lisp as this: apply function arg &rest more-args ^^^ https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node81.html On the other hand the CL HyperSpec agrees with you: apply function &rest args+ => result* http://www.lispworks.com/documentation/HyperSpec/Body/f_apply.htm Dunno what the truth is. I've always considered CLTL the language spec. But that may be wrong. What's the use case for (apply FUN)? ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 18:00 ` Drew Adams @ 2020-05-06 18:28 ` Noam Postavsky 2020-05-06 19:17 ` Drew Adams 0 siblings, 1 reply; 44+ messages in thread From: Noam Postavsky @ 2020-05-06 18:28 UTC (permalink / raw) To: Drew Adams; +Cc: Stefan Kangas, 40968, Pip Cet Drew Adams <drew.adams@oracle.com> writes: > apply function arg &rest more-args > ^^^ > > https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node81.html > > On the other hand the CL HyperSpec agrees with you: > > apply function &rest args+ => result* > > http://www.lispworks.com/documentation/HyperSpec/Body/f_apply.htm I think '&rest args+' is just BNF shorthand for 'arg &rest more-args' (i.e., both signatures above require a total of 2 or more arguments). http://www.lispworks.com/documentation/HyperSpec/Body/01_dab.htm ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 18:28 ` Noam Postavsky @ 2020-05-06 19:17 ` Drew Adams 2020-05-06 19:21 ` Pip Cet 0 siblings, 1 reply; 44+ messages in thread From: Drew Adams @ 2020-05-06 19:17 UTC (permalink / raw) To: Noam Postavsky; +Cc: Stefan Kangas, 40968, Pip Cet > > apply function arg &rest more-args > > ^^^ > > > https://urldefense.com/v3/__https://www.cs.cmu.edu/Groups/AI/html/cltl/ > clm/node81.html__;!!GqivPVa7Brio!Kes9zsDqvdTyifr0LvhIg- > x3qHNl3XPQZaBRBxbjXsP-qGz7ieoaYPp6rfGGzbvg$ > > > > On the other hand the CL HyperSpec agrees with you: > > > > apply function &rest args+ => result* > > > https://urldefense.com/v3/__http://www.lispworks.com/documentation/Hype > rSpec/Body/f_apply.htm__;!!GqivPVa7Brio!Kes9zsDqvdTyifr0LvhIg- > x3qHNl3XPQZaBRBxbjXsP-qGz7ieoaYPp6rY83YOj5$ > > I think '&rest args+' is just BNF shorthand for 'arg &rest more-args' > (i.e., both signatures above require a total of 2 or more arguments). > > https://urldefense.com/v3/__http://www.lispworks.com/documentation/Hype > rSpec/Body/01_dab.htm__;!!GqivPVa7Brio!Kes9zsDqvdTyifr0LvhIg- > x3qHNl3XPQZaBRBxbjXsP-qGz7ieoaYPp6rbGmUXoP$ Ah yes; thanks. So Common Lisp is unequivocally in the camp of requiring at least two args. And if Elisp follows that (which I think it should, unless someone can present a good use case for just (apply FUNCTION)), then it should raise an error if there are not at least two args. Just one opinion. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 19:17 ` Drew Adams @ 2020-05-06 19:21 ` Pip Cet 2020-05-06 19:28 ` Drew Adams 0 siblings, 1 reply; 44+ messages in thread From: Pip Cet @ 2020-05-06 19:21 UTC (permalink / raw) To: Drew Adams; +Cc: Stefan Kangas, 40968, Noam Postavsky On Wed, May 6, 2020 at 7:17 PM Drew Adams <drew.adams@oracle.com> wrote: > And if Elisp follows that (which I think it should, > unless someone can present a good use case for just > (apply FUNCTION)), then it should raise an error if > there are not at least two args. Note that single-argument apply in current Elisp is not (apply FUNCTION) but (apply LIST-OF-FUNCTION-PLUS-ARGS). (apply '+) is an error. (apply '(+)) is 0. That's why the problem with (apply nil) arose. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 19:21 ` Pip Cet @ 2020-05-06 19:28 ` Drew Adams 2020-05-07 2:27 ` Eli Zaretskii 0 siblings, 1 reply; 44+ messages in thread From: Drew Adams @ 2020-05-06 19:28 UTC (permalink / raw) To: Pip Cet; +Cc: Stefan Kangas, 40968, Noam Postavsky > > And if Elisp follows that (which I think it should, > > unless someone can present a good use case for just > > (apply FUNCTION)), then it should raise an error if > > there are not at least two args. > > Note that single-argument apply in current Elisp is not (apply > FUNCTION) but (apply LIST-OF-FUNCTION-PLUS-ARGS). > > (apply '+) is an error. > (apply '(+)) is 0. > > That's why the problem with (apply nil) arose. I think (apply '(+)) should raise an error, because (a) the first arg is not a function and (b) it's missing a second arg. I think (apply ()) should raise an error for the same reason: nil is not a function, and there's no second arg. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 19:28 ` Drew Adams @ 2020-05-07 2:27 ` Eli Zaretskii 0 siblings, 0 replies; 44+ messages in thread From: Eli Zaretskii @ 2020-05-07 2:27 UTC (permalink / raw) To: Drew Adams; +Cc: stefan, 40968, npostavs, pipcet > Date: Wed, 6 May 2020 12:28:21 -0700 (PDT) > From: Drew Adams <drew.adams@oracle.com> > Cc: Stefan Kangas <stefan@marxist.se>, 40968@debbugs.gnu.org, > Noam Postavsky <npostavs@gmail.com> > > I think (apply '(+)) should raise an error, because > (a) the first arg is not a function and (b) it's > missing a second arg. That'd be an incompatible change, so it is best not to make such a change. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 11:49 ` Pip Cet 2020-05-06 13:02 ` Stefan Kangas @ 2020-05-06 17:46 ` Drew Adams 2020-05-06 20:32 ` Phil Sainty 2 siblings, 0 replies; 44+ messages in thread From: Drew Adams @ 2020-05-06 17:46 UTC (permalink / raw) To: Pip Cet, Stefan Kangas; +Cc: 40968 Dunno whether this has been mentioned in this thread (haven't followed it). The signature in Common Lisp (which is more or less the _common_ ground of several Lisps from the 70s & 80s) is this: (apply function arg &rest more-args) I don't see why Emacs Lisp should be different. Is there a good reason? Is there some advantage to being able to do just (apply #'foo)? Why do we use this signature: (apply FUNCTION &rest ARGUMENTS) https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node81.html ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 11:49 ` Pip Cet 2020-05-06 13:02 ` Stefan Kangas 2020-05-06 17:46 ` Drew Adams @ 2020-05-06 20:32 ` Phil Sainty 2020-05-06 21:35 ` Drew Adams 2 siblings, 1 reply; 44+ messages in thread From: Phil Sainty @ 2020-05-06 20:32 UTC (permalink / raw) To: Pip Cet; +Cc: Stefan Kangas, 40968 On 2020-05-06 23:49, Pip Cet wrote: > Again, I'm no longer sure what the right thing to do here is. I think > I'm up to five different interpretations of (apply ARG) here, > depending on whether ARG is interpreted as FUNCTION, the last of the > ARGUMENTS, an ARGUMENT but not the last one, or a combination thereof, > and whether ARG is required to be a list or not... To my mind the nicest change would be to handle the two error cases, and keep everything else the same. 1. (apply nil) would signal an error. 2. (apply FUNC) would be equivalent to funcall, rather than signalling an error, when (functionp FUNC) -- or perhaps just (not (consp FUNC)). 3. (apply LIST) would remain equivalent to (apply (car LIST) (cdr LIST)) I don't feel strongly about #2. It seems like a nice enhancement to me, but if others feel that would be cause problems then I wouldn't argue. Existing uses of that in the wild are obviously signalling errors at present, so offhand it doesn't seem to me like a dangerous change, and it would match the existing signature. #3 just seems like the only useful thing that apply could possibly do with a single list argument, so I'd definitely keep that. #2 and #3 are surely both convenient for generated code which doesn't know how many arguments it's going to be dealing with. I would still use (FUNCTION &optional ARGS) as the signature, and just document what happens when FUNCTION is actually a list. -Phil ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 20:32 ` Phil Sainty @ 2020-05-06 21:35 ` Drew Adams 0 siblings, 0 replies; 44+ messages in thread From: Drew Adams @ 2020-05-06 21:35 UTC (permalink / raw) To: Phil Sainty, Pip Cet; +Cc: Stefan Kangas, 40968 > To my mind the nicest change would be to handle the two error cases, > and keep everything else the same. > > 1. (apply nil) would signal an error. > 2. (apply FUNC) would be equivalent to funcall, rather than signalling > an error, when (functionp FUNC) -- or perhaps just (not (consp > FUNC)). > 3. (apply LIST) would remain equivalent to (apply (car LIST) (cdr > LIST)) > > I don't feel strongly about #2. It seems like a nice enhancement to > me, > but if others feel that would be cause problems then I wouldn't argue. > Existing uses of that in the wild are obviously signalling errors at > present, so offhand it doesn't seem to me like a dangerous change, and > it would match the existing signature. > > #3 just seems like the only useful thing that apply could possibly do > with a single list argument, so I'd definitely keep that. > > #2 and #3 are surely both convenient for generated code which doesn't > know how many arguments it's going to be dealing with. > > I would still use (FUNCTION &optional ARGS) as the signature, and just > document what happens when FUNCTION is actually a list. What's wrong with doing what Common Lisp does (apparently, per the doc'd signature)? (apply FUNCTION first-arg &rest other-args) The first arg to `apply' is required, and must be a function. The second arg to `apply' is required. Any arg after the second is optional. The last arg to `apply' must be a list. (This is true even if it is the second arg.) It can be nil. * If the last arg is the second arg, then its elements are the args passed to FUNCTION. (If it is the empty list then FUNCTION must be nullary.) * If the last arg is not the second arg, then its elements are the Nth args for FUNCTION, where N = 1+ the element index. FUNCTION must be able to accept M args, where M = 1+ the number of elements in the last arg. "Must" means an error is raised if not so. (apply) => error (apply ANYTHING) => error (apply FUNCTION '(x) ) => (funcall FUNCTION x) ; last arg: singleton list of args (apply FUNCTION x ()) => (funcall FUNCTION x) ; first arg: x ; last arg: empty list of other args (apply FUNCTION () ) => (funcall FUNCTION) ; last arg: empty list of args (apply FUNCTION () ()) => (funcall FUNCTION nil) ; first arg nil ; last arg: empty list of other args ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 7:26 ` Pip Cet 2020-05-06 11:24 ` Stefan Kangas @ 2020-09-29 3:00 ` Stefan Monnier 1 sibling, 0 replies; 44+ messages in thread From: Stefan Monnier @ 2020-09-29 3:00 UTC (permalink / raw) To: Pip Cet; +Cc: Stefan Kangas, 40968 > as implying that (apply FUNCTION) is equivalent to (funcall FUNCTION) I think the current half-broken semantics is a fairly natural generalization: (apply ... ARGS) is equivalent to (funcall ... ,@ARGS) so when `...` is empty it means that (apply ARGS) should be equivalent to (funcall ,@ARGS) aka (apply (car ARGS) (cdr ARGS)) When ARGS is the empty list we should probably signal an error (just like (apply (car ARGS) (cdr ARGS)) would, BTW). Stefan ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-04-29 18:26 Pip Cet 2020-04-29 18:35 ` Pip Cet 2020-05-06 1:51 ` Stefan Kangas @ 2020-05-06 10:18 ` Mattias Engdegård 2020-05-06 10:45 ` Eli Zaretskii 2020-05-06 11:25 ` Pip Cet 2020-05-06 18:42 ` Mattias Engdegård 3 siblings, 2 replies; 44+ messages in thread From: Mattias Engdegård @ 2020-05-06 10:18 UTC (permalink / raw) To: Pip Cet; +Cc: Stefan Kangas, 40968 > The byte compiler also gets single-argument `apply' "wrong", so the latter would seem to be the best option, to me. Strong agreement! An 'apply' that actually does 'eval'... Will you write the patch or shall I? ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 10:18 ` Mattias Engdegård @ 2020-05-06 10:45 ` Eli Zaretskii 2020-05-06 10:57 ` Andreas Schwab 2020-05-06 11:25 ` Pip Cet 1 sibling, 1 reply; 44+ messages in thread From: Eli Zaretskii @ 2020-05-06 10:45 UTC (permalink / raw) To: 40968, mattiase, pipcet, rms; +Cc: stefan On May 6, 2020 1:18:44 PM GMT+03:00, "Mattias Engdegård" <mattiase@acm.org> wrote: > > The byte compiler also gets single-argument `apply' "wrong", so the > latter would seem to be the best option, to me. > > Strong agreement! An 'apply' that actually does 'eval'... > Will you write the patch or shall I? Hmm... but Fapply clearly attempts to handle the case of zero arguments. Richard do you remember what use cases was that supposed to handle? ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 10:45 ` Eli Zaretskii @ 2020-05-06 10:57 ` Andreas Schwab 0 siblings, 0 replies; 44+ messages in thread From: Andreas Schwab @ 2020-05-06 10:57 UTC (permalink / raw) To: Eli Zaretskii; +Cc: mattiase, 40968, stefan, rms, pipcet On Mai 06 2020, Eli Zaretskii wrote: > Hmm... but Fapply clearly attempts to handle the case of zero > arguments. I don't think it does. It handles (apply ... nil) (numargs is the length of the last argument), but mishandles the boundary case (when nargs is one). Andreas. -- Andreas Schwab, schwab@linux-m68k.org GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510 2552 DF73 E780 A9DA AEC1 "And now for something completely different." ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 10:18 ` Mattias Engdegård 2020-05-06 10:45 ` Eli Zaretskii @ 2020-05-06 11:25 ` Pip Cet 2020-05-06 11:49 ` Mattias Engdegård 1 sibling, 1 reply; 44+ messages in thread From: Pip Cet @ 2020-05-06 11:25 UTC (permalink / raw) To: Mattias Engdegård; +Cc: Stefan Kangas, 40968 [-- Attachment #1: Type: text/plain, Size: 555 bytes --] On Wed, May 6, 2020 at 10:18 AM Mattias Engdegård <mattiase@acm.org> wrote: > > The byte compiler also gets single-argument `apply' "wrong", so the latter would seem to be the best option, to me. > > Strong agreement! An 'apply' that actually does 'eval'... Well, it's not quite `eval': the arguments aren't evaluated again. The obvious single-character patch results in four warnings when rebuilding Emacs, all of which seem to use the previous semantics, so I think it might be best to document this behavior and simply catch (apply nil). [-- Attachment #2: 0001-Require-at-least-two-arguments-for-Fapply.patch --] [-- Type: text/x-patch, Size: 3518 bytes --] From 898af394fcbde57ca3d08e3648b62fe0b3f41911 Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet@gmail.com> Date: Wed, 6 May 2020 11:08:29 +0000 Subject: [PATCH] Require at least two arguments for Fapply * src/eval.c (Fapply): Require at least two arguments, to prevent crashes when (apply nil) is evaluated. * lisp/progmodes/ebrowse.el (ebrowse-tags-loop-continue): Provide two arguments to `apply'. * lisp/svg.el (svg--eval-path-command): Provide two arguments to `apply'. * lisp/simple.el (primitive-undo): Provide two arguments to `apply'. --- lisp/net/telnet.el | 3 ++- lisp/progmodes/ebrowse.el | 3 ++- lisp/simple.el | 2 +- lisp/svg.el | 3 ++- src/eval.c | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lisp/net/telnet.el b/lisp/net/telnet.el index e8c0c1bbdf..b0350eacbf 100644 --- a/lisp/net/telnet.el +++ b/lisp/net/telnet.el @@ -109,7 +109,8 @@ telnet-revert-buffer (revert-buffer ignore-auto noconfirm)) (if (or noconfirm (yes-or-no-p (format "Restart connection? "))) - (apply telnet-connect-command)))) + (apply (car telnet-connect-command) + (cdr telnet-connect-command))))) (defun telnet-c-z () (interactive) diff --git a/lisp/progmodes/ebrowse.el b/lisp/progmodes/ebrowse.el index 1c9e805f03..f052266b1a 100644 --- a/lisp/progmodes/ebrowse.el +++ b/lisp/progmodes/ebrowse.el @@ -3612,7 +3612,8 @@ ebrowse-tags-loop-continue (when first-time (ebrowse-tags-next-file first-time tree-buffer) (goto-char (point-min))) - (while (not (apply ebrowse-tags-loop-call)) + (while (not (apply (car ebrowse-tags-loop-call) + (cdr ebrowse-tags-loop-call))) (ebrowse-tags-next-file) (message "Scanning file `%s'..." buffer-file-name) (goto-char (point-min)))) diff --git a/lisp/simple.el b/lisp/simple.el index b5ba05426f..9a96ce0013 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -2778,7 +2778,7 @@ primitive-undo (error "Changes to be undone by function different from announced")) (set-marker start-mark nil) (set-marker end-mark nil)) - (apply fun-args)) + (apply (car fun-args) (cdr fun-args))) (unless (eq currbuff (current-buffer)) (error "Undo function switched buffer")) (setq did-apply t))) diff --git a/lisp/svg.el b/lisp/svg.el index 7aadbc2359..982e2b0965 100644 --- a/lisp/svg.el +++ b/lisp/svg.el @@ -437,7 +437,8 @@ svg--eval-path-command #'svg--elliptical-arc-command) (extended-command (append command (list :default-relative default-relative)))) - (mapconcat 'prin1-to-string (apply extended-command) " "))) + (mapconcat 'prin1-to-string (apply (car extended-command) + (cdr extended-command)) " "))) (defun svg-path (svg commands &rest args) "Add the outline of a shape to SVG according to COMMANDS. diff --git a/src/eval.c b/src/eval.c index 014905ce6d..1b9fd90482 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2358,7 +2358,7 @@ eval_sub (Lisp_Object form) return val; } \f -DEFUN ("apply", Fapply, Sapply, 1, MANY, 0, +DEFUN ("apply", Fapply, Sapply, 2, MANY, 0, doc: /* Call FUNCTION with our remaining args, using our last arg as list of args. Then return the value FUNCTION returns. Thus, (apply \\='+ 1 2 \\='(3 4)) returns 10. -- 2.26.2 ^ permalink raw reply related [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 11:25 ` Pip Cet @ 2020-05-06 11:49 ` Mattias Engdegård 0 siblings, 0 replies; 44+ messages in thread From: Mattias Engdegård @ 2020-05-06 11:49 UTC (permalink / raw) To: Pip Cet; +Cc: Stefan Kangas, 40968 6 maj 2020 kl. 13.25 skrev Pip Cet <pipcet@gmail.com>: > The obvious single-character patch results in four warnings when > rebuilding Emacs, all of which seem to use the previous semantics, so > I think it might be best to document this behavior and simply catch > (apply nil). There is one in cl-generic-tests.el, too. It appears in one GNU ELPA package as well, and in one other external package where it seems to be a misunderstanding of apply: (cl-defun nim-log (&rest msg-and-rest) (apply `((lambda () (lwarn 'nim :debug ,@msg-and-rest))))) ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-04-29 18:26 Pip Cet ` (2 preceding siblings ...) 2020-05-06 10:18 ` Mattias Engdegård @ 2020-05-06 18:42 ` Mattias Engdegård 2020-05-07 6:53 ` Pip Cet 3 siblings, 1 reply; 44+ messages in thread From: Mattias Engdegård @ 2020-05-06 18:42 UTC (permalink / raw) To: Pip Cet; +Cc: 40968, Stefan Kangas [-- Attachment #1: Type: text/plain, Size: 346 bytes --] That patch is fine but perhaps incomplete, if we want (apply nil) to result in the same error when interpreted and byte-compiled. Suggested test case attached. The manual change is fine. Perhaps we should adopt a somewhat discouraging tone. You could also say that (apply X) is defined as (apply (car X) (cdr X)), and X must be a cons. [-- Attachment #2: test.diff --] [-- Type: application/octet-stream, Size: 746 bytes --] diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index bfe2d06a61..65fd57b4e0 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el @@ -352,7 +352,14 @@ byte-opt-testsuite-arith-data ;; `substring' bytecode generation (bug#39709). (substring "abcdef") (substring "abcdef" 2) - (substring "abcdef" 3 2)) + (substring "abcdef" 3 2) + + ;; `apply' with single argument (bug#40968). + (apply '(+ 2 3 5)) + (condition-case nil + (apply nil) + (wrong-type-argument 'wrong-arg)) + ) "List of expression for test. Each element will be executed by interpreter and with bytecompiled code, and their results compared.") ^ permalink raw reply related [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-06 18:42 ` Mattias Engdegård @ 2020-05-07 6:53 ` Pip Cet 2020-05-07 9:11 ` Mattias Engdegård 0 siblings, 1 reply; 44+ messages in thread From: Pip Cet @ 2020-05-07 6:53 UTC (permalink / raw) To: Mattias Engdegård; +Cc: 40968, Stefan Kangas enterOn Wed, May 6, 2020 at 6:42 PM Mattias Engdegård <mattiase@acm.org> wrote: > That patch is fine but perhaps incomplete, if we want (apply nil) to result in the same error when interpreted and byte-compiled. It is incomplete. However, I've yet to find an elegant way to fix the byte compiler and get it to emit the right error message. Can you think of one? (apply nil) is easy to fix, of course, but (apply (function-returning-nil)) is more difficult. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-07 6:53 ` Pip Cet @ 2020-05-07 9:11 ` Mattias Engdegård 2020-05-07 11:54 ` Noam Postavsky 0 siblings, 1 reply; 44+ messages in thread From: Mattias Engdegård @ 2020-05-07 9:11 UTC (permalink / raw) To: Pip Cet; +Cc: 40968, Stefan Kangas 7 maj 2020 kl. 08.53 skrev Pip Cet <pipcet@gmail.com>: > It is incomplete. However, I've yet to find an elegant way to fix the > byte compiler and get it to emit the right error message. Can you > think of one? You are right, that's a puzzle. No, I cannot think of an elegant way (but a few inelegant ones). It's probably not worth the trouble; just change the error test case to (condition-case nil (apply nil) (error 'some-error)) ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-07 9:11 ` Mattias Engdegård @ 2020-05-07 11:54 ` Noam Postavsky 2020-05-07 11:58 ` Pip Cet 2020-05-07 13:53 ` Mattias Engdegård 0 siblings, 2 replies; 44+ messages in thread From: Noam Postavsky @ 2020-05-07 11:54 UTC (permalink / raw) To: Mattias Engdegård; +Cc: Stefan Kangas, 40968, Pip Cet Mattias Engdegård <mattiase@acm.org> writes: > 7 maj 2020 kl. 08.53 skrev Pip Cet <pipcet@gmail.com>: > >> It is incomplete. However, I've yet to find an elegant way to fix the >> byte compiler and get it to emit the right error message. Can you >> think of one? > > You are right, that's a puzzle. No, I cannot think of an elegant way > (but a few inelegant ones). The obvious solution is just to leave the weird single arg form unoptimized. Otherwise, what about your earlier suggestion? (apply X) == (apply (car X) (cdr X)) > It's probably not worth the trouble; just change the error test case By the way, bytecomp-check-1 already ignores differences between error types. So the test case doesn't need a condition-case at all if we don't care about which particular error is signalled. --- i/lisp/emacs-lisp/byte-opt.el +++ w/lisp/emacs-lisp/byte-opt.el @@ -1100,7 +1100,7 @@ byte-optimize-apply ;; The funcall optimizer can then transform (funcall 'foo ...) -> (foo ...). (if (= (length form) 2) ;; single-argument `apply' is special (bug#40968) - (byte-optimize-apply `(apply #'funcall ,(cadr form))) + (byte-optimize-apply `(apply (car ,(cadr form)) (cdr ,(cadr form)))) (let ((fn (nth 1 form)) (last (nth (1- (length form)) form))) ; I think this really is fastest (or (if (or (null last) diff --git i/src/eval.c w/src/eval.c index 77f54ad7b1..836be7a906 100644 --- i/src/eval.c +++ w/src/eval.c @@ -2373,10 +2373,11 @@ DEFUN ("apply", Fapply, Sapply, 1, MANY, 0, Lisp_Object fun = args[0]; USE_SAFE_ALLOCA; - ptrdiff_t numargs = list_length (spread_arg); + if (nargs == 1) + /* Special case: FUN is really a list of (FUNCTION . ARGS). */ + return CALLN (Fapply, CAR (fun), CDR (fun)); - if (numargs == 0 && nargs == 1) - wrong_type_argument (Qconsp, spread_arg); + ptrdiff_t numargs = list_length (spread_arg); if (numargs == 0) return Ffuncall (nargs - 1, args); ^ permalink raw reply related [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-07 11:54 ` Noam Postavsky @ 2020-05-07 11:58 ` Pip Cet 2020-05-07 12:20 ` Noam Postavsky 2020-05-07 13:53 ` Mattias Engdegård 1 sibling, 1 reply; 44+ messages in thread From: Pip Cet @ 2020-05-07 11:58 UTC (permalink / raw) To: Noam Postavsky; +Cc: Mattias Engdegård, Stefan Kangas, 40968 On Thu, May 7, 2020 at 11:54 AM Noam Postavsky <npostavs@gmail.com> wrote: > - (byte-optimize-apply `(apply #'funcall ,(cadr form))) > + (byte-optimize-apply `(apply (car ,(cadr form)) (cdr ,(cadr form)))) That double-evaluates (cadr form), doesn't it? > - ptrdiff_t numargs = list_length (spread_arg); > + if (nargs == 1) > + /* Special case: FUN is really a list of (FUNCTION . ARGS). */ > + return CALLN (Fapply, CAR (fun), CDR (fun)); But what if someone defines nil as a function (not serious)? (apply nil) gets translated to (apply nil nil) which I guess will throw an error, so that's okay. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-07 11:58 ` Pip Cet @ 2020-05-07 12:20 ` Noam Postavsky 0 siblings, 0 replies; 44+ messages in thread From: Noam Postavsky @ 2020-05-07 12:20 UTC (permalink / raw) To: Pip Cet; +Cc: Mattias Engdegård, 40968, Stefan Kangas Pip Cet <pipcet@gmail.com> writes: > On Thu, May 7, 2020 at 11:54 AM Noam Postavsky <npostavs@gmail.com> wrote: >> - (byte-optimize-apply `(apply #'funcall ,(cadr form))) >> + (byte-optimize-apply `(apply (car ,(cadr form)) (cdr ,(cadr form)))) > > That double-evaluates (cadr form), doesn't it? Oops, right. Then let me go back to "don't optimize that case" (i.e., just return form). >> + if (nargs == 1) >> + /* Special case: FUN is really a list of (FUNCTION . ARGS). */ >> + return CALLN (Fapply, CAR (fun), CDR (fun)); > > But what if someone defines nil as a function (not serious)? Emacs doesn't care if you're serious; it has an answer ready for you anyway: (defun nil () t) ;=> Cannot define ’nil’ as a function ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-07 11:54 ` Noam Postavsky 2020-05-07 11:58 ` Pip Cet @ 2020-05-07 13:53 ` Mattias Engdegård 2020-06-02 7:36 ` Pip Cet 1 sibling, 1 reply; 44+ messages in thread From: Mattias Engdegård @ 2020-05-07 13:53 UTC (permalink / raw) To: Noam Postavsky; +Cc: Stefan Kangas, 40968, Pip Cet [-- Attachment #1: Type: text/plain, Size: 616 bytes --] 7 maj 2020 kl. 13.54 skrev Noam Postavsky <npostavs@gmail.com>: > The obvious solution is just to leave the weird single arg form > unoptimized. Otherwise, what about your earlier suggestion? > > (apply X) == (apply (car X) (cdr X)) Yes, we are under no obligation to optimise this case in any way. > By the way, bytecomp-check-1 already ignores differences between error > types. So the test case doesn't need a condition-case at all if we > don't care about which particular error is signalled. It seems to treat all errors as producing the value nil, but that should of course be fixed. [-- Attachment #2: bytecomp-tests.diff --] [-- Type: application/octet-stream, Size: 2470 bytes --] diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index bfe2d06a61..c235dd43fc 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el @@ -363,10 +363,10 @@ bytecomp-check-1 (byte-compile-warnings nil) (v0 (condition-case nil (eval pat) - (error nil))) + (error 'bytecomp-check-error))) (v1 (condition-case nil (funcall (byte-compile (list 'lambda nil pat))) - (error nil)))) + (error 'bytecomp-check-error)))) (equal v0 v1))) (put 'bytecomp-check-1 'ert-explainer 'bytecomp-explain-1) @@ -374,10 +374,10 @@ bytecomp-check-1 (defun bytecomp-explain-1 (pat) (let ((v0 (condition-case nil (eval pat) - (error nil))) + (error 'bytecomp-check-error))) (v1 (condition-case nil (funcall (byte-compile (list 'lambda nil pat))) - (error nil)))) + (error 'bytecomp-check-error)))) (format "Expression `%s' gives `%s' if directly evalled, `%s' if compiled." pat v0 v1))) @@ -402,10 +402,10 @@ test-byte-opt-arithmetic (dolist (pat byte-opt-testsuite-arith-data) (condition-case nil (setq v0 (eval pat)) - (error (setq v0 nil))) + (error (setq v0 'bytecomp-check-error))) (condition-case nil (setq v1 (funcall (byte-compile (list 'lambda nil pat)))) - (error (setq v1 nil))) + (error (setq v1 'bytecomp-check-error))) (insert (format "%s" pat)) (indent-to-column 65) (if (equal v0 v1) @@ -561,11 +561,11 @@ bytecomp-lexbind-check-1 (byte-compile-warnings nil) (v0 (condition-case nil (eval pat t) - (error nil))) + (error 'bytecomp-check-error))) (v1 (condition-case nil (funcall (let ((lexical-binding t)) (byte-compile `(lambda nil ,pat)))) - (error nil)))) + (error 'bytecomp-check-error)))) (equal v0 v1))) (put 'bytecomp-lexbind-check-1 'ert-explainer 'bytecomp-lexbind-explain-1) @@ -573,11 +573,11 @@ bytecomp-lexbind-check-1 (defun bytecomp-lexbind-explain-1 (pat) (let ((v0 (condition-case nil (eval pat t) - (error nil))) + (error 'bytecomp-check-error))) (v1 (condition-case nil (funcall (let ((lexical-binding t)) (byte-compile (list 'lambda nil pat)))) - (error nil)))) + (error 'bytecomp-check-error)))) (format "Expression `%s' gives `%s' if directly evalled, `%s' if compiled." pat v0 v1))) ^ permalink raw reply related [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-05-07 13:53 ` Mattias Engdegård @ 2020-06-02 7:36 ` Pip Cet 2020-06-02 16:32 ` Drew Adams ` (2 more replies) 0 siblings, 3 replies; 44+ messages in thread From: Pip Cet @ 2020-06-02 7:36 UTC (permalink / raw) To: Mattias Engdegård; +Cc: Stefan Kangas, 40968, Noam Postavsky [-- Attachment #1: Type: text/plain, Size: 563 bytes --] On Thu, May 7, 2020 at 1:53 PM Mattias Engdegård <mattiase@acm.org> wrote: > 7 maj 2020 kl. 13.54 skrev Noam Postavsky <npostavs@gmail.com>: > > > The obvious solution is just to leave the weird single arg form > > unoptimized. Otherwise, what about your earlier suggestion? > > > > (apply X) == (apply (car X) (cdr X)) > > Yes, we are under no obligation to optimise this case in any way. I think there's consensus, then. I've updated the documentation, in the patch, to state that providing two or more arguments is faster. Patch attached. [-- Attachment #2: 0001-Handle-single-argument-apply-consistently-bug-40968.patch --] [-- Type: application/x-patch, Size: 4278 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-06-02 7:36 ` Pip Cet @ 2020-06-02 16:32 ` Drew Adams 2020-06-02 16:43 ` Eli Zaretskii 2020-06-02 16:36 ` Eli Zaretskii 2020-09-27 15:01 ` Lars Ingebrigtsen 2 siblings, 1 reply; 44+ messages in thread From: Drew Adams @ 2020-06-02 16:32 UTC (permalink / raw) To: Pip Cet, Mattias Engdegård; +Cc: Stefan Kangas, 40968, Noam Postavsky > I think there's consensus, then. FTR, FWIW: Not a consensus that includes me. I'm in favor of making an incompatible change, to align Emacs with Common Lisp's more reasonable behavior. As I said: Common Lisp is unequivocally in the camp of requiring at least two args. And if Elisp follows that (which I think it should, unless someone can present a good use case for just (apply FUNCTION)), then it should raise an error if there are not at least two args. The only good case presented was to say that this would be an incompatible change. Yes, it would, and we should make it. If you want to, temporarily, issue a compile-time and runtime warning, instead of raising an error, OK. But Emacs should wean code and users off of the undesirable (IMO) behavior that's been allowed. The doc should be changed to discourage such use, and users should be warned that it's deprecated. Just one, non-consensual, opinion. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-06-02 16:32 ` Drew Adams @ 2020-06-02 16:43 ` Eli Zaretskii 0 siblings, 0 replies; 44+ messages in thread From: Eli Zaretskii @ 2020-06-02 16:43 UTC (permalink / raw) To: Drew Adams; +Cc: mattiase, stefan, 40968, npostavs, pipcet > From: Drew Adams <drew.adams@oracle.com> > Cc: Stefan Kangas <stefan@marxist.se>, 40968@debbugs.gnu.org, > Noam Postavsky <npostavs@gmail.com> > > Common Lisp is unequivocally in the camp of > requiring at least two args. We will not require minimum 2 args because that would be backward incompatible. I'm quite sure I already said that before. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-06-02 7:36 ` Pip Cet 2020-06-02 16:32 ` Drew Adams @ 2020-06-02 16:36 ` Eli Zaretskii 2020-09-27 15:01 ` Lars Ingebrigtsen 2 siblings, 0 replies; 44+ messages in thread From: Eli Zaretskii @ 2020-06-02 16:36 UTC (permalink / raw) To: Pip Cet; +Cc: mattiase, stefan, 40968, npostavs > From: Pip Cet <pipcet@gmail.com> > Date: Tue, 2 Jun 2020 07:36:26 +0000 > Cc: Noam Postavsky <npostavs@gmail.com>, Stefan Kangas <stefan@marxist.se>, Eli Zaretskii <eliz@gnu.org>, > 40968@debbugs.gnu.org > > +@code{apply} with a single argument is special: the first element of > +the argument, which must be a non-empty list, is called as a function > +with the remaining elements as individual arguments. Passing two or > +more arguments will be faster. This is okay, but I think the last sentence could be more explicit if reworded like this: However, we recommend always passing 2 or more arguments, as the function works faster in that case. Thanks. ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-06-02 7:36 ` Pip Cet 2020-06-02 16:32 ` Drew Adams 2020-06-02 16:36 ` Eli Zaretskii @ 2020-09-27 15:01 ` Lars Ingebrigtsen 2020-09-27 19:28 ` Drew Adams 2 siblings, 1 reply; 44+ messages in thread From: Lars Ingebrigtsen @ 2020-09-27 15:01 UTC (permalink / raw) To: Pip Cet; +Cc: Mattias Engdegård, 40968, Stefan Kangas, Noam Postavsky Pip Cet <pipcet@gmail.com> writes: > * src/eval.c (Fapply): Handle (apply nil) without crashing. > Document single-argument form. > * lisp/emacs-lisp/byte-opt.el (byte-optimize-apply): Don't attempt > to optimize single-argument apply. > * doc/lispref/functions.texi (Calling Functions): Document > single-argument apply. Provide example. It looked like most everybody was in agreement with this patch, and it fixes the (apply nil) crash, so I've applied it to Emacs 28 now. There was some followup on the details of the documentation, with several suggested patches, and now that this is on the trunk, people can go ahead and tweak it as they like, and I'm closing this bug report. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 44+ messages in thread
* bug#40968: 28.0.50; (apply nil) 2020-09-27 15:01 ` Lars Ingebrigtsen @ 2020-09-27 19:28 ` Drew Adams 0 siblings, 0 replies; 44+ messages in thread From: Drew Adams @ 2020-09-27 19:28 UTC (permalink / raw) To: Lars Ingebrigtsen, Pip Cet Cc: Mattias Engdegård, 40968, Stefan Kangas, Noam Postavsky > It looked like most everybody was in agreement with this patch, and it > fixes the (apply nil) crash, so I've applied it to Emacs 28 now. > > There was some followup on the details of the documentation, with > several suggested patches, and now that this is on the trunk, people can > go ahead and tweak it as they like, and I'm closing this bug report. Emacs Lisp should follow Common Lisp wrt `apply' behavior. `apply' is pretty central to Lisp, and Common Lisp is a better spec for what Lisp is than Emacs Lisp is. Lots of discussion has gone into every Common Lisp design decision. That doesn't mean its design is perfect, of course. But in the case of `apply' Emacs should follow CL. (Just one outlier opinion.) ^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2020-09-29 3:00 UTC | newest] Thread overview: 44+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <<CC40D602-5027-40A7-9BAB-1AADC9E4BDAE@acm.org> [not found] ` <<CAOqdjBfj6AExvem5WWLfMiw4fEsY-xUUmosV+fj9CaPgWM16ag@mail.gmail.com> [not found] ` <<ECEC9424-919F-4364-9294-381C8751921A@acm.org> [not found] ` <<874kssm04d.fsf@gmail.com> [not found] ` <<6ADF0807-7EBD-4054-8579-4D9AD3065D51@acm.org> [not found] ` <<CAOqdjBdQne3RFTjg4hej40L5aeBx6vbGp6nXKx2TwPkLPf5NPw@mail.gmail.com> [not found] ` <<fabfb1fd-4da1-4e72-90c9-333532011a48@default> [not found] ` <<83pnahctad.fsf@gnu.org> 2020-06-02 17:10 ` bug#40968: 28.0.50; (apply nil) Drew Adams 2020-06-02 18:41 ` Pip Cet 2020-04-29 18:26 Pip Cet 2020-04-29 18:35 ` Pip Cet 2020-05-06 1:51 ` Stefan Kangas 2020-05-06 7:26 ` Pip Cet 2020-05-06 11:24 ` Stefan Kangas 2020-05-06 11:49 ` Pip Cet 2020-05-06 13:02 ` Stefan Kangas 2020-05-06 13:55 ` Pip Cet 2020-05-06 15:28 ` Stefan Kangas 2020-05-06 18:06 ` Pip Cet 2020-05-06 19:26 ` Drew Adams 2020-05-06 14:03 ` Eli Zaretskii 2020-05-06 17:54 ` Pip Cet 2020-05-06 18:09 ` Eli Zaretskii 2020-05-06 18:00 ` Drew Adams 2020-05-06 18:28 ` Noam Postavsky 2020-05-06 19:17 ` Drew Adams 2020-05-06 19:21 ` Pip Cet 2020-05-06 19:28 ` Drew Adams 2020-05-07 2:27 ` Eli Zaretskii 2020-05-06 17:46 ` Drew Adams 2020-05-06 20:32 ` Phil Sainty 2020-05-06 21:35 ` Drew Adams 2020-09-29 3:00 ` Stefan Monnier 2020-05-06 10:18 ` Mattias Engdegård 2020-05-06 10:45 ` Eli Zaretskii 2020-05-06 10:57 ` Andreas Schwab 2020-05-06 11:25 ` Pip Cet 2020-05-06 11:49 ` Mattias Engdegård 2020-05-06 18:42 ` Mattias Engdegård 2020-05-07 6:53 ` Pip Cet 2020-05-07 9:11 ` Mattias Engdegård 2020-05-07 11:54 ` Noam Postavsky 2020-05-07 11:58 ` Pip Cet 2020-05-07 12:20 ` Noam Postavsky 2020-05-07 13:53 ` Mattias Engdegård 2020-06-02 7:36 ` Pip Cet 2020-06-02 16:32 ` Drew Adams 2020-06-02 16:43 ` Eli Zaretskii 2020-06-02 16:36 ` Eli Zaretskii 2020-09-27 15:01 ` Lars Ingebrigtsen 2020-09-27 19:28 ` Drew Adams
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).