Subject: Re: bug#76413: [PATCH] New macro 'compf' for composing functions

From: Eshel Yaron <[email-masked]>
Date: Wed, 19 Feb 2025 16:10:08 +0100

Hi Michael,

Michael Albinus <[email-masked]> writes:

> Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text
> editors" <[email-masked]> 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:

>From e2834b162f3f1fa1c8dc8e057e3aa00950269d8e Mon Sep 17 00:00:00 2001
From: Eshel Yaron <[email-masked]>
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