kb84tkhrのブログ

何を書こうか考え中です あ、あと組織とは関係ないってやつです 個人的なやつ

RacketのMacroを調べてみる (6)

ここらから少し応用編
適当に端折っていきます

ちゃんとしくみを理解してないと、の方針は続いていて、エラーを出したり
それを調べたりしながら順を追って関数名を作る話をしてくれます

  • syntaxの内側でないとpattern variableは参照できない
  • template内はpattern variable以外置き換えは起こらない
  • 置き換えたければsyntax-caseを入れ子にして使うことができる
  • マクロがうまく動かなくてどう展開されているのか知りたいときはMacro Stepperが便利

ここでいったんなんとか動くソース

(define-syntax (hyphen-define/ok1 stx)
  (syntax-case stx ()
    [(_ a b (args ...) body0 body ...)
     (syntax-case (datum->syntax stx
                                 (string->symbol (format "~a-~a"
                                                         (syntax->datum #'a)
                                                         (syntax->datum #'b))))
       ()
       [name #'(define (name args ...)
                 body0 body ...)])]))

(hyphen-define/ok1 foo bar () #t)を評価すると
(define (foo-bar) #t)になります

  • stx#'(hyphen-define/ok1 foo bar () #t)が入る
  • afoobbar(args ...)()body ...#tにマッチする
  • (syntax->datum #'a)fooを、(syntax->datum #'b)barを取り出す
  • format"foo-bar"を作る
  • string->symbolfoo-barを作る
  • datum-syntax#'foo-barを作る
  • 内側のsyntax-casename#'foo-barにマッチする
  • #'(define (name ...(define (foo-bar) #t)に変換される

てことをしてます
死にそうです

こういうよくある(はず)の処理は簡単にできるようになってるはず
ふたつのツールが使えます

  • (syntax-case <syntax> () [<pattern> <body>])の代わりに(with-syntax ([<pattern> <syntax>]) <body>)が使えます
  • (datum->syntax stx (string->symbol (format ... (syntax->datum #'a))))の代わりに(format-id stx ... a)が使えます

こうなりました

(define-syntax (hyphen-define/ok3 stx)
  (syntax-case stx ()
    [(_ a b (args ...) body0 body ...)
     (with-syntax ([name (format-id stx "~a-~a" #'a #'b)])
       #'(define (name args ...)
           body0 body ...))]))

これなら生きていけます