THROW 특수 연산자
THROW 특수 연산자 (throw)
throw는 태그 이름으로 캐치(catch) 블록을 콕 집어서, 그 블록으로 비지역(non-local) 제어 이동을 시키는 특수 연산자예요. return-from이 렉시컬 블록을 벗어난다면, throw는 다이내믹 스코프로 동작해서 호출 스택을 따라 올라가 가까운 catch로 값을 던져요. 그래서 함수를 몇 겹이나 건너뛰어도 태그만 맞으면 그 지점으로 돌아갈 수 있어요.
문법 (Syntax)
throw tag result-form =>
인자와 값 (Arguments and Values)
tag— 캐치 태그(catch tag)이며, 평가돼요.result-form— 폼이며, 아래 설명대로 평가돼요.
설명 (Description)
throw는 그 태그와 eq인 태그를 가진 catch로 비지역 제어 이동을 일으켜요.
먼저 tag를 평가해 throw 태그라는 객체를 만들고, 그 다음 result-form을 평가해 그 결과를 저장해요. result-form이 다중 값을 만들면 그 값 전부가 저장돼요. 태그가 throw 태그와 eq인 가장 최근의 대기 중인 catch가 종료되고, 저장된 결과가 그 catch의 값으로 반환돼요.
throw가 시작하는 제어 이동은 Section 5.2 (Transfer of Control to an Exit Point)에서 설명하는 방식대로 진행돼요.
예제 (Examples)
(catch 'result
(setq i 0 j 0)
(loop (incf j 3) (incf i)
(if (= i 3) (throw 'result (values i j))))) => 3, 9
(catch nil
(unwind-protect (throw nil 1)
(throw nil 2))) => 2
아래 코드의 결과는 정의되지 않아요. 첫 번째 throw가 b의 catch를 스쳐 지나가기 때문에, 휴대 가능한 프로그램은 그 다이내믹 익스텐트가 끝났다고 가정해야 해요. 그런데 catch 태그의 바인딩은 아직 해제되지 않아서, 두 번째 throw의 대상이 됩니다.
(catch 'a
(catch 'b
(unwind-protect (throw 'a 1)
(throw 'b 2))))
다음 코드는 The inner catch returns :SECOND-THROW를 출력한 뒤 :outer-catch를 반환해요.
(catch 'foo
(format t "The inner catch returns ~s.~%"
(catch 'foo
(unwind-protect (throw 'foo :first-throw)
(throw 'foo :second-throw))))
:outer-catch)
>> The inner catch returns :SECOND-THROW
=> :OUTER-CATCH
영향 (Affected By)
없음.
예외 상황 (Exceptional Situations)
throw 태그와 일치하는 대기 중인 catch 태그가 없으면, 스택의 언와인딩(unwinding)은 일어나지 않고 control-error 타입 오류가 나요. 오류가 발생할 때의 다이내믹 환경은 throw 지점에서 적용되던 환경이에요.
함께 보기 (See Also)
block,catch,return-from,unwind-protect, Section 3.1 (Evaluation)
참고 (Notes)
출구 지점이 다이내믹 스코프를 필요로 할 때(예: throw가 catch에 렉시컬로 감싸여 있지 않을 때)는 catch와 throw를 쓰고, 렉시컬 스코프만으로 충분할 때는 block과 return을 써요.