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

From: Eli Barzilay (eli at barzilay.org)
Date: Tue Jun 12 16:27:32 EDT 2007

On Jun 12, Eli Barzilay wrote:
> [...]

So far, I have two big problems with the new suggestion.

1. Due to the `apply' problem, changing a function to use keywords
   means that you need to scan and possible change all call sites.
   This is the same problem that will make using portable code much
   more difficult -- which will make using this keyword-argument
   device much less popular than it should be.

   In some previous discussion about keyword arguments, I had this as
   my main constraint on any keyword facility -- it must be easy to
   add keywords to an existing function with no additional changes.
   Matthias summarized it much nicer -- the problem is that adding
   keywords to a function definition is a backward-incompatible

2. Another property that I mentioned earlier is that it should be as
   lightweight as possible.  Paying the price to do keywords searches
   etc is fine (of course, using code that does not add unnecessary
   price), but the new proposal suffers in two places: (a) it uses a
   lot of allocation -- as in the previous message, this is both in
   (wrapper) function definitions and in function calls; (b) it looks
   like wrapper functions will have some penalty too.

   The first one is problematic, since it also will drive people away
   from using keyword args.  The second is very bad -- given the
   code-infection property, it means that lots of code will need to
   pay the price.  I can see myself replicating mzlib code just
   because I want to suck every cpu cycle for some program...

Here's an alternative proposal:

* The core language is left as-is, minus self-quoting keywords.  This
  means that if you call

    (+ 1 2 '#:foo 3)

  you still get an error, but it should not be any worse than using
  any other non-numeric type.  #:foo by itself is still a syntax
  error.  The extra punctuation still has the good effect of
  discouraging keywords where symbols can do.

* The higher-level language (I don't remember if it was `big' or
  `large') redefines `#%app' so that you can have unquoted keywords.
  It can also do some of the syntax-time operations that were
  discussed, like ordering keywords, moving them all to the end,
  detecting multiple keywords and value-less keywords etc etc.

* This covers most of the benefits of the original design, but not
  all.  For example, in this language, you can still do

    (+ 1 2 #:foo 3)

  and not get an error; and you can be surprised that:

    (list #:foo 1 2 3)

  produces (2 3 #:foo 1).  These problems happen only when you use an
  unquoted keyword for a non-keyword function.

* Side advantage: existing code can be used, perhaps with removing
  some of the exotic things that "kw.ss" can do.

The above seems to me to be a resonable compromise: it doesn't cover
all errors, but it is *much* better (IMO) than keyword-ifying being a
backward-incompatible change.  Looking back at the original two
problems that Matthew mentioned:

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

The first one is still a problem, but the second one is also a syntax
error.  (Still, I prefer this over breaking code in the original


I'm not sure how this can be solved both sanely and efficiently.
Perhaps make this:

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

expand to

  (greet "John" keyword-value-marker #:last "Doe")

where that marker is a unique value, or an instance of a unique
struct?  (This is bad because `greet' will need to check each of its
positional arguments to make sure that they're not the marker.)  Maybe
expand that to something like

  (apply greet "John" (make-keyword-args '(#:last "Doe")))

and extend `apply' to work on improper lists where the last value is a
keyword-args instance?  (This is also suffering from the
incompatibility problem -- `(lambda xs (list? xs))' can return #f, but
it feels less invasive as the original suggestion.)

Or maybe expand to

  (greet (make-keyword-args '(#:last "Doe")) "John")

(But this leads to confusing errors when passing keyword arguments to
a procedure that does not expect them.)


          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!

Posted on the users mailing list.