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

From: Eli Barzilay (eli at barzilay.org)
Date: Wed Jun 13 01:01:44 EDT 2007

On Jun 13, Matthew Flatt wrote:
> At Tue, 12 Jun 2007 13:12:04 -0400, Eli Barzilay wrote:
> > 
> >   Completely new languages (like frtime, lazy, typed scheme, the
> >   new servlet language) will probably need to use keyword wrappers
> >   in many places too.
> Yes, that makes sense.

This brings the efficiency re allocation to a more important status --
if writing a wrapper for a possibly-keyworded function degrades
performance, then these languages can suffer from a global slowdown.

> > * There is another aspect to this problem -- back when we were
> >   discussing keyword arguments, one important feature I talked
> >   about is that they should make it easy to extend an existing
> >   definition in a way that does not require changing all call
> >   sites.  The change in `apply' violates this -- if you extend
> >   `fprintf' with keywords, you will have to inspect and possibly
> >   modify all uses of it in `apply'.  And BTW, it doesn't help that
> >   you're looking for uses that involve `apply' only -- since you
> >   don't know how values move around.
> No, you misunderstand. A keyword-accepting procedure that doesn't
> *require* a keyword argument is still a procedure that works with
> `apply'.
> So, it's pretty much always ok to add an optional keyword argument.
> (The only exception I can think of is if some code uses
> `keyword-procedure-arity' to determine exactly what keywords are
> allowed. But that's the same obscure class of problems as adding an
> optional non-keyword argument.)

One thing that I should have mentioned earlier is that I simply
filtered places that look suspicious in their use of `apply'.  I
should have spent more time looking at them, but it took a lot of time
as is...  (I meant to filter the list more, but then I got tired to
make me useless as a human filter...)

Let me repeat what I understand so far, and show the problem.
(Hopefully this will be correct.)

1. I write

      (define (foo x) ...)

2. I use it with some wrapper using code (in this case it's Swindle's

      (define mfoo (memoize foo))

3. I generalize `foo' and add an optional keyword argument:

      (define (foo x #:y [y "wie"]) ...)

4. `mfoo' is now not a memoized version of `foo' -- it's only a
   memoized version of it when there are no keywords given.

5. That was a good case -- if I had added a required keyword argument:

      (define (foo x #:y y) ...)

   then `mfoo' would be a useless function that throws an exception if
   given keywords (since `mfoo' does not want them), or if not given
   keywords (since `foo' requires them).

6. I mail the Swindle author, he replies that he likes to write
   portable Scheme code so the code runs with SuperScheme's amazing
   compiler.  After some flaming it's clear that I'm on my own.

7. I copy the Swindle code, and make it work for keyworded functions.

8. A year later Swindle gets improved with the new zumba-hashing
   algorithm that makes `memoizate' 10 times faster.  My code is not
   not in sync...


Another example of this: `-get-file' and `-put-file' from the end of
"framework/private/finder.ss" wrap some functions that currently do
not accept keyword values.  When these functions are updated, the call
site in `-get-file' and `-put-file' should be updated too.

> > * handin-server/private/reloadable.ss -- installs a function wrapper
> >   for auto-reloading code; .../private/run-status.ss -- applies a
> >   messenger value (taken from a parameter); .../utils.ss applies a
> >   checker procedure.
> Ah, an interesting example, at last!
> Yes, this probably need to change.

This one:

> > * lazy/force.ss uses a wrapper when doing a "very deep" (!!!) force of
> >   a value -- wraps functions so their results is forced.
> I can't tell in this code.

is also problematic.  The idea is that you force some value
recursively, and any functions in the result are wrapped so using them
produces forced results too.  In other words:

  ;; in a lazy module:
  (define bar (lambda ...))
  (define foo (list bar))

  ;; in an eager module:
  (require (only ... foo)) ; `foo' is a promise
  (let ([foo (!!! foo)])
    ;; `foo' is now a proper list of one argument
    ((car foo) ...) ; returns a proper value, not a promise

> Maybe you can finish out the list for me. It should be clear from
> the above that the problem is nowhere near as great as you're
> suggesting.

Here's a little more careful searching of the files I had left
(probably still some bogus entries):

* You also missed the `memoize' thing above

* Also, I mentioned the curry thing, for example a simple two-stage
    (define (currify f)
      (lambda args1 (lambda args2 (apply f (append args1 args2)))))

* Some of the things that i mentioned are a problem, here's a random
  | > * `control-io-apply' from "setup/setup-unit.ss"
  | It's internal and only used with a non-keyword procedure.

  The problem is that if any of the functions that are fed to this is
  extended to use keywords, then this needs to be updated too.  If
  it's not, then it wraps a less useful (perhaps completely useful)
  version of the input function.

* lang/private/contracts/contracts-helpers.ss:144

* lang/prim.ss:24 (my guess is that this will need to change if
  keyword procs are used in the teaching languages)

* mztake/engine.ss:145, mztake/engine.ss:166 (similar to tracing, I
  guess); mztake/mztake.ss:51 (not sure about that one)

* mztake/annotator.ss:88, mztake/annotator.ss:93 (not sure about this,
  but annotators like mztake's or the stepper will need to change)

* htdp/big-draw.ss:73, htdp/big-draw.ss:119, htdp/big-draw.ss:178
  (looks like they will need to change if used with keyword

* tests/mred/item.ss:309 (few similar cases in this file)

* tests/mred/random.ss:849

* tests/stepper/through-tests.ss:92

* tests/mzscheme/number.ss:259 (many other tests, I think that I
  mentioned tests earlier)

* browser/external.ss:59

* mred/private/app.ss:65

* mzlib/deflate.ss:75

(I need to crash now, leftover entries that I didn't get to are at

Another point for consideration -- the symmetric multiple-values-in
and -out is gone, so `(compose f g)' cannot pass keyword args to `f'.

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

Posted on the users mailing list.