WITH-SIMPLE-RESTART — 간단한 restart를 걸어두는 매크로
WITH-SIMPLE-RESTART — 간단한 restart를 걸어두는 매크로
프로그램이 오류 상황에 빠졌을 때, 사용자에게 '이 지점에서 포기하고 돌아갈래요?' 같은 선택지를 제공하고 싶다면 restart를 써야 해요. CLHS의 restart 매커니즘은 강력하지만 restart-case를 직접 쓰려면 문법이 조금 무거워요. 아주 자주 쓰는 단순한 경우를 간편하게 만든 것이 with-simple-restart예요. 이 문서는 매크로(macro) 문서예요.
본문
문법 (Syntax)
with-simple-restart (name format-control format-argument*) form*
=> result*
인자와 값 (Arguments and Values)
name— 심볼(symbol)이에요.format-control— format control이에요.format-argument— 객체(object), 즉 format 인자예요.forms— implicit progn이에요.results— 정상 상황에서는forms가 반환하는 값들이에요. 예외 상황에서name이라는 restart가 호출되면 두 값, 즉nil과t를 반환해요.
설명 (Description)
with-simple-restart는 restart 하나를 확립해요.
forms를 실행하는 동안 name이 가리키는 restart가 호출되지 않으면, 마지막 forms가 반환하는 모든 값을 반환해요. name이 가리키는 restart가 호출되면 제어가 with-simple-restart로 옮겨지고, 이 폼은 두 값 nil과 t를 반환해요.
name이 nil이면 익명(anonymous) restart가 확립돼요.
format-control과 format-arguments는 restart를 보고(report)하는 데 쓰여요.
예제 (Examples)
오류가 났을 때 '명령 레벨로 돌아가기', '명령 레벨에서 빠져나오기' 같은 선택지를 제시하는 예를 볼게요.
(defun read-eval-print-loop (level)
(with-simple-restart (abort "Exit command level ~D." level)
(loop
(with-simple-restart (abort "Return to command level ~D." level)
(let ((form (prog2 (fresh-line) (read) (fresh-line))))
(prin1 (eval form)))))))
=> READ-EVAL-PRINT-LOOP
(read-eval-print-loop 1)
(+ 'a 3)
>> Error: The argument, A, to the function + was of the wrong type.
>> The function expected a number.
>> To continue, type :CONTINUE followed by an option number:
>> 1: Specify a value to use this time.
>> 2: Return to command level 1.
>> 3: Exit command level 1.
>> 4: Return to Lisp Toplevel.
두 번째로, nil을 이름으로 써 익명 restart를 만드는 예시예요. 값이 너무 커지면 에러를 내고, 아래 함수가 그 restart를 통해 something big으로 대체해요.
(defun compute-fixnum-power-of-2 (x)
(with-simple-restart (nil "Give up on computing 2^~D." x)
(let ((result 1))
(dotimes (i x result)
(setq result (* 2 result))
(unless (fixnump result)
(error "Power of 2 is too large."))))))
COMPUTE-FIXNUM-POWER-OF-2
(defun compute-power-of-2 (x)
(or (compute-fixnum-power-of-2 x) 'something big))
COMPUTE-POWER-OF-2
(compute-power-of-2 10)
1024
(compute-power-of-2 10000)
>> Error: Power of 2 is too large.
>> To continue, type :CONTINUE followed by an option number.
>> 1: Give up on computing 2^10000.
>> 2: Return to Lisp Toplevel
>> Debug> :continue 1
=> SOMETHING-BIG
compute-fixnum-power-of-2가 nil과 t를 반환하면 or가 그다음 표현식 'something big을 평가해서 SOMETHING-BIG를 돌려주는 흐름이에요.
부수 효과 (Side Effects)
없음.
영향받는 요소 (Affected By)
없음.
예외 상황 (Exceptional Situations)
없음.
더 알아보기 (See Also)
restart-case
참고 (Notes)
with-simple-restart는 restart-case의 가장 흔한 용법 중 하나를 줄여 놓은 문법이에요.
with-simple-restart는 다음과 같이 정의할 수 있어요.
(defmacro with-simple-restart ((restart-name format-control
&rest format-arguments)
&body forms)
`(restart-case (progn ,@forms)
(,restart-name ()
:report (lambda (stream)
(format stream ,format-control ,@format-arguments))
(values nil t))))
예외 상황에서 두 번째 반환 값이 t이므로, 보통은(항상은 아니지만) 정상 상황의 두 번째 반환 값을 생략하거나 nil로 만들어 두 상황을 구분하는 게 흔해요.