`ABORT`·`CONTINUE`·`MUFFLE-WARNING`·`STORE-VALUE`·`USE-VALUE` — 리스타트 전환 함수
ABORT·CONTINUE·MUFFLE-WARNING·STORE-VALUE·USE-VALUE — 리스타트 전환 함수
커먼 리스프의 오류 처리는 그냥 프로그램을 멈추는 데서 끝나지 않아요. 실행 지점을 특정 지점으로 되돌리는 리스타트(restart) 라는 이름표를 걸 수 있고, 이 다섯 함수는 각각 같은 이름을 가진 리스타트로 제어권을 넘기는 역할을 해요.
출처: Common Lisp HyperSpec — Function ABORT, CONTINUE, MUFFLE-WARNING, STORE-VALUE, USE-VALUE
본문
시그니처
abort &optional condition => |
continue &optional condition => nil
muffle-warning &optional condition => |
store-value value &optional condition => nil
use-value value &optional condition => nil
인자와 반환값
value— 어떤 객체.condition— 조건 객체(condition object), 또는nil.
설명
이 함수들은 각자 자신과 같은 이름을 가진, 가장 최근에 설립된 적용 가능한 리스타트로 제어권을 넘겨요. 즉 abort는 적용 가능한 abort 리스타트를 찾고, continue는 적용 가능한 continue 리스타트를 찾는 식이에요.
해당하는 리스타트가 없으면 행동이 갈라져요.
continue,store-value,use-value는nil을 반환해요.abort와muffle-warning은control-error타입의 오류를 신호해요.
condition이 nil이 아닐 때는, 그 조건과 명시적으로 연관된 리스타트나 어떤 조건에도 연관되지 않은 리스타트만 고려돼요. 즉 주어진 조건의 요소가 아닌 조건들의 비어 있지 않은 집합에 연관된 리스타트는 제외돼요. condition이 nil이면 모든 리스타트가 고려돼요.
예제
리스타트의 실제 쓰임은 오류 처리 매크로(handler-bind, restart-case)와 함께일 때 빛나요. abort-on-error 매크로는 error를 잡으면 그 자리에서 abort 리스타트로 되돌리는 예예요.
;;; ABORT 리스타트 예제
(defmacro abort-on-error (&body forms)
`(handler-bind ((error #'abort))
,@forms)) => ABORT-ON-ERROR
(abort-on-error (+ 3 5)) => 8
(abort-on-error (error "You lose."))
>> Returned to Lisp Top Level.
continue 리스타트는 디버거에서 어떤 옵션을 선택했을 때 그 지점으로 이어서 진행하도록 도와줘요.
;;; CONTINUE 리스타트 예제
(defun real-sqrt (n)
(when (minusp n)
(setq n (- n))
(cerror "Return sqrt(~D) instead." "Tried to take sqrt(-~D)." n))
(sqrt n))
(real-sqrt 4) => 2
(real-sqrt -9)
>> Error: Tried to take sqrt(-9).
>> To continue, type :CONTINUE followed by an option number:
>> 1: Return sqrt(9) instead.
>> 2: Return to Lisp Toplevel.
>> Debug> (continue)
>> Return sqrt(9) instead.
=> 3
(handler-bind ((error #'(lambda (c) (continue))))
(real-sqrt -9)) => 3
muffle-warning은 경고를 무시하고 실행을 이어가고 싶을 때 써요.
;;; MUFFLE-WARNING 리스타트 예제
(defun count-down (x)
(do ((counter x (1- counter)))
((= counter 0) 'done)
(when (= counter 1)
(warn "Almost done"))
(format t "~&~D~%" counter)))
=> COUNT-DOWN
(count-down 3)
>> 3
>> 2
>> Warning: Almost done
>> 1
=> DONE
(defun ignore-warnings-while-counting (x)
(handler-bind ((warning #'ignore-warning))
(count-down x)))
=> IGNORE-WARNINGS-WHILE-COUNTING
(defun ignore-warning (condition)
(declare (ignore condition))
(muffle-warning))
=> IGNORE-WARNING
(ignore-warnings-while-counting 3)
>> 3
>> 2
>> 1
=> DONE
store-value와 use-value는 오류가 났을 때 잘못된 값을 대신할 값을 정하는 데 써요. 아래 예제는 바인딩되지 않은 변수를 읽으려다 오류가 났을 때, 값을 "이번만 쓰기"(use-value) 또는 "앞으로 저장해 쓰기"(store-value)로 선택하게 하는 예시예요.
;;; STORE-VALUE 와 USE-VALUE 리스타트 예제
(defun careful-symbol-value (symbol)
(check-type symbol symbol)
(restart-case (if (boundp symbol)
(return-from careful-symbol-value
(symbol-value symbol))
(error 'unbound-variable
:name symbol))
(use-value (value)
:report "Specify a value to use this time."
value)
(store-value (value)
:report "Specify a value to store and use in the future."
(setf (symbol-value symbol) value))))
(setq a 1234) => 1234
(careful-symbol-value 'a) => 1234
(makunbound 'a) => A
(careful-symbol-value 'a)
>> Error: A is not bound.
>> Debug> (use-value 12)
=> 12
(careful-symbol-value 'a)
>> Error: A is not bound.
>> Debug> (store-value 24)
=> 24
(careful-symbol-value 'a) => 24
use-value로 고른 값은 이번 호출만 유효하고, store-value로 고른 값은 실제로 바인딩에 저장되어 다음 호출부터는 그 값이 그대로 쓰이는 차이를 눈여겨볼 만해요.
함께 보기
handler-bind,restart-case,restart-bind등 리스타트 관련 매크로control-error조건