[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] Some improvements for cl-flet
From: |
akater |
Subject: |
Re: [PATCH] Some improvements for cl-flet |
Date: |
Sat, 09 Oct 2021 05:33:00 +0000 |
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>> +The return value of said lambdas should be either
>> +
>> +- a valid let-binding (SYMBOL function) to be used in let*
>> + bindings over BODY so that SYMBOL could be used in place of the
>> + corresponding function name in BODY
>> +
>> +or
>> +
>> +- a list (NIL EXPR) for EXPR to be used in BODY in place of the
>> + corresponding function name as is.
>
> Can we simplify this so only one of the two is supported?
There are several ways to simplify this. Here they go, in the order of
attractiveness (to you, as I perceive it):
a. When flet-expander returns a symbol, plug it in as is. If it returns
anything else, use it as initial value in a let binding.
This basically mimics the present mechanism. It sounds good but the question
is, why are we certain that we'll only ever want to plug in symbols.
Plugging in symbols and symbols only is arbitrary, as my example with
symbol-macrolet demonstrates. Aren't values of type “compiled-function” worth
injecting directly as well? Arguably more so since at least they won't
evaluate indefinite number of times with possible side effects. Same applies
to other constant values that include present or future funcallable objects.
What if expander returns nil or t? We probably should add exception concerning
those.
Overall, such spec involves guessing and eventually will get more complicated
than what we started with. Which is why I don't recommend a.
b. Allow building more let bindings than necessary
As long as we have to distinguish between buliding a let binding and plugging
in an external form as is, our lambda must return values of two distinguishable
types. We might however unconditionally generate let bindings and use return
values of flet-expanders as corresponding initial values. As was the case with
“a”, the values don't have to specify gensyms then, and can be used directly
instead. Note however that this would introduce incompatibility with the
existing code due to presence of (func exp) syntax in cl-flet: it might be that
some code that used to evaluate several times at runtime will only evaluate
once. I understand that this is deemed unlikely (and I wish this logic could
be applied to make consp return its argument for true --- even though this
would not be not CL-compatible). Good news is, this would also make cl-flet
with (func exp) more predictable.
So far low-level functions of Emacs that generate Elisp code do try to avoid
building more let bindings than necessary. In fact, cl-flet itself does this
right now. IIUC, with native compiler in action, we might not care that much
about minimizing let bindings. However, cleaner code generation helps with
debugging, and not only debugging macros.
My impression is, lispers generaly don't produce cleaner macroexpanded code
because it would take too much time to write a procedure that does and because
they don't see that much value in clean macroexpansions. I think they are
valuable enough (which is why I took the time to ensure it in the case as
important as cl-flet). Also, despite the fact that clean macroexpansions are
not fashionable, complaints about difficulty of debugging macros themselves or
of macroexpanded code, are fairly popular. Cluttered macroexpansions
contribute to that difficulty.
So I don't recommend b either.
c. Transfer the relevant information from return type of
code-generating lambdas into arguments' types of expand-flet
c.1 We could introduce another argument that explicitly lists the symbols for
which forms returned by flet-expanders are plugged in and no let bindings are
generated.
c.2 The expanders could be specified as either lambdas, in which case the
return value is used in the flet body as is, or, say, singleton vectors
[lambda] in which case the return value of funcall of the 0th element is used
as let-binding. Downsides: (c.2.1) it might be worth it to make this decision
late rather than early; (c.2.2) the interface gets more complicated and less
reasonable.
In c.2 I picked vector because it makes it less likely to confuse lambda
expressions with something that should not be evaluated but overall I think
complicating expand-flet is a bad idea anyway. So I don't recommend c as well.
The proposed return value types, (var value) and (nil value), looked more
straightforward to me than alternatives I could think of.
signature.asc
Description: PGP signature