THROW 특수 연산자

THROW 특수 연산자 (throw)

throw태그 이름으로 캐치(catch) 블록을 콕 집어서, 그 블록으로 비지역(non-local) 제어 이동을 시키는 특수 연산자예요. return-from이 렉시컬 블록을 벗어난다면, throw다이내믹 스코프로 동작해서 호출 스택을 따라 올라가 가까운 catch로 값을 던져요. 그래서 함수를 몇 겹이나 건너뛰어도 태그만 맞으면 그 지점으로 돌아갈 수 있어요.

출처: CLHS: Special Operator THROW

문법 (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

아래 코드의 결과는 정의되지 않아요. 첫 번째 throwbcatch를 스쳐 지나가기 때문에, 휴대 가능한 프로그램은 그 다이내믹 익스텐트가 끝났다고 가정해야 해요. 그런데 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)

출구 지점이 다이내믹 스코프를 필요로 할 때(예: throwcatch에 렉시컬로 감싸여 있지 않을 때)는 catchthrow를 쓰고, 렉시컬 스코프만으로 충분할 때는 blockreturn을 써요.

더 알아보기 (Learn more)