GNU bug report logs -
#76413
[PATCH] New macro 'compf' for composing functions
Previous Next
To reply to this bug, email your comments to 76413 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
Report forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 13:53:02 GMT)
Full text and
rfc822 format available.
Acknowledgement sent
to
Eshel Yaron <me <at> eshelyaron.com>
:
New bug report received and forwarded. Copy sent to
bug-gnu-emacs <at> gnu.org
.
(Wed, 19 Feb 2025 13:53:02 GMT)
Full text and
rfc822 format available.
Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
Tags: patch
This patch adds a new macro 'compf' that streamlines the common pattern
of function composition.
Namely, instead of (lambda (x) (foo (bar (baz x)))), with this macro we
can write (compf foo bar baz), which expands to exactly the same form.
This is similar in essence to the "compose" function proposed in
https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg01138.html
with the main difference that compf is a macro, not a function, so it
incurs no runtime performance penalty.
There are many occurrences of function composition in the Emacs code
base alone that can be simplified with this macro. For example:
- In lisp/calc/calc-mode.el:
(lambda (v) (symbol-value (car v))) => (compf symbol-value car)
- In lisp/dired-aux.el:
(lambda (dir) (not (file-remote-p dir))) => (compf not file-remote-p)
- In lisp/env.el:
(lambda (var) (getenv (upcase var))) => (compf getenv upcase)
- In lisp/mail/smtpmail.el:
(lambda (s) (intern (downcase s))) => (compf intern downcase)
- In lisp/net/tramp-container.el:
(lambda (line) (car (split-string line))) => (compf car split-line)
- ...
Best,
Eshel
[0001-New-macro-compf-for-composing-functions.patch (text/patch, attachment)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 14:35:02 GMT)
Full text and
rfc822 format available.
Message #8 received at 76413 <at> debbugs.gnu.org (full text, mbox):
Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text
editors" <bug-gnu-emacs <at> gnu.org> writes:
Hi Eshel,
> This patch adds a new macro 'compf' that streamlines the common pattern
> of function composition.
I have no opinion about this change. However,
> - In lisp/net/tramp-container.el:
> (lambda (line) (car (split-string line))) => (compf car split-line)
Pls don't touch Tramp. It is backwards compatible down to Emacs 28.
> Best,
>
> Eshel
Best regards, Michael.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 14:36:02 GMT)
Full text and
rfc822 format available.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 15:11:02 GMT)
Full text and
rfc822 format available.
Message #14 received at submit <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
Hi Michael,
Michael Albinus <michael.albinus <at> gmx.de> writes:
> Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text
> editors" <bug-gnu-emacs <at> gnu.org> writes:
>
> Hi Eshel,
>
>> This patch adds a new macro 'compf' that streamlines the common pattern
>> of function composition.
>
> I have no opinion about this change. However,
>
>> - In lisp/net/tramp-container.el:
>> (lambda (line) (car (split-string line))) => (compf car split-line)
>
> Pls don't touch Tramp. It is backwards compatible down to Emacs 28.
Noted. To be clear, I'm not proposing to change any existing code at
this point, only to add the new macro.
Anyway, here's an updated patch, which improves the macro's hygiene and
fixes a typo in the docstring:
[v2-0001-New-macro-compf-for-composing-functions.patch (text/x-patch, inline)]
From e2834b162f3f1fa1c8dc8e057e3aa00950269d8e Mon Sep 17 00:00:00 2001
From: Eshel Yaron <me <at> eshelyaron.com>
Date: Wed, 19 Feb 2025 12:58:40 +0100
Subject: [PATCH v2] New macro 'compf' for composing functions
* lisp/subr.el (compf): New macro. (Bug#76413)
---
lisp/subr.el | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/lisp/subr.el b/lisp/subr.el
index e9b49ae5376..d1615c9083f 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -7678,4 +7678,37 @@ internal--c-header-file-path
base
(append base (list (expand-file-name arch "/usr/include"))))))))))
+(defmacro compf (&rest funs)
+ "Expand to the function composition of FUNS, outermost function first.
+
+For example, (compf car cdr) expands to (lambda (x) (car (cdr x))),
+which does the same as `cadr'.
+
+FUNS may contain symbols which refer to functions, such as `car' and
+`cdr' in the example above, and it can also contain `lambda' functions
+and other forms which evaluate to function values. To refer to a local
+variable VAR that is bound to a function, wrap VAR in a vector, as in:
+
+ (let ((foo (lambda (...) ...)))
+ (compf ignore [foo] always))
+
+If FUNS is empty, expand to `identity'."
+ (cond
+ ((null funs) '#'identity)
+ ((length= funs 1)
+ (let ((fun (car funs)))
+ (cond
+ ((symbolp fun) `#',fun) ; Function name.
+ ((vectorp fun) (aref fun 0)) ; Local variable reference.
+ (t fun)))) ; `lambda' and other forms.
+ (t
+ (let* ((x (gensym "x")) (arg x))
+ (dolist (fun (reverse funs))
+ (setq arg
+ (cond
+ ((symbolp fun) `(,fun ,arg))
+ ((vectorp fun) `(funcall ,(aref fun 0) ,arg))
+ (t `(funcall ,fun ,arg)))))
+ `(lambda (,x) ,arg)))))
+
;;; subr.el ends here
--
2.46.2
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 15:11:03 GMT)
Full text and
rfc822 format available.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 17:14:01 GMT)
Full text and
rfc822 format available.
Message #20 received at 76413 <at> debbugs.gnu.org (full text, mbox):
Eshel Yaron [2025-02-19 16:10 +0100] wrote:
> From e2834b162f3f1fa1c8dc8e057e3aa00950269d8e Mon Sep 17 00:00:00 2001
> From: Eshel Yaron <me <at> eshelyaron.com>
> Date: Wed, 19 Feb 2025 12:58:40 +0100
> Subject: [PATCH v2] New macro 'compf' for composing functions
Nice.
> +FUNS may contain symbols which refer to functions, such as `car' and
> +`cdr' in the example above, and it can also contain `lambda' functions
> +and other forms which evaluate to function values.
A (declare (debug ...)) property would be nice, particularly for
stepping through the latter types of argument.
> To refer to a local
> +variable VAR that is bound to a function, wrap VAR in a vector, as in:
> +
> + (let ((foo (lambda (...) ...)))
> + (compf ignore [foo] always))
Is there precedent for this [syntax] somewhere?
I understand that function symbols may be needed more frequently than
variable symbols in arguments to compf, but did you consider
distinguishing between the two kinds of symbol by #'-quoting function
symbols, and leaving variables unquoted?
AFAIK this has more precedents in Elisp.
> + (let* ((x (gensym "x")) (arg x))
> + (dolist (fun (reverse funs))
> + (setq arg
> + (cond
> + ((symbolp fun) `(,fun ,arg))
> + ((vectorp fun) `(funcall ,(aref fun 0) ,arg))
> + (t `(funcall ,fun ,arg)))))
> + `(lambda (,x) ,arg)))))
I wonder if we could/should be smartâ„¢ about the func-arity of the
rightmost function and adapt the wrapper lambda's arglist accordingly.
Finally, would you like to write an announcement and some tests as well?
Thanks,
--
Basil
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 18:37:02 GMT)
Full text and
rfc822 format available.
Message #23 received at 76413 <at> debbugs.gnu.org (full text, mbox):
Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text
editors" <bug-gnu-emacs <at> gnu.org> writes:
> This patch adds a new macro 'compf' that streamlines the common pattern
> of function composition.
>
> Namely, instead of (lambda (x) (foo (bar (baz x)))), with this macro we
> can write (compf foo bar baz), which expands to exactly the same form.
>
> This is similar in essence to the "compose" function proposed in
> https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg01138.html
> with the main difference that compf is a macro, not a function, so it
> incurs no runtime performance penalty.
>
> There are many occurrences of function composition in the Emacs code
> base alone that can be simplified with this macro. For example:
>
> - In lisp/calc/calc-mode.el:
> (lambda (v) (symbol-value (car v))) => (compf symbol-value car)
> - In lisp/dired-aux.el:
> (lambda (dir) (not (file-remote-p dir))) => (compf not file-remote-p)
> - In lisp/env.el:
> (lambda (var) (getenv (upcase var))) => (compf getenv upcase)
> - In lisp/mail/smtpmail.el:
> (lambda (s) (intern (downcase s))) => (compf intern downcase)
> - In lisp/net/tramp-container.el:
> (lambda (line) (car (split-string line))) => (compf car split-line)
> - ...
I'm copying in Stefan Monnier.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 18:41:02 GMT)
Full text and
rfc822 format available.
Message #26 received at 76413 <at> debbugs.gnu.org (full text, mbox):
Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text
editors" <bug-gnu-emacs <at> gnu.org> writes:
> Tags: patch
>
> This patch adds a new macro 'compf' that streamlines the common pattern
> of function composition.
>
> Namely, instead of (lambda (x) (foo (bar (baz x)))), with this macro we
> can write (compf foo bar baz), which expands to exactly the same form.
>
> This is similar in essence to the "compose" function proposed in
> https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg01138.html
> with the main difference that compf is a macro, not a function, so it
> incurs no runtime performance penalty.
>
> There are many occurrences of function composition in the Emacs code
> base alone that can be simplified with this macro. For example:
>
> - In lisp/calc/calc-mode.el:
> (lambda (v) (symbol-value (car v))) => (compf symbol-value car)
> - In lisp/dired-aux.el:
> (lambda (dir) (not (file-remote-p dir))) => (compf not file-remote-p)
> - In lisp/env.el:
> (lambda (var) (getenv (upcase var))) => (compf getenv upcase)
> - In lisp/mail/smtpmail.el:
> (lambda (s) (intern (downcase s))) => (compf intern downcase)
> - In lisp/net/tramp-container.el:
> (lambda (line) (car (split-string line))) => (compf car split-line)
> - ...
Why not name it `compose` as in CL and ELisp?
How about a function with a compiler macro?
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 18:47:02 GMT)
Full text and
rfc822 format available.
Message #29 received at 76413 <at> debbugs.gnu.org (full text, mbox):
Hi,
"Basil L. Contovounesios" <basil <at> contovou.net> writes:
> Eshel Yaron [2025-02-19 16:10 +0100] wrote:
>
>> From e2834b162f3f1fa1c8dc8e057e3aa00950269d8e Mon Sep 17 00:00:00 2001
>> From: Eshel Yaron <me <at> eshelyaron.com>
>> Date: Wed, 19 Feb 2025 12:58:40 +0100
>> Subject: [PATCH v2] New macro 'compf' for composing functions
>
> Nice.
>
>> +FUNS may contain symbols which refer to functions, such as `car' and
>> +`cdr' in the example above, and it can also contain `lambda' functions
>> +and other forms which evaluate to function values.
>
> A (declare (debug ...)) property would be nice, particularly for
> stepping through the latter types of argument.
Oh, good idea... The following spec seems to work well, but I'm not
that experienced with Edebug specs, so improvement suggestions would be
very welcome:
(declare (debug (&rest &or symbolp vectorp lambda-expr form)))
>> To refer to a local
>> +variable VAR that is bound to a function, wrap VAR in a vector, as in:
>> +
>> + (let ((foo (lambda (...) ...)))
>> + (compf ignore [foo] always))
>
> Is there precedent for this [syntax] somewhere?
Not that I know of.
> I understand that function symbols may be needed more frequently than
> variable symbols in arguments to compf,
Indeed, that's the use case I'm optimizing for.
> but did you consider distinguishing between the two kinds of symbol by
> #'-quoting function symbols, and leaving variables unquoted? AFAIK
> this has more precedents in Elisp.
I have considered and tried it, and I found that alternative slightly
more cumbersome. It's nice to be able to pass function names unquoted.
Of course I'm open to reconsider if people have a strong preference.
The way I see it, it's important to support local variables for the sake
of flexibility, but it's more of a niche use case, so it's OK if
specifying a local variable requires a bit more effort, compared to a
named function.
>> + (let* ((x (gensym "x")) (arg x))
>> + (dolist (fun (reverse funs))
>> + (setq arg
>> + (cond
>> + ((symbolp fun) `(,fun ,arg))
>> + ((vectorp fun) `(funcall ,(aref fun 0) ,arg))
>> + (t `(funcall ,fun ,arg)))))
>> + `(lambda (,x) ,arg)))))
>
> I wonder if we could/should be smartâ„¢ about the func-arity of the
> rightmost function and adapt the wrapper lambda's arglist accordingly.
Perhaps, could be interesting if we could do it reliably.
> Finally, would you like to write an announcement and some tests as well?
Sure, if there no objections to this addition, I'll some tests and docs.
Eshel
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#76413
; Package
emacs
.
(Wed, 19 Feb 2025 19:01:02 GMT)
Full text and
rfc822 format available.
Message #32 received at 76413 <at> debbugs.gnu.org (full text, mbox):
Hi Stefan,
Stefan Kangas <stefankangas <at> gmail.com> writes:
> Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text
> editors" <bug-gnu-emacs <at> gnu.org> writes:
>
>> Tags: patch
>>
>> This patch adds a new macro 'compf' that streamlines the common pattern
>> of function composition.
>>
>> Namely, instead of (lambda (x) (foo (bar (baz x)))), with this macro we
>> can write (compf foo bar baz), which expands to exactly the same form.
>
> Why not name it `compose` as in CL and ELisp?
It's a slightly different construct then Common Lisp's compose, so I
feel like calling it by that name could lead to wrong expectations.
Another option is fcomp instead of compf, BTW, if that seems better.
> How about a function with a compiler macro?
That's a valid alternative, but it'd require quoting function names,
which is slightly less concise/convenient.
Do you see a clear advantage for using a function in this case?
Thanks,
Eshel
This bug report was last modified 1 day ago.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.