[plt-scheme] keyword arguments (in v4.0, maybe)

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Mon Jun 11 23:48:17 EDT 2007

PLT Scheme currently supports keyword-based arguments via the "kw.ss"

 > (define/kw (greet first #:key [last "Smith"] [hi "Hello"])
     (string-append hi ", " first " " last))
 > (greet "John")
 "Hello, John Smith"
 > (greet "John" #:last "Doe")
 "Hello, John Doe"

Many of our library APIs have been improved by the use of keyword-based
arguments, and keyword argument play an important role in the new
Scribble documentation tools.

I think they should be used more --- probably even supported by
`lambda' and `define' in the v4.0 `big' language.

At the same time, the way that keyword arguments are currently defined
is a bit weird. Consider these examples:

 > (greet #:last "Doe")
 greet: expecting a (#:last) keyword got: "Doe"
 > (greet "John" #:hi #:last "Doe")
 greet: expecting a (#:last #:hi) keyword got: "Doe"

The keyword system can't give a better error message, because you might
have intended to pass `#:last' as a value to `greet', instead of using
it to mark a by-keyword argument.

Of course, you should never mean to pass `#:last' as an argument like
that. Keyword arguments work well when keywords are used only to tag
arguments. If the proper use of keywords be enforced, then keyword
arguments become a lot easier to explain, and better error messages can
be given for the above example.

Completely banishing keywords as by-position argument values, however,
doesn't work. Itt's too expensive, and keyword passing and checking
need to be implemented somehow...

Here's a way to help enforce correct use of keywords, making the system
simpler, more consistent, and better able to report errors:

 * Change the language so that a keyword is not an expression, though
   it can be quoted:

     > #:last
     syntax error
     > '#:last
     #:last ; i.e., the same result as (string->keyword "last")

 * Change the application form to recognize (unquoted) keywords as
   markers in the application, and to trigger a keyword-procedure
   application protocol, instead of a normal procedure application.

 * Change the calling convention for keyword procedures so that
   keywords are not passed via by-position arguments (where they can be
   confused with normal arguments), but through a completely different
   protocol. Procedures with only optional keywords could be used in
   plain applications.

With these changes:

 * `(greet #:last "Doe")' would be a run-time error, but the error
   would be about a missing by-position argument, as opposed to a
   missing keyword.

 * `(greet "John" #:hi #:last "Doe")' would be a syntax error, because
   there are two keywords in a row.

 * Keyword-based arguments don't have to appear in any particular order
   compared to by-position arguments. So the following would be

     (greet "John" #:last "Doe" #:hi "Howdy")
     (greet #:last "Doe" "John" #:hi "Howdy")
     (greet #:hi "Howdy" #:last "Doe" "John")

   Programmers will probably put keyword arguments last, normally, but
   sometimes it's nicer to be able to put them first. Scribble, in
   particular, relies heavily on keyword arguments that appear first.
   (The current `lambda/kw' form lets the programmer pick either first
   or last for a given procedure.)

As it turns out, keyword-based applications can also be more efficient
on average, because the keywords can be statically ordered by the
application form. And all of this can be implemented in a library
(i.e., no changes to MzScheme's core or to the performance of
non-keyword procedures and applications).

One drawback of this strategy is that `(lambda args (apply f args))' is
not an all-purpose wrapper strategy, because that only works for `f'
that doesn't take keyword arguments. But there would be a new way to
create wrappers that works for both plain and keyword-accepting
procedures. Another drawback is that not all styles of keywords
supported by "kw.ss" would be the allowed in the new form.

While we're at it, I think the form for declaring keyword-based
procedures more parallel to the application side --- more like SRFI-89
than "kw.ss".

The new guide and reference document the possible changes. See
especially sections 4.3 and 4.4 in the new guide; here's a starting
point (in a temporary copy on my web site):



Posted on the users mailing list.