Upgrade to Pro — share decks privately, control downloads, hide ads and more …

scheme-syntactic-quasiquote.pdf

Niyarin
October 31, 2019

 scheme-syntactic-quasiquote.pdf

Niyarin

October 31, 2019
Tweet

More Decks by Niyarin

Other Decks in Programming

Transcript

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

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

  3. 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はパターンマッチを使うマクロシステム パターン テンプレート
  4. 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回以上使用できる 繰り返し
  5. syntax-rulesとは (define-syntax 1symbol? (syntax-rules () ((_ a …) (let-syntax ((aux-syntax

    (syntax-rules %... () ((_ a …) #t) ((_ _ %... ) #f))) (aux-syntax symbol)))) 繰り返し記号は好きに変えられる →syntax-rulesがネストしたときに区別するため
  6. 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コード
  7. 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のやっかいな点はまだあって、後で出てきます)
  8. syntactic-quasiquote (define-syntax <syntax名> (syntax-rules () ((_ <pattern> …) (syntactic-quasiquote <syntactic-quasiquoteテンプレート>))))

    ・syntax-rulesのラッパ ・CPSマクロが不要にする ・lispで式を操作できる syntactic-quasiquoteの定義 syntacticテンプレート ・syntactic-unquote:中のsyntactic式を評価した結果をいれる ・syntactic-unquote-splicing:syntactic式を評価して括弧を1つ取っていれる ・リテラル:そのまま
  9. 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のようには動きません →正しく展開はされます
  10. 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
  11. 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を使う → このへんができればほぼ全ての処理はできる
  12. 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
  13. Lisp評価器っぽいものはできるが.. ・syntax-rulesはチューリング完全なのでLisp評価器っぽいものを動かすのは可能 ・パターンマッチと展開の外側の部分でまだ問題が残っている → Hygienic macroに関する問題

  14. 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) となってしまうと困る。
  15. 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内の識別子なので ライブラリの名前空間で評価される ユーザー側で定義した識別子は ユーザー側で評価される
  16. 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は見れない
  17. 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の中に展開する
  18. 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)を刺さなくなる 比較的メジャーな処理系はすべてこうなる
  19. 解決できませんでしたよ。 symbolを最後に置き換えればいけると思ったが、hygienicな箇所を全部破壊す るので結局 ・hygienic macroによって名前のバインディングを壊すか ・後処理でhygienicでなければならない場所も壊すか の選択になってしまった →今後は、この間をうまくやりたい。

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

    Github:https://github.com/niyarin/syntactic-quasiquote