Re: Keyword arguments in procedures specified in SRFIs
Per Bothner 22 Jul 2019 15:52 UTC
On 7/22/19 1:35 AM, Lassi Kortela wrote:
>> In newer versions of Kawa too keywords are syntax rather than self-evaluating literals.
>
> I didn't know that. Do you have something like Racket's keyword-apply?
Yes, by using "splice" operators:
(fun arg1 @rargs)
or:
(fun arg1 @:rargs)
is roughly equivalent to:
(apply fun arg1 rargs)
For details see:
https://www.gnu.org/software/kawa/Application-and-Arguments-Lists.html
> How does this go in practice? When I worked with Common Lisp, almost all calls to functions taking keyword arguments were like this:
>
> (some-function arg1 arg2 :keyword3 arg3 :keyword4 arg4)
One problem is that this is a valid call to:
(defun some-function (a b c d e f) ...)
or:
(defun some-function (a b &rest r) ...)
because keywords aren't "reserved" in any way.
These mistakes are caught in Kawa/Racket (potentially at compile-time).
Another problem is you can't know (without more extensive analysis) whether arg1
is also a keyword.
> Does this mean that if keywords were just ordinary symbol-like objects, then a call like the some-function example above could be statically analyzed to use this fast and compact calling convention?
I think so.
> Would this imply that each procedure taking kwargs in principle needs two entry points - a fast path and a slow path?
No.
In general, when the argument list is "simple", a procedure is compiled into two methods:
(define (fun x (y ::int)) body)
becomes:
Object fun(Object x, int y) { return body; }
Object fun$check(Procedure f, CallContext ctx)
{
Object x = ctx.getNextArg();
Object y$in = ctx.getNextArg();
if (y$in instanceof Number)
return fun(x, ((Number)y$in).intValue());
else
ctx.matchError(ERR_CODE);
}
When fun is called and known (and compatible) the compiler will
compile a call directly to the fun method.
Otherwise, such as called using apply, the compiler will generate
a call that indirects to calling fun$check, which does argument-checking
and conversions. Note fun$check also can be used when we have a "generic
function" - i.e. multiple functions, chosen dynamically based on argument types.
Depending on how fun$check is called, matchError can either throw an excepion
or just return an code indicating "try the next candidate".
When keywords are involved, it gets more complicated. If no apply or explicit
arglist objects are involved, the caller can still pre-allocate a sorted keyword array.
This constant array is used to set various fields of the CallContext object
(with no deep copying). Then fun$check method is called and it does a quick
linear scan of the keywords. So still quite efficient. In theory the
compiler could statically process the keywords when calling a known function,
but that has not been implemented.
--
--Per Bothner
xxxxxx@bothner.com http://per.bothner.com/