読者です 読者をやめる 読者になる 読者になる

分配エラー

プログラムというものは想定する使い方できちんと使えるようにすることは当然としても、使えてはいけないときにはエラーにすることも重要であるということを以前に書いたことがある。

エラーにしたいとき - 主題のない日記

しかし、 Scheme ではそれは少しばかり難しい。 R7RS では「エラーである」と表現されていても処理系の裁量で適当に無難な結果を返してもよいことになっている。 (エラーを検出して通知しなければならない場合については別の表現で書かれている。) そうした状況では処理系がエラーにすることを期待せずにプログラマが陽に書かなければ思わぬ事態になるかもしれないのである。

そのひとつが省略子付きのパターン変数にマッチしたものの分配だ。

例として以下のようなものが挙げられる。

(import (scheme base) (scheme write))

(define-syntax zipm
  (syntax-rules ()
    ((_ (x ...) (y ...))
     (list (list x y) ...))))

(display (zipm (1 2) (1 2 3)))

このときパターン変数 x にマッチするのは (1 2) であり y にマッチするのは (1 2 3) なので (x y) ... は数が合わずエラーである。 しかし、それを通知せずに短い方に長さを合わせる処理系は多い。 そうした処理系でも確実にエラーにすることを考えると以下のような書き方が思い付く。

(import (scheme base) (scheme write))

(define-syntax %zipm
  (syntax-rules ()
    ((_ (tx ...) (ty ...) () (y ys ...))
     (syntax-error "invalid input"))
    ((_ (tx ...) (ty ...) (x xs ...) ())
     (syntax-error "invalid input"))
    ((_ (tx ...) (ty ...) () ())
     (list (list tx ty) ...))
    ((_ (tx ...) (ty ...) (x xs ...) (y ys ...))
     (%zipm (tx ... x) (ty ... y) (xs ...) (ys ...)))))

(define-syntax zipm
  (syntax-rules ()
    ((_ (x ...) (y ...))
     (%zipm () () (x ...) (y ...)))))

(display (zipm (1 2 3) (1 2 3 4)))

事前に全てを想定できるわけではないにしても、特に汎用性の高い部品については入力を信用しないということを意識するのは重要であるとあらためて思う次第である。

Document ID: b3f37dd8178a9063779cf788e179efea