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.