Email list hosting service & mailing list manager

An alternative proposal felix winkelmann (15 Apr 2006 21:20 UTC)
Re: An alternative proposal Alex Shinn (16 Apr 2006 07:27 UTC)

Re: An alternative proposal Alex Shinn 16 Apr 2006 07:27 UTC

On 4/16/06, felix winkelmann <> wrote:
> [syntax] (let-keywords* <ARGLIST> ((<VARIABLE> <KEYWORD> [<DEFAULT>])
>              ...) BODY ...)

This is similar to Gauche's LET-KEYWORDS*, but Gauche also allows
<KEYWORD> to be omitted, defaulting to (make-keyword '<variable>)
[which in Gauche happens to be a disjoint keyword type with a prefix

  (define (number->string n . args)
    (let-keywords* args ((radix 10)
                         (precision #f))

  (number->string n :radix 16)

Not specifying a default keyword avoids the whole prefix/suffix
debate, but in this case that could cause more trouble than it's
worth, since you could end up with different libraries that use
multiple conflicting styles:

  (button ':text "OK" ':action click-ok)

  (button 'text: "OK" 'action: click-ok)

  (button 'text "OK" 'action click-ok)

I don't think there's much to be gained by encouraging this
diversity, and people may have trouble remembering which library uses
which style.  This is, of course, orthogonal to whether or not the
keywords are a disjoint type.

Also, it's probably a good idea to also provide a KEY-LAMBDA or
OPT-LAMBDA form, and analogous LET-KEYWORDS (no *).  This avoids an
extra level of passing an argument list, which can make it easier for
compilers to optimize.  It also has the advantage that you're free to
bind the parameters in any order.

The most parametrized procedure I've ever written took exactly 49
keyword arguments (I'm not proud).  Even though this was for a very
generalized SEARCH procedure which I expected to take hours or days
to run, the O(MN) LET-KEYWORDS* behaviour bothered me, so I wrote a
version that looped over the actual passed keywords rather than all

(define *plist-undef* (list '*plist-undef*))

(define-syntax %let-plist
  (syntax-rules ()
    ((%let-plist ls (binds ...) ((var keyword default) . rest) . body)
     (%let-plist ls (binds ... (var keyword default)) rest . body))
    ((%let-plist ls (binds ...) ((var default) . rest) . body)
     (%let-plist ls (binds ... (var var default)) rest . body))
    ((%let-plist ls (binds ...) ((var) . rest) . body)
     (%let-plist ls (binds ... (var var #f)) rest . body))
    ((%let-plist ls (binds ...) (var . rest) . body)
     (%let-plist ls (binds ... (var var #f)) rest . body))
    ((%let-plist ls ((var keyword default) ...) () . body)
     ;; fast path, rest-var not used
     (let ((var *plist-undef*) ...)
       (let loop ((pls ls))
         (unless (null? pls)
           (case (car pls) ((keyword) (set! var (cadr pls))) ...)
           (loop (cddr pls))))
       (if (eq? var *plist-undef*) (set! var default)) ...
       . body))
    ((%let-plist ls ((var keyword default) ...) rest-var . body)
     (let ((var *plist-undef*) ...)
       (let loop ((pls ls) (acc '()))
           ((null? pls)
            (if (eq? var *plist-undef*) (set! var default)) ...
            (let ((rest-var (reverse! acc)))
              . body))
            (loop (cddr pls)
                  (case (car pls)
                    ((keyword) (set! var (cadr pls)) acc) ...
                    (else (cons (cadr pls) (cons (car pls) acc))))))))))
    ((%let-plist . ?)
     (syntax-error "malformed let-plist " (let-plist ?)))))

(define-syntax let-plist
  (syntax-rules ()
    ((let-plist ls specs . body)
     (%let-plist ls () specs . body))))

(let-plist '(a 1 b 2 d 4) ((a 4) (b 5) (c 6) . rest)
  (list a b c rest))
(1 2 6 (d 4))