Macro RESTART-CASE

UP


Macro RESTART-CASE

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-casenilが返却されます。

もしcase-nameがシンボルなら、それはrestartの名前です。

同じ名前のcase-nameを使ったclauseは複数存在できます。 このような場合、その名前の最初のclausefind-restartによって発見されます。 他のclausecompute-restartsを使うことでアクセスできます。

arglistは通常のラムダリスト、 対応するformを実行中に束縛されます。 これらのパラメーターはrestart-caseclauseinvoke-restart呼び出しから必要なデータを受け取って使われます。

標準では、invoke-restart-interactivelyは引数を受け取らず、 全ての引数はrestartから対話式で集めるための オプションでなければなりません。 しかし、もしinvoke-restart-interactivelyへ どのように正しい引数リストを計算するのかについて 知らせるときに使われる:interactiveキーワードを持っている場合は、 その引数はオプションである必要はありません。

キーワードオプションは次のような意味を持ちます。

もし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 ...))

TOP, Github