For the record, I eventually came up with the following code:

(define-module (extra define-keywords)
  #:use-module (ice-9 nice-9)
  #:use-module (extra attributes)
  #:replace ((define/keywords . define*)))

(define-syntax define/keywords
  (lambda (x)
    (define* (required args #:optional (gathered '()))
      (match args
    (((? symbol?) #:= . _)
     (values (reverse gathered) args))
    (((? symbol? s) . rest)
     (required rest `(,s . ,gathered)))
     (values (reverse gathered) args))))

    (define* (optional args #:optional (gathered '()))
      (match args
    (((? symbol? s) #:= value . rest)
     (optional rest `((,s ,value) . ,gathered)))
     (values (reverse gathered) args))))

    (define* (keyword args #:optional (gathered '()))
      (match args
    (((? keyword? k) (? symbol? s) #:= value . rest)
     (keyword rest `((,s ,value ,k) . ,gathered)))
    (((? keyword? k) (? symbol? s) . rest)
     (keyword rest `((,s #f ,k) . ,gathered)))
     (values (reverse gathered) args))))

    (define (required+optional+keyword+rest+keys args)
      (let* ((required args* (required (syntax->datum args)))
         (optional args** (optional args*))
         (keyword rest (keyword args**))
         (((names values keys) ...) keyword))
    (datum->syntax x `(,required ,optional ,keyword ,rest ,keys))))

    (syntax-case x ()
      ((_ (proc . args) body ...)
       (with-syntax ((((required ...) (optional ...) (keyword ...) rest
              (required+optional+keyword+rest+keys #'args)))
     #'(define proc
         (lambda* (required ... #:optional optional ...
                #:key keyword ...
                #:allow-other-keys . rest)
           (let ((rest (remove-attributes 'keys rest)))
         body ...))))))))

In addition to removing the supported keywords from the "rest" list, it
provides an alternative (incompatible) syntax for optional and keyword

(define* (f r1 r2 r3 opt1 #:= 1 opt2 #:= 2 #:key1 k1 #:key2 k2 #:= 7)

where r1, r2 and r3 are required, opt1 and opt2 optional (with default
values of 1 and 2, respectively), and variables k1 and k2 are accessible
via #:key1 and #:key2 keywords, where in addition k2 has a default value 7.

I'm not sure whether the use of #:= keyword is a good idea -- probably not.
But I'm not a big fan of the current syntax either.

The definition requires the (ice-9 nice-9) library, which -- traditionally
-- can be found here:

In addition, it requires a definition of "remove-attributes":

(define (remove-attributes attributes #;from attribute-list)
  (match attribute-list
    (((? keyword? key)  value . rest)
     (if (member key attributes)
     (remove-attributes attributes rest)
     `(,key ,value . ,(remove-attributes attributes #;from rest))))

Reply via email to