Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

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とは (define-syntax cond-subset (else) ((_ (else body)) body) ((_ (test1

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

    (syntax-rules %... () ((_ a …) #t) ((_ _ %... ) #f))) (aux-syntax symbol)))) 繰り返し記号は好きに変えられる →syntax-rulesがネストしたときに区別するため
  4. 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コード
  5. 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のやっかいな点はまだあって、後で出てきます)
  6. 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つ取っていれる ・リテラル:そのまま
  7. 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のようには動きません →正しく展開はされます
  8. 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
  9. 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を使う → このへんができればほぼ全ての処理はできる
  10. 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) となってしまうと困る。
  11. 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内の識別子なので ライブラリの名前空間で評価される ユーザー側で定義した識別子は ユーザー側で評価される
  12. 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は見れない
  13. 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の中に展開する
  14. 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)を刺さなくなる 比較的メジャーな処理系はすべてこうなる