Macro RESTART-CASE
restart-case restartable-form {clause} => result*
clause::= (case-name lambda-list
[[:interactive interactive-expression | :report report-expression | :test test-expression]]
declaration* form*)
restartable-form - フォーム
case-name - シンボルか、nil
lambda-list - 通常のラムダリスト
interactive-expression - シンボルか、ラムダ式
report-expression - 文字列か、シンボルか、ラムダ式
test-expression - シンボルか、ラムダ式
declaration - 宣言式。評価されません。
form - フォーム
result - restartable-formの評価の返却値か、 選択されたclause内の最後のformの返却値か、nil
restart-caseは、 clause制御の遷移先として特別な意味を持つ動的環境下で restartable-formを評価します。 もしrestartable-formの実行が終わり何らかの値を返却したときは、 そのすべての値はrestart-caseの返却値となり処理が完了します restartable-formが実行中に、 どんなコードでもclauseのどれかに 制御を遷移するかもしれません(invoke-restartを参照)。 もし遷移が起こったとき、そのclause本体内のformは評価され、 そして最後のformの返却値がrestart-caseによって返却されます。 この場合、動的状態はclauseを実行する前に適切に巻き戻されます (つまりrestartable-formの周りで確立されたrestartは もはや有効ではありません)。
もしformが存在しないclauseが選択された場合、 restart-caseはnilが返却されます。
もしcase-nameがシンボルなら、それはrestartの名前です。
同じ名前のcase-nameを使ったclauseは複数存在できます。 このような場合、その名前の最初のclauseが find-restartによって発見されます。 他のclauseはcompute-restartsを使うことでアクセスできます。
各arglistは通常のラムダリスト、 対応するformを実行中に束縛されます。 これらのパラメーターはrestart-caseのclauseが invoke-restart呼び出しから必要なデータを受け取って使われます。
標準では、invoke-restart-interactivelyは引数を受け取らず、 全ての引数はrestartから対話式で集めるための オプションでなければなりません。 しかし、もしinvoke-restart-interactivelyへ どのように正しい引数リストを計算するのかについて 知らせるときに使われる:interactiveキーワードを持っている場合は、 その引数はオプションである必要はありません。
キーワードオプションは次のような意味を持ちます。
:interactive
:interactiveによって指定された値は、 適切な引数を取る関数でなければなりません。 (function value)が現在のレキシカルな環境上で評価されます。 これは引数なしの関数を返却しなければならず、 その関数が起動されるとinvoke-restart-interactivelyによって 使われる引数を返却します。 invoke-restart-interactivelyは、 restartが試行される前にその利用可能な動的環境下で呼び出され、 query-ioがユーザーとの対話で使用されます。 restartが対話式に呼ばれたものの :interactiveオプションが指定されていなかったときは、 その引数リストは空リストのものが呼び出しに使われます。 :report
:reportによる値がラムダ式かシンボルのとき、 それはfunctionで受け付けられるものでなければなりません。 (function value)が現在のレキシカルな環境上で評価されます。 これは引数をストリームひとつ取る関数を返却しなければならず、 その関数はrestartの定義をストリームへ印刷するものです。 この関数はrestartが印刷されるとき *print-escape*がnilであれば いつでも呼び出されます。 (lambda (stream) (write-string value stream))restartが報告を尋ねられたものの 引数に:reportが指定されていなかったときは、 そのrestartの名前がデフォルトの報告のテキストを 生成するときに使用されます。 *print-escape*がnilのとき、 プリンターはrestartの報告情報を使います。 例えば、デバッガーはcontinueコマンドが 入力されたときのアクションを次のように説明するかもしれません。(format t "~&~S -- ~A~%" ':continue some-restart):CONTINUE -- Return to command levelrestartに :reportオプションが提供されていなかったときの 結果は未定義です。 :test
:testによって指定された値は、 適切な引数を取る関数でなければなりません。 (function value)が現在のレキシカルな環境上で評価されます。 これは引数をコンディションひとつ取る関数を返却しなければならず、 その関数はもしそのrestartが見えていると考えられるなら trueを返却するようなものです。 (lambda (c) (declare (ignore c)) t)と同等です。 もしrestartable-formがリストであり、 car部がsignal, error, cerror, warnのシンボルののどれか出るとき (またはマクロフォームで展開したらこのようなリストになるとき)、 with-condition-restartsが暗黙的に使用され、 指定したrestartと通知されるコンディションが関連付けられます。
(restart-case
(handler-bind ((error #'(lambda (c)
(declare (ignore condition))
(invoke-restart 'my-restart 7))))
(error "Foo."))
(my-restart (&optional v) v))
=> 7
(define-condition food-error (error) ())
=> FOOD-ERROR
(define-condition bad-tasting-sundae (food-error)
((ice-cream :initarg :ice-cream :reader bad-tasting-sundae-ice-cream)
(sauce :initarg :sauce :reader bad-tasting-sundae-sauce)
(topping :initarg :topping :reader bad-tasting-sundae-topping))
(:report (lambda (condition stream)
(format stream "Bad tasting sundae with ~S, ~S, and ~S"
(bad-tasting-sundae-ice-cream condition)
(bad-tasting-sundae-sauce condition)
(bad-tasting-sundae-topping condition)))))
=> BAD-TASTING-SUNDAE
(defun all-start-with-same-letter (symbol1 symbol2 symbol3)
(let ((first-letter (char (symbol-name symbol1) 0)))
(and (eql first-letter (char (symbol-name symbol2) 0))
(eql first-letter (char (symbol-name symbol3) 0)))))
=> ALL-START-WITH-SAME-LETTER
(defun read-new-value ()
(format t "Enter a new value: ")
(multiple-value-list (eval (read))))
=> READ-NEW-VALUE
(defun verify-or-fix-perfect-sundae (ice-cream sauce topping)
(do ()
((all-start-with-same-letter ice-cream sauce topping))
(restart-case
(error 'bad-tasting-sundae
:ice-cream ice-cream
:sauce sauce
:topping topping)
(use-new-ice-cream (new-ice-cream)
:report "Use a new ice cream."
:interactive read-new-value
(setq ice-cream new-ice-cream))
(use-new-sauce (new-sauce)
:report "Use a new sauce."
:interactive read-new-value
(setq sauce new-sauce))
(use-new-topping (new-topping)
:report "Use a new topping."
:interactive read-new-value
(setq topping new-topping))))
(values ice-cream sauce topping))
=> VERIFY-OR-FIX-PERFECT-SUNDAE
(verify-or-fix-perfect-sundae 'vanilla 'caramel 'cherry)
>> Error: Bad tasting sundae with VANILLA, CARAMEL, and CHERRY.
>> To continue, type :CONTINUE followed by an option number:
>> 1: Use a new ice cream.
>> 2: Use a new sauce.
>> 3: Use a new topping.
>> 4: Return to Lisp Toplevel.
>> Debug> :continue 1
>> Use a new ice cream.
>> Enter a new ice cream: 'chocolate
=> CHOCOLATE, CARAMEL, CHERRYなし。
なし。
なし。
restart-bind, with-simple-restart,
(restart-case expression
(name1 arglist1 ...options1... . body1)
(name2 arglist2 ...options2... . body2))上記のフォームは下記のものと同等です。
(block #1=#:g0001
(let ((#2=#:g0002 nil))
(tagbody
(restart-bind ((name1 #'(lambda (&rest temp)
(setq #2# temp)
(go #3=#:g0003))
...slightly-transformed-options1...)
(name2 #'(lambda (&rest temp)
(setq #2# temp)
(go #4=#:g0004))
...slightly-transformed-options2...))
(return-from #1# expression))
#3# (return-from #1#
(apply #'(lambda arglist1 . body1) #2#))
#4# (return-from #1#
(apply #'(lambda arglist2 . body2) #2#)))))名前なしのrestartは一般的に対話式でのみ使用され、 :interactiveオプションは定義を持たず小さな値です。 実装は名前なしのrestartが:report情報なしで コンパイル時に提供されていたときは、警告を出すことが推奨されます。 実行時では、このエラーはデバッガーに入ったときに通知されるかもしれません。 エラーを通知すると、 デバッガーに再帰的に入場される可能性があるため (さらに別の再帰的なエラーを引き起こすなど)、 デバッガーはエラーが起こっても実際にはエラーを通知せず、 その問題の何らかの指示を印刷するようにお勧めします。
(restart-case (signal fred)
(a ...)
(b ...))
==
(restart-case
(with-condition-restarts fred
(list (find-restart 'a)
(find-restart 'b))
(signal fred))
(a ...)
(b ...))