読者です 読者をやめる 読者になる 読者になる

lambda 構文の拡張

(lambda (arg . rest) ...) という記法は &rest しか知らなかった時分からするとめちゃくちゃ感動したんですが… たしかに拡張性に乏しいのかもしれません。 でも scheme の仕様からするとそもそも (lambda (arg1 arg2 :optional arg3) ...) とか出来ないので scheme の仕様の範囲内ではなかなか簡潔さと合理性が勝ってるんじゃないでしょうかね。

http://d.hatena.ne.jp/wasabiz/20110221/1298287501

それマクロで出来るよ。
先日書いた let-optionals* を活用して、 lambda を置換えるライブラリを書いてみた。 ついでに define でも同様の挙動になるようにしている。 一応 R6RS 範囲内のつもり。

(library (optional)
  (export let-optionals*
          (rename (my:define define)
                  (my:lambda lambda)))
  (import (rnrs))

  (define-syntax let-optionals*
    (syntax-rules ()
      ((_ a ((v d) . r) . b)
       (let* ((t a)
              (v (if (null? t) d (car t))))
         (let-optionals* (if (null? t) '() (cdr t)) r . b)))
      ((_ a () . b)
       (begin . b))
      ((_ a (v . r) . b)
       (let-optionals* a ((v #f) . r) . b))
      ((_ a rv . b)
       (let ((rv a)) . b))))

  (define-syntax lambda%
    (syntax-rules (:optional)
      ((_ (a ...) (:optional r ...) rv c0 c1 ...)
       (lambda(a ... . t)
         (let-optionals* t (r ... . rv) c0 c1 ...)))
      ((_ (a ...) (o . r) rv c0 c1 ...)
       (lambda% (a ... o) r rv c0 c1 ...))
      ((_ (a ...) () rv c0 c1 ...)
       (lambda (a ... . rv) c0 c1 ...))))

  (define-syntax my:lambda
    (syntax-rules ()
      ((_ (a ... . rv) b0 b1 ...)
       (lambda% () (a ...) rv b0 b1 ...))))

  (define-syntax my:define
    (syntax-rules ()
      ((_ (name . args) b0 b1 ...)
       (define name (my:lambda args b0 b1 ...)))
      ((_ name obj)
       (define name obj))))
  )

テストケースというか、使用例はこんな感じ。

(import (except (rnrs) define lambda)
        (optional))

(define (test-a a b :optional c  (d 'd) (e 'e))
  (list a b c d e))

(define (test-b a b :optional c . rest)
  (list a b c rest))

(define (test-c a b . rest)
  (list a b rest))

(display (test-a 1 2))     (newline)
(display (test-a 1 2 3 4)) (newline)
(display (test-b 1 2))     (newline)
(display (test-b 1 2 3 4)) (newline)
(display (test-c 1 2 3 4)) (newline)

(display ((lambda(a b :optional c (d 'd) (e 'e))
            (list a b c d e))
          1 2))

それからこっちの疑問について、

それにしても scheme にはなんでキーワード構文がないんでしょうか? リードマクロ関連ですかね。

http://d.hatena.ne.jp/wasabiz/20110221/1298287501

聞き齧りの話だけれど、 Common Lisp のシンボルはパッケージに保護されるので、カレントパッケージ以外のシンボルを使おうとするとパッケージ名で修飾したりせねばならず、名前付き引数に使うのはうざったいという事情があると聞いたことがある。 キーワードもキーワードパッケージというものに属するとかはあるらしいのだけれども、とにかく名前付き引数に使うのに便利なようにうまいことなっているようだ。
Scheme ではシンボルはパッケージに属するわけではないので、名前付き引数をやりたければシンボルでやれる。 そういう構文が欲しければマクロで書けるので、好き勝手やればいいんじゃないかな。 マクロってそういうものだし。
Document ID: ea9f6577ab882fccae3dd3b89a9ff69b