Re: Procedures with keyword arguments should be macros not procedures
Lassi Kortela 17 Oct 2019 21:31 UTC
Thanks for chiming in Per! The Kawa implementation of 177 was especially
easy.
> Consider:
> (f x y z)
> Obvious a call to f with 3 positional (non-keyword) arguments, right?
>
> But what about:
> (define y k1:)
> (f x y z)
>
> Ooops. Turns out if was 1 positional and 1 keyword argument.
> But only if f allows keyword arguments.
>
> Unless you have some kind of type-checking/data-flow you can't guard
> against this. Even with that type of analysis you can't always *prove*
> whether y is a keyword or not, so you have to compile conservative code
> and check at run-time.
Is the following a reasonable summary?
Common Lisp style:
* Find out the arity of `f`
* If `y` appears in the argument list before the position where
keywords start, treat it as an optional arg (not a keyword arg) no
matter what kind of object it is (keyword, list, anything else).
* Otherwise, if `f` takes keyword args and `y` is an even distance
at/after the keyword start position then assert that `y` is a
keyword object, thet `f` takes that particular keyword, that there
is an object following `y`, and assign that object to the keyword
pointed to by `y`.
Kawa/Racket style:
* Arity of `f` does not need to be known at call site, because
keywords can always be passed in a separate part of the stack frame
apart from positional and rest arguments.
* Never need to check whether `y` is a keyword object.
* Even if the static analyzer does not know which keyword args `foo`
accepts, it can sort the keywords given by the caller at the call
site, so they get pushed into the stack frame in the right order.
* There is never confusion about whether a particular arguments
belongs to the optional/rest arguments or the keyword arguments.
So with the latter style, the compiler can implement more
optimizations in more situations with simpler code. And the user can
be confident that keywords in a procedure call are always keyword
arguments unless the keyword is quoted.
The former style is more flexible and syntactically simpler, but its
runtime behavior is more complex. Consequently the compiler has to
work harder, or leave more checking to be done at run time.