マクロを知る

目的と経緯

マクロを実装するには、マクロを知らなければならない。
知ってみよう。

R5RS

R5RS を読んだがよく分からず。
使ってみないとよく分からないことを悟る

マクロを使ってみる

Scheme 入門 15. 構文の定義より

基本
(define-syntax nil!
  (syntax-rules ()
    ((_ x)
     (set! x '()))))

んー。なるほど syntax-rules に元の式→変換後の式を指定するのか。_ という変数が Perl の $_変数 を思い起こさせる。
お題にも上がっている when を書いてみよう。

(define-syntax when
  (syntax-rules ()
    ((_ pred a b)
     (if pred (begin a b)))))

(define x 3)

(when (= x 3)
      (display "x=3")
      (display "\n"))

分かってきたよ。ちなみに ... を使えば 0 個を含む任意個の式を表現できるとのこと。

パターンマッチング
(define-syntax incf
  (syntax-rules ()
    ((_ x) (begin (set! x (+ x 1)) x))
    ((_ x i) (begin (set! x (+ x i)) x))))

incf の呼出しパターンによって振る舞いを変えられる。

練習問題の decf

(define-syntax decf
  (syntax-rules ()
    ((_ x) (begin (set! x (- x 1)) x))
    ((_ x dx) (begin (set! x (- x dx)) x))))

(decf x)
(display x)

(decf x 3)
(display x)

;; これはエラー
(display (decf x 3))
再帰定義
(define-syntax my-and
  (syntax-rules ()
    ((_) #t)
    ((_ e) e)
    ((_ e1 e2 ...)
     (if e1
     (my-and e2 ...)
     #f))))

マクロの定義中で、マクロを参照できる。
マクロ解釈の実装が面倒そうだなぁ。(まだ良く分からないけど)


練習問題 let* を実装せよ。
まずは let* -> let がどのような感じだったかを思い出す。

(let* ((a 3) (b (+ a 1)))
  (p a)
  (p b))

は、以下のような入れ子の let になります。(let*を教えてくれたのは arino さんだったな。)

(let ((a 3))
  (let ((b (+ a 1)))
    (p a)
    (p b)))


これを踏まえると

(define-syntax my-let*
  (syntax-rules ()
    ((_ ((a b)) z ...) (let ((a b)) z ...))
    ((_ ((a b) (c d) ...) z ...)
     (let ((a b))
       (my-let* ((c d) ...)
         z ...)))))

うまくいった。
2個めのパターンマッチングで ... を2ヶ所で使っているけど、どちらがどちらにマッチするルールはどうなっているんだろうか。
出現順?


脈絡ないけど

(define-syntax pp
  (syntax-rules ()
  ((_ a) (begin (display a) (newline)))
  ((_ a b ...) (begin (pp a) (pp b ...)))))

(pp "hoge" "hige" "huga")


さらに

(define-syntax my-if
  (syntax-rules (my-then my-else)
    ((_ a my-then b) (if a b))
    ((_ a my-then b my-else c) (if a b c))))

(define y 4)
(my-if (= y 3)
  my-then (pp "3")
  my-else (pp "not 3"))

楽しくなってきた!

局所的なマクロ
(let-syntax ((hello (syntax-rules ()
  ((_ a) (pp "hello" a)))))
  (hello "higepon"))