Slide 1

Slide 1 text

syntax-rulesを楽に書くためのおもちゃ syntactic-quasiqoute Niyarin

Slide 2

Slide 2 text

今日の内容 ・syntax-rulesのめんどうな部分を改善するライブラリの紹介 ・R7RS-smallで解決することが目的 ・syntax-case(R6RS/未来のR7RS?)の劣化版 → ポータブル以外のメリットがないよ → おもちゃとしてはおもしろいね

Slide 3

Slide 3 text

syntax-rulesとは (define-syntax cond-subset (else) ((_ (else body)) body) ((_ (test1 body1) clauses …) (if test1 body1 (cond-subset clauses … ))) ((_ (test body)) (if test body)))) syntax-rulesはパターンマッチを使うマクロシステム パターン テンプレート

Slide 4

Slide 4 text

syntax-rulesとは (define-syntax cond-subset (else) ((_ (else body)) body) ((_ (test1 body1) clauses …) (if test1 body1 (cond-subset clauses … ))) ((_ (test body)) (if test body)))) テンプレートの識別子は基本的にパターン変数 →任意のものにマッチングし、テンプレートで展開される リテラル (同じシンボルだけマッチする) “_ “も任意のものにマッチする(非展開用)が テンプレートで2回以上使用できる 繰り返し

Slide 5

Slide 5 text

syntax-rulesとは (define-syntax 1symbol? (syntax-rules () ((_ a …) (let-syntax ((aux-syntax (syntax-rules %... () ((_ a …) #t) ((_ _ %... ) #f))) (aux-syntax symbol)))) 繰り返し記号は好きに変えられる →syntax-rulesがネストしたときに区別するため

Slide 6

Slide 6 text

syntax-rulesのうれしくない点 (define-syntax case   (syntax-rules (else =>) ((case (key ...)clauses ...) (let ((atom-key (key ...)))(case atom-key clauses ...))) ((case key (else => result)) (result key)) ((case key(else result1 result2 ...))(begin result1 result2 ...)) ((case key((atoms ...) result1 result2 ...)) (if (memv key '(atoms ...))(begin result1 result2 ...))) ((case key((atoms ...) => result)) (if (memv key '(atoms ...))(result key))) ((case key((atoms ...) => result)clause clauses ...) (if (memv key '(atoms ...))(result key)(case key clause clauses ...))) ((case key((atoms ...) result1 result2 ...)clause clauses ...) (if (memv key '(atoms ...))(begin result1 result2 ...)(case key clause clauses ...))))) パターンマッチは単純なので複雑なことをさせようとするとパターン数が多くなる 面倒そうなsyntax-rulesコード

Slide 7

Slide 7 text

syntax-rulesのうれしくない点 (define-syntax rev-lambda (syntax-rules () ((_ (args …) body) (lambda (rev-macro args …) body)))) → lambda式の引数部は式ではないので展開されない (lambda (a b) (cons a b)) は (lambda (rev-macro a b) (cons a b))となるだけ 外側から展開されていく性質上、 2つ以上のマクロを組み合わせる場合CPSマクロが必要になることがある 例) リストを反転するマクロを使って、lambda式の引数を反転させるマクロを作る (※rev-lambdaのやっかいな点はまだあって、後で出てきます)

Slide 8

Slide 8 text

syntactic-quasiquote (define-syntax (syntax-rules () ((_ …) (syntactic-quasiquote )))) ・syntax-rulesのラッパ ・CPSマクロが不要にする ・lispで式を操作できる syntactic-quasiquoteの定義 syntacticテンプレート ・syntactic-unquote:中のsyntactic式を評価した結果をいれる ・syntactic-unquote-splicing:syntactic式を評価して括弧を1つ取っていれる ・リテラル:そのまま

Slide 9

Slide 9 text

syntactic-quasiquoteの例 (define-syntax like-let (syntax-rules () ((_ bindings bodies ...)  (syntactic-quasiquote   ((lambda      (syntactic-unquote   (syntactic-map1      syntactic-car      (syntactic-quote bindings)))   bodies ...)   (syntactic-unquote-splicing     (syntactic-map1     (syntactic-lambda (x)      (syntactic-car      (syntactic-cdr x)))   (syntactic-quote bindings))))))))) 普通のlisp式のようにマクロがかけます。 ※Schemeにリードマクロをください  →syntactic-quoteとか    syntactic-unquote-splicingとか    かきたくない ※別の問題でletのようには動きません →正しく展開はされます

Slide 10

Slide 10 text

syntax-rulesでLispもどき1 (syntax-rules () ((_ (a . b)) a)) (syntax-rules () ((_ a b) (a . b))) (syntax-rules () ((_ (a … ) (b …)) (a … b …))) listの操作をsyntax-rulesでやる ○CAR ○CONS ○APPEND

Slide 11

Slide 11 text

syntax-rulesでLispもどき2 条件分岐:パターンマッチをそのまま使う (define-syntax sif (syntax-rules () ((_ #t true-body false-body) true-body) ((_ #f true-body false-body) false-body))) 関数呼び出し、ジャンプとかはlet-syntaxやdefine-syntaxを使う → このへんができればほぼ全ての処理はできる

Slide 12

Slide 12 text

syntactic-quasiquoteの実装した機能 syntactic-lambda syntactic-define syntactic-if syntactic-quote syntactic-quasiquote syntactic-unquote syntactic-unquote-splicing syntactic-map1 syntactic-symbol? syntactic-equal? syntactic-car syntactic-cdr syntactic-cons syntactic-append

Slide 13

Slide 13 text

Lisp評価器っぽいものはできるが.. ・syntax-rulesはチューリング完全なのでLisp評価器っぽいものを動かすのは可能 ・パターンマッチと展開の外側の部分でまだ問題が残っている → Hygienic macroに関する問題

Slide 14

Slide 14 text

Hygienic macroの恩恵1 (define-library (library a) ~略~ (define-syntax my-let                       (syntax-rules (_) ((_ ((a b) ...) body) ((lambda (a ...) body) b ...)))) ・ユーザーはlambdaを別のものに置き換えてしまった hygienic macroがないとどうなるのか? ・ライブラリ内でmy-letというマクロを作った。中でlambdaに変換ている (define lambda ‘AAAAAAAAAAA)                  (my-let ((a 1)(b 2)) (cons a b)) → ((AAAAAAAAAAAA (a b) (cons a b)) 1 2) となってしまうと困る。

Slide 15

Slide 15 text

Hygienic macroの恩恵2 (define-library (library a) ~略~ (define-syntax my-let (syntax-rules (_) ((_ ((a b) ...) body) ((lambda (a ...) body) b ...)))) (define lambda ‘AAAAAAAAAAA)                  (my-let ((a 1)(b 2)) (cons a b)) マクロを定義した環境と呼び出した環境で識別子の評価を自動的に分けてくれる syntax-rules内の識別子なので ライブラリの名前空間で評価される ユーザー側で定義した識別子は ユーザー側で評価される

Slide 16

Slide 16 text

Hygienic macroは良い機能 ・syntactic-quasiquoteと相性が悪いだけで良い機能 ・自動ですべての識別子をきれいにするため、 ・名前空間を意図的に汚すことができない  → anaphoric macroとかはできない (define-syntax bad-aif ((syntax-rules () ((_ test tbody fbody) (let ((it test)) (if it tbody fbody)))))) (define it ‘aaaaaaa) (aif (foo x) (car it) (cdr it)) ;syntax-rulesの外側なので中のitは見れない

Slide 17

Slide 17 text

Hygienic macroとCPSマクロ1 例) リストを反転するマクロを使って、lambda式の引数を反転させるマクロを作る (define-syntax rev-lambda (syntax-rules () ((_ args body) (rev-syntax (syntax-lambda (it) (lambda it body)) args)))) CPS は Continuation Passing Style (継続渡しスタイル)のことです 渡す継続 結果をitに束縛して、 lambdaの中に展開する

Slide 18

Slide 18 text

Hygienic macroとCPSマクロ2 渡した継続に結果を渡すところの抜粋 (let-syntax ((cont-syntax (syntax-rules () ((_ cont-args) cont-body)))) (cont-syntax reverse-result)) ・ (rev-lambda (a b) (cons a b))を与えたとすると cont-args:(a b) ← rev-lambdaのargs cont-body:(cons a b) ← rev-lambdaのbody → rev-lambdaのargsの(a b) とbodyの(cons a b)がsyntax-rulesの中と外で別れた 仕様で決められた挙動ではないが(cons a b)が(a b)を刺さなくなる 比較的メジャーな処理系はすべてこうなる

Slide 19

Slide 19 text

解決できませんでしたよ。 symbolを最後に置き換えればいけると思ったが、hygienicな箇所を全部破壊す るので結局 ・hygienic macroによって名前のバインディングを壊すか ・後処理でhygienicでなければならない場所も壊すか の選択になってしまった →今後は、この間をうまくやりたい。

Slide 20

Slide 20 text

まとめと感想 ・syntactic-quasiquoteは展開はうまくいくけど、hygenic-macroとの相性が悪かった → 名前空間の拘束をしないマクロだけならうまくうごく ・syntax-rulesはデバッグしずらい (macro-expand-1みたいなのが標準ではない、lispで評価するマクロではないから) → こんなおもちゃよりもマクロデバッガの開発したほうが世の中のためになった ・高速なsyntax-rules展開器を作りたくなった syntactic-quasiquoteは中で大量のマッチングと展開が行われる →syntactic-quasiquoteはそこそこ負荷のあるコードを生成できるので、 ベンチマーク用に有効活用したい Github:https://github.com/niyarin/syntactic-quasiquote