[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: An alternative proposal

This page is part of the web mail archives of SRFI 89 from before July 7th, 2015. The new archives for SRFI 89 contain all messages, not just those from before July 7th, 2015.

On 4/16/06, felix winkelmann <bunny351@gmail.com> 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))