--- title: section7 tags: macro --- ### 7 堅牢なマクロ:シンタックス-パース 関数はエラーになることがあります。マクロも同様です。 --- ### 7.1 関数のエラー処理戦略 普通の関数の場合、誤用を処理する方法にはいくつかの選択肢があります。 1. 全くチェックをしない。 ```ocaml= > (define (misuse s) (string-append s " snazzy suffix")) ; User of the function: > (misuse 0) string-append: contract violation expected: string? given: 0 argument position: 1st other arguments...: " snazzy suffix" ; I guess I goofed, but – what is this "string-append" of which you ; speak?? ``` 問題は、結果として生じるエラーメッセージが混乱することです。ユーザーは誤用と思っていますが、string-appendからエラーメッセージを受け取っているのです。この単純な例では,何が起こっているのか推測できるかもしれませんが,ほとんどの場合は推測できないでしょう。 2. エラー処理のコードを書いてみましょう。 ```ocaml= > (define (misuse s) (unless (string? s) (error 'misuse "expected a string, but got ~a" s)) (string-append s " snazzy suffix")) ; User of the function: > (misuse 0) misuse: expected a string, but got 0 ; I goofed, and understand why! It's a shame the writer of the ; function had to work so hard to tell me. ``` 残念ながら、エラーコードは関数の定義を圧倒したり、曖昧にしたりする傾向があります。また、このエラーメッセージは良いものですが、素晴らしいものではありません。これを改善するには、さらに多くのエラーコードが必要です。 3. コントラクトを使う。 ```ocaml= > (define/contract (misuse s) (string? . -> . string?) (string-append s " snazzy suffix")) ; User of the function: > (misuse 0) misuse: contract violation expected: string? given: 0 in: the 1st argument of (-> string? string?) contract from: (function misuse) blaming: program (assuming the contract is correct) at: eval:131.0 ; I goofed, and understand why! I'm happier, and I hear the writer of ; the function is happier, too. ``` これは両方の世界の良いところを併せ持っています. 契約はシンプルで簡潔です。さらに良いことに、それは宣言的です。どうやって実現するかではなく、何を実現したいのかを言います。 一方で、この関数のユーザは非常に詳細なエラーメッセージを受け取ります。しかも、そのメッセージは標準的で親しみやすい形式です。 4. 型付けされたRacketを使いましょう。 ```ocaml= #lang typed/racket > (: misuse (String -> String)) > (define (misuse s) (string-append s " snazzy suffix")) > (misuse 0) eval:3:0: Type Checker: type mismatch expected: String given: Zero in: 0 ``` さらに良いことに、Typed Racketはコンパイル時に使用法の間違いを前もって発見することができます。 --- ### 7.2 マクロのエラー処理戦略 マクロの場合も同様の選択肢があります。 1. 誤用の可能性を無視する。この選択はマクロの場合はさらに悪いです。デフォルトのエラーメッセージは意味をなさず、ましてやユーザーが何をすべきかを知る助けにはならないでしょう。 2. 2. エラー処理のコードを書く。ドット記法でハッシュの入れ子検索」の例では、これがマクロをどれだけ複雑にするかを見ました。まだマクロの書き方を学んでいる最中ですが、これ以上の認知的負荷や難読化は特に避けたいところです。 3. syntax-parseを使う。マクロの場合、これは関数にコントラクトや型を使うのと同じです。入力パターンの要素は、識別子のようなある種のものでなければならないと宣言できます。型」の代わりに、その種類は「構文クラス」と呼ばれます。あらかじめ定義された構文クラスもありますし、独自に定義することもできます。 --- ### 7.3 syntax-parseの使用 2012年11月1日。さて、ここからが本題です。ここまで書いてきて、syntax-parseのドキュメントを読み直してみました。それは...とてもわかりやすかったです。混乱することもありませんでした。 なぜでしょう?ドキュメントには、多くの簡単な例を含む素晴らしい「はじめに」と、多くの実世界のシナリオを示す「例」のセクションがあります。 更新しました。さらに、Ben Greenman氏が作成したパッケージのドキュメントには、さらに多くのSyntax Parseの例を示す素晴らしいセットが用意されています。 さらに、この時点までに学んだすべてのことは、syntax-parseが何をするのか、そしてなぜするのかを理解するための準備となりました。これまでのところ、使い方の詳細はとても簡単なようです。 これは、「知らないことを知らない」という一時的な状態かもしれません。もっと掘り下げて使っていくうちに、何か混乱したことや厄介なことを発見するかもしれません。そのときは、ここに戻ってきて、この記事を更新します。 でも今は、これまでの部分を改善することに集中します。 * [next section(8 参考文献と謝辞)](https://hackmd.io/4HqQUdbNT6KJbyLxYf80WQ) * [previous section(6 splicing-letは何のために存在するのか?)](https://hackmd.io/VlVQKufDTLy0jraU2UFusQ)