このセクションでは(rnrs exceptions (6)) ライブラリが提供する Scheme の例外ハンドリングの仕組みと例外を上げる仕組みの構成概念を説明する。
例外ハンドラは 1 引数の手続きであり、例外的状況が伝えられたときにプログラムがどのような振る舞いをするかをその手続きが決める。システムは暗黙的にカレント例外ハンドラを持つ。
プログラムはその例外についての情報を包んだオブジェクトをカレント例外ハンドラに渡し呼び出すことで例外を上げる。1引数を受け取る手続きであればどんなものでも例外ハンドラとして使うことができる。また例外を表現するためにどんなオブジェクトも使うことができる。
システムはプログラムの動的環境(dynamic environment)の一部分としてカレント例外ハンドラを保持する。 R6RS:翻訳:R6RS:5.12 Dynamic extent and the dynamic environment を見よ。
カレント例外ハンドラは、プログラムの実行の開始当初には実行を中断する全ての &serious コンディションを取扱い、与えられたコンディションオブジェクトについての情報を表示する事が期待されている。ハンドラは exit することもあるし別の選択肢を提供する場合もある。加えて例外ハンドラはそれ以外の non-&serious コンディションを渡されたときは処理を戻すこと(return すること)が期待される。これらの期待する動作の解釈は、必然的にプログラムが実行されているシステムの性質に依存する。ユーザーは例外をクラッシュとしてでなく、例外を引き起こした状況からの制御された脱出として知られるようにという意図である。
handler は1つの引数を受け取る手続きでなければならない。thunk は引数を受け取らない手続きでなければならない。 with-exception-handler 手続きは thunk の呼び出しの結果を戻す。 handler は thunk の呼び出しの dynamic extent (dynamic-wind によって決まる)のためのカレント例外ハンドラとしてインストールされる。
実装の義務:実装はハンドラが適用されたことにより実行されたエクステントの制限をチェックしなければならない。 raise もしくは raise-continuable の呼び出しの結果としてハンドラが呼ばれたときなどである。(訳注:自信無)実装はハンドラを適用する前に適切な引数かチェックしても良い。
構文:<cond clause> は cond の仕様にある <cond-clause> と同じようなものである 。(R6RS:翻訳:R6RS:11.4.5 Derived conditionalsを見よ)=> と else は (rnrs base (6)) ライブラリにあるものと同じである。
意味論: guard フォームを評価すると、例外送出されたオブジェクトが <variable> に束縛された例外ハンドラとともに <body> が評価される。またその束縛のスコープで clause をあたかも cond 式の clause のように評価する。暗黙の cond 式が guard 式の継続と dynamic environment で評価されるのである。もしもすべての <cond clause> の <test> が #f に評価され、さらに else 節もない場合には、送出されたオブジェクトでもう一度 raise される。raise は元々の raise の呼び出しの 動的環境内で行われるがカレント例外ハンドラが guard 式のものである点が異なる。
guard 式が末尾文脈にあれば cond 節の最後の式も末尾文脈である。
カレント例外ハンドラに obj を渡し呼び出すことで継続不能な例外を上げる。ハンドラは raise を呼び出した動的環境を保持する継続の元で呼び出される。その動的環境は呼び出されているハンドラがインストールされたときそこにカレント例外ハンドラが存在したという点において異なっている。ハンドラから戻るとハンドラと同じ dynamic environment 内で &non-continuable コンディションタイプを持った継続不能な例外が送出される。
カレント例外ハンドラに obj を渡し呼び出すことで継続可能な例外を送出する。ハンドラは raise-continuable を呼んだ継続と同じ継続の元で呼ばれるが以下2つの点で異なる: (1)カレント例外ハンドラは呼び出されているハンドラがインストールされた時に存在していたハンドラそのものである。 (2)呼び出されているハンドラから戻った場合、そのハンドラは再びカレント例外ハンドラになるだろう。ハンドラから戻った場合、ハンドラが返した値は raise-continuable の呼び出しが返す値になる。
(guard (con
((error? con)
(if (message-condition? con)
(display (condition-message con))
(display "an error has occurred"))
’error)
((violation? con)
(if (message-condition? con)
(display (condition-message con))
(display "the program has a bug"))
’violation))
(raise
(condition
(make-error)
(make-message-condition "I am an error"))))
prints: I am an error
=> error
(guard (con
((error? con)
(if (message-condition? con)
(display (condition-message con))
(display "an error has occurred"))
’error))
(raise
(condition
(make-violation)
(make-message-condition "I am an error"))))
=> &violation exception
(guard (con
((error? con)
(display "error opening file")
#f))
(call-with-input-file "foo.scm" read))
prints: error opening file
=> #f
(with-exception-handler
(lambda (con)
(cond
((not (warning? con))
(raise con))
((message-condition? con)
(display (condition-message con)))
(else
(display "a warning has been issued")))
42)
(lambda ()
(+ (raise-continuable
(condition
(make-warning)
(make-message-condition
"should be a number")))
23)))
prints: should be a number
=> 65
rio orange