Keyword arguments

"Richard A. O'Keefe" <>
22 Jan 1997 00:14:22 -0500

          From comp.compilers

Related articles
Keyword arguments (Richard A. O'Keefe) (1997-01-22)
Re: Keyword arguments (1997-01-25)
| List of all articles for this month |

From: "Richard A. O'Keefe" <>
Newsgroups: comp.compilers
Date: 22 Jan 1997 00:14:22 -0500
Organization: Compilers Central
Keywords: design

There's a programming language for statistics called S, which has
keyword arguments pretty much like Common Lisp's, in that the
parameter profile of the callee is not available to the compiler when
it is compiling the caller.

There's a new redesign and reimplementation of S called R. Currently
it is interpreted. I've agreed to set up a project here to develop a
compiler for R. I've figured out how to handle a lot of things, but
Lisp-style keyword arguments have me baffled.

How can lisp-style keyword parameter passing be implemented efficiently?

Actually, S adds a couple of extra wrinkles.
(1) ALL parameters may be passed positionally or by keyword.
        This is a "must have" for the users.
(2) You can abbreviate keyword parameter names: if the parameter is
        "length.out" in the function, "length.out", "length", "len", or
        even "l" may work in the call. This is a "must have" for the users.
(3) You can freely mix keyword and positional arguments. This has the
        same effect as if all the positional arguments came before the
        keyword ones. (S evaluates arguments lazily, even though it is
        a mostly conventional imperative language, so the order in which
        the arguments are _evaluated_ has nothing to do with the order in
        which they are _passed_.) This doesn't look like a problem.
(4) S has an equivalent of "rest" parameters, and a function which
        has a "..." parameter will accept any keywords. I am prepared to
        expend slow magic on rest parameters in R, it can't be worse than
        what S does now.
(5) Formal parameters can be given default expressions; parameters
        with defaults can be omitted.

Here's my current idea:
(a) Sort the actual parameters so that the positional parameters
        appear before the keyword parameters, and the keyword parameters
        are in lexicographic order. This is legitimate because the order
        of evaluation of arguments isn't defined (as mentioned above, in
        S it's controlled by when the argument is first _used_, which may
        vary from call to call of the same function).

(b) S and R have introspection facilities. Environments can be reified
        and operated on. My current idea about that is to build environments
        on a stack to simplify introspection, lazily copying them to the heap
        when and only when included in heap structures. Because of this,
        positional parameters will be passed on a stack in such a way that
        they are already in the right place in the environment object.

(c) Plain function call, e0(e1,...,em)

evaluate e0
push e1
push em

        "Prepare-Call" sets up the header for the new environment on the stack.
        "Enter" expands the environment to have slots for all the parameters
        and local variables and transfers control to the function (which then
        basically does
if (is.missing(opt.par.i)) opt.par.i = default.value.for.pari.i
        for all the optional parameters.

(d) Function call with keyword parameters, e0(e1,..,em,k1=v1,...,kn=vn)

evaluate e0
push e1
push em
push v1
push vn
Enter-With-Keywords ["k1",...,"kn"]

        "Allocate-Rest-Of-Environment" would do everything that "Enter"
        does except transfer control. The values of the keyword parameters
        would then _not_ be in the area comprising the new environment.

        Thanks to the introspection facilities, an S or R function object
        must contain the names of its parameters, and might as well contain
        another copy in lexicographic order, so we can use a linear merge
        to distribute the keyword parameter values to the appropriate slots.

I can't help feeling that there has to be a better way, but since you
can't know at compile time what the parameter names _are_, I don't
know what that better way might be.
[How about this: at compile time, make up an array of pointers with an
entry for each {caller, callee, arguments} triple used in a module,
e.g. "foo calls bar using zip, zorp, and two positionals", and make
the calls go through that array. Initialize each of those pointers to
point to a generic glue routine that constructs an argument
descrambler that puts stuff in the right places for the callee,
substitutes that descrambler in the pointer array and then jumps to
it. That way, there's a lot of interpretive overhead on the first
call but calls in loops, the ones that matter for performance, run
fast the second and later times. The calling sequence could be quite
simple, just push all the arguments and a pointer to a signature that
the generic glue would use to create the unscramber, but the unscrambler
would ignore. -John]


Post a followup to this message

Return to the comp.compilers page.
Search the comp.compilers archives again.