--- title: section5 tags: macro --- ### 5 構文パラメータ "Anaphoric if "または "aif "は、よく使われるマクロの例です。書き方の代わりに ```ocaml= (let ([tmp (big-long-calculation)]) (if tmp (foo tmp) #f)) ``` と書くことができます。 ```ocaml= (aif (big-long-calculation) (foo it) #f) ``` つまり、条件が真であれば、it識別子が自動的に作成され、条件の値に設定されます。これは簡単なはずです。 ```ocaml= > (define-syntax-rule (aif condition true-expr false-expr) (let ([it condition]) (if it true-expr false-expr))) > (aif #t (displayln it) (void)) it: undefined; cannot reference an identifier before its definition in module: 'program ``` ちょっと待って、itが未定義だって? マクロの中である種のミスを犯さないように、ずっと保護されていることがわかります。そのミスとは、新しい構文で導入した変数が、マクロを囲むコード内の変数と誤って衝突してしまうことです。 Racket の[Transformer Bindings]の項に、良い説明と例があります。基本的に、構文にはレキシカル スコープを保持するための「マーク」があります。これにより、マクロは、レキシカル スコープのために、通常の関数のように動作します。 通常の関数が x という名前の変数を定義している場合、外側のスコープにある x という名前の変数と衝突することはありません。 ```ocaml= > (let ([x "outer"]) (let ([x "inner"]) (printf "The inner `x' is ~s\n" x)) (printf "The outer `x' is ~s\n" x)) The inner `x' is "inner" The outer `x' is "outer" ``` マクロもレキシカル・スコープを尊重すれば、予測可能な動作をする信頼性の高いマクロを書くことが容易になります。 これは素晴らしいデフォルトの動作です。しかし、aifのように意図的に魔法の変数を導入したい場合もあります。 この場合、悪い方法と良い方法があります。 悪い方法はdatum->syntaxを使うことで、これは正しく使うのが難しいです。 良い方法は、define-yntax-parameterやsyntax-parameterizeを使って、構文パラメータを使うことです。Racketの通常のパラメータには慣れていると思います。 [Keeping it Clean with Syntax Parameters (PDF)](http://www.schemeworkshop.org/2011/papers/Barzilay2011.pdf)を参照してください。 ```ocaml= > (define current-foo (make-parameter "some default value")) > (current-foo) "some default value" > (parameterize ([current-foo "I have a new value, for now"]) (current-foo)) "I have a new value, for now" > (current-foo) "some default value" ``` これは通常のパラメータです。構文のバリエーションも同様に動作します。考え方としては、デフォルトでエラーを意味するように定義します。aifの中でのみ、意味のある値を持つことになります。 ```ocaml= > (require racket/stxparam) > (define-syntax-parameter it (lambda (stx) (raise-syntax-error (syntax-e stx) "can only be used inside aif"))) > (define-syntax-rule (aif condition true-expr false-expr) (let ([tmp condition]) (if tmp (syntax-parameterize ([it (make-rename-transformer #'tmp)]) true-expr) false-expr))) > (aif 10 (displayln it) (void)) 10 > (aif #f (displayln it) (void)) ``` syntax-parameterizeの内部では、tmpのエイリアスとして動作します。このエイリアスの動作は、make-rename-transformerで作られています。 aif形式の外で、他に定義されていないものを使おうとすると、私たちが望むようなエラーが発生します。 ```ocaml= > (displayln it) it: can only be used inside aif ``` しかし、次のようなローカル定義の文脈では、通常の変数として定義することができます。 ```ocaml= > (let ([it 10]) it) 10 ``` または ```ocaml= > (define (foo) (define it 10) it) > (foo) 10 ``` もっと詳しく知りたい方は、[Keeping it Clean with Syntax Parameters (PDF)](http://www.schemeworkshop.org/2011/papers/Barzilay2011.pdf)を参照してください。 * [next section(6 splicing-letは何のために存在するのか?)](https://hackmd.io/VlVQKufDTLy0jraU2UFusQ) * [previous section(4 パターンマッチング: syntax-case と syntax-rules)](https://hackmd.io/Nk0tT1F8St2-iu07-mqllA)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up