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

From: Eshel Yaron <[email-masked]>
Date: Wed, 19 Feb 2025 19:46:39 +0100

Hi,

"Basil L. Contovounesios" <[email-masked]> writes:

> Eshel Yaron [2025-02-19 16:10 +0100] wrote:
>
>> 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
>
> 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