예외

예외 (Exceptions)

Racket의 예외 시스템을 알아봐요. error·raise 계열 함수, 내장 예외 타입 계층구조, 그리고 에러 메시지를 조정하는 리얼름(realm) 메커니즘까지 다룹니다.

출처: Racket Reference

예외 발생 함수 (Error Reporting and Raising)

errorraise 함수들로 예외를 발생시키는 방법을 알아봐요.

(error v ...)

error는 첫 번째 인자를 문자열로 형식화한 뒤 그 문자열과 나머지 인자를 결합해 exn:fail 구조체를 만들어 raise합니다. 즉,

(error msg v ...) ≡ (raise (make-exn:fail (format "~a" msg v ...) (current-continuation-marks)))

라는 실행을 보장하지만, 실제 error는 보다 최적화된 경로를 탑니다. msgsymbol?이거나 문자열이면 format 없이 곧바로 사용하고, vexn:fail?이면 그 예외의 message 필드를 그대로 재사용합니다. v가 오직 한 개이고 exn?이라면 그 exn을 그대로 raise합니다(다른 어떤 전처리 없이). v가 하나의 string?이면 그것을 message로 사용합니다.

exn:fail의 message 필드는 예외가 발생하는 "위치"에 대한 정보를 담고 싶을 때가 많으므로, error는 예외를 발생시키기 전에 스택 위치를 포함시킬 수 있습니다. 이때 각 v를 문자열로 변환할 때 error-value->string-handler를 사용합니다.

(error! msg v ...)

error!error와 같지만 항상 예외를 발생시킨다는 점이 다릅니다. 즉 예외는 절대 캐치될 수 없습니다. error#%app 변환으로 인해 절대 발생하지 않는다고 정적으로 알려진 경우(예: 항상 실패하는 함수 호출 뒤)에만 error!가 사용됩니다. 프로그램에서 직접 error!를 부르면 exn:fail:contract 예외가 발생합니다(프로그램에서 error!를 부르는 것은 정적으로는 "불가능"해야 하므로).

(raise exn)
(raise v) 

raise는 현재 예외 핸들러의 존재 유무에 관계없이 exn을 발생시킵니다. exn이 아닌 v를 주면 exn:fail:contract가 발생합니다. raise가 값을 반환하는 일은 없습니다. 다만 raises에 대한 핸들러는 (raise ...) 호출에서 반환할 수 있는데, 그 반환 값이 raise의 결과가 됩니다.

특히 raise는 현재 예외 핸들러가 없는 상태에서 호출되면 raise 자체가 실패(즉 exn:fail:contract를 발생)시킵니다. raise가 현재 스레드의 초기 예외 핸들러(base raise 구현)에 도달했다면 이는 예외가 완전히 캐치되지 않았음을 뜻하므로, 초기 핸들러가 exn을 처리합니다.

raise의 특별한 점은 raise가 호출되기 전에 평가되는 컨텍스트가 아니라 "예외 발생 시점의 컨텍스트"에 예외가 전개된다는 것입니다. 그래서 raise는 보통의 함수가 아니며, (#%app raise exn) 같은 형태로 쓰일 때 특별 취급됩니다. 이 특별 취급은 식의 컨텍스트(스택)가 raise 호출에 포함되도록 만들어, 실패 위치가 왜곡되지 않게 합니다.

(raise-argument-error proc arg-num msg v ...) 

raise-argument-errorexn:fail:contract 예외가 적절한 "인자가 아니어서" 발생함을 나타내는 예외를 생성해 raise합니다. proc은 잘못 인용된 프로시저 이름이고, arg-num은 문제가 된 인자의 번호(1부터 시작), msg는 예상 인자 설명입니다.

예를 들어:

> (raise-argument-error 'vector-ref "vector?" 1 "not a vector")
vector-ref: contract violation
  expected: vector?
  given: "not a vector"
  argument position: 1st
  other arguments...:

이때 exn:fail:contract의 message는 (format "~a: contract violation" proc)를 사용하고, error-value->string-handler가 각 v를 문자열로 만듭니다.

(raise-arity-error proc arg-num v ...)

raise-arity-errorexn:fail:contract:arity 예외를 생성해 raise합니다. proc은 잘못 인용된 프로시저 이름, arg-num은 잘못된 인자 개수입니다.

(raise-type-error who name v)

raise-type-errorexn:fail:contract 타입 예외를 생성해 raise합니다. who는 오류가 발생한 함수 이름, name은 예상 타입의 설명, v는 문제가 된 값입니다.

(raise-range-error who name v)

raise-range-error는 범위(range) 오류를 나타내는 exn:fail:contract를 생성해 raise합니다.

(raise-mismatch-error who msg v)
(raise-arguments-error who msg v ...)

둘 다 exn:fail:contract 예외를 생성해 raise하는데, raise-arguments-error는 "arguments: ..." 부분을 추가합니다.

(raise-order-error who arg-order)

raise-order-error는 인자 순서가 잘못되었을 때 exn:fail:contract:arity 예외를 생성해 raise합니다.

(raise-result-error who arg-num msg v)
(raise-result-arity-error who arg-num v)
(raise-contract-error who msg v)
(raise-syntax-error who message stx)
(raise-read-error who message source line column position span)
(raise-filesystem-error who path)
(raise-network-error who host port)
(raise-user-error fmt v ...)
(raise-out-of-memory-error msg)
(raise-unsupported-error fmt v ...)

각각 특정한 예외 유형의 exn:fail 종류를 생성해 raise합니다. raise-syntax-error부터는 자세히 알아봐요.

(raise-syntax-error who message stx)

형식이 잘못된 문법을 나타내는 exn:fail:syntax를 생성해 raise합니다. who는 잘못된 문법이 발생한 구문 형식의 이름이고, message는 오류 메시지, stx는 문제가 된 구문 객체입니다. who#f이면 stx에서 이름을 추출합니다. raise-syntax-error의 결과는 없습니다(항상 예외).

(raise-read-error who message source line column position span)

exn:fail:read를 생성해 raise하며, who는 소스 이름, message는 메시지, source는 소스 경로, line/column/position/span은 위치 정보입니다.

예외 표시 인자 (Error Display Parameters)

예외가 어떻게 표시되는지 제어하는 매개변수들을 알아봐요.

(parameterize ([error-print-width 50])
  ...)

error-print-width는 에러 메시지에 임베드된 Racket 값을 출력할 때 사용할 최대 문자 수를 정하는 매개변수입니다. 값이 너무 길면 잘리고 마지막 세 글자가 "..."가 됩니다. 기본값은 50이고, #f를 주면 제한 없이 출력합니다.

(error-print-context-length)
(error-print-context-length cnt)

error-print-context-length는 기본 에러 표시 핸들러가 출력할 컨텍스트("스택 트레이스") 줄 수의 최대값입니다. cnt만큼 출력한 뒤 더 많은 줄이 있으면 "..." 한 줄을 출력합니다. 0이면 컨텍스트 표시를 완전히 끕니다.

(error-print-source-location)
(error-print-source-location include?)

error-print-source-location은 읽기(read)·문법(syntax) 오류 메시지에 소스 정보(라인·컬럼·식 등)를 포함할지, 그리고 모듈 변수가 정의 실행 전에 접근될 때 메시지에 모듈 이름을 포함할지를 제어합니다. 오직 exn:fail:read, exn:fail:syntax, exn:fail:contract:variable 구조체의 message 필드에만 영향을 줍니다. 기본값은 #t입니다.

(error-value->string-handler)
(error-value->string-handler proc)

error-value->string-handler는 원시(primitive) 오류 메시지에 임베드된 Racket 값을 출력하는 변환 핸들러를 정하는 매개변수입니다. 핸들러는 값과 최대 문자 수(정수)를 받아 문자열을 돌려줍니다. 기본 핸들러는 현재 전역 포트 출력 핸들러로 값을 문자열에 출력하고, 너무 길면 잘라서 마지막 세 글자를 "..."로 만듭니다. error 같은 함수가 호출할 때, 반환된 문자열이 요청 길이보다 길면 요청 길이로 자릅니다. 바이트 문자열을 반환하면 bytes->string/utf-8로 변환하고, 그 외의 비문자열 값을 반환하면 "..."를 씁니다. 핸들러 호출은 parameterize 되어 다시 기본 핸들러를 설치하고, 읽을 수 없는 값의 출력을 허용합니다.

반환된 문자열에 개행이 들어가는데 개행으로 시작하지 않는다면, 컨텍스트(예: raise-argument-error)는 각 개행 뒤에 필요한 만큼 들여쓰기를 추가합니다. (base 패키지 8.15.0.2에서 추가된 규칙)

(error-syntax->string-handler)
(error-syntax->string-handler proc)

error-syntax->string-handler는 오류 메시지에 임베드된 문법 형태를 출력하는 변환 핸들러입니다. error-value->string-handler와 비슷하지만, 길이 대신 #f를 줄 수 있어 "출력 형태를 자르지 않는다"는 뜻입니다. 첫 인자는 보통 구문 객체지만, raise-syntax-error가 다른 S-식도 받듯이 구문 객체가 아닌 표현도 처리해야 합니다. (base 8.2.0.8 추가)

(error-syntax->name-handler)
(error-syntax->name-handler proc)

error-syntax->name-handlerraise-syntax-error가 첫 인자로 #f를 받을 때 문법 형태의 이름을 추출하는 핸들러입니다. 인자는 raise-syntax-error의 세 번째 인자(구문 객체)입니다. 이름을 추출할 수 있으면 심볼을, 그렇지 않으면 #f를 반환합니다. (base 8.15.0.2 추가)

(error-syntax->srcloc-handler)
(error-syntax->srcloc-handler proc)

error-syntax->srcloc-handlerraise-syntax-error가 호출될 때 문법 형태의 소스 위치를 추출하는 핸들러입니다. 인자는 보통 구문 객체(예: source-location->prefix가 부를 때)이지만 raise-syntax-error의 세·네 번째 인자처럼 다른 표현이 될 수도 있습니다. 결과는 srcloc 인스턴스 또는 #f입니다. (base 9.2.0.3 추가)

(error-module-path->string-handler)
(error-module-path->string-handler proc)

error-module-path->string-handlererror-value->string-handler와 비슷하지만 모듈 경로용입니다. 기본은 모듈 경로를 문자열로 write합니다. (base 8.16.0.3 추가)

내장 예외 타입 (Built-in Exception Types)

Racket에서 발생하는 예외들이 exn 아래에 형성하는 계층구조를 알아봐요.

(struct exn (message continuation-marks)
  #:extra-constructor-name make-exn
  #:transparent)

모든 예외의 기본 구조체 타입입니다. message 필드는 오류 메시지를 담고, continuation-marks 필드는 예외가 발생되기 직전 current-continuation-marks가 만든 값을 담습니다.

Racket이 발생시키는 예외는 exn 아래 계층을 이룹니다.

exn
  exn:fail
    exn:fail:contract
      exn:fail:contract:arity
      exn:fail:contract:divide-by-zero
      exn:fail:contract:non-fixnum-result
      exn:fail:contract:continuation
      exn:fail:contract:variable
    exn:fail:syntax
      exn:fail:syntax:unbound
      exn:fail:syntax:missing-module
    exn:fail:read
      exn:fail:read:eof
      exn:fail:read:non-char
    exn:fail:filesystem
      exn:fail:filesystem:exists
      exn:fail:filesystem:version
      exn:fail:filesystem:errno
      exn:fail:filesystem:missing-module
    exn:fail:network
      exn:fail:network:errno
    exn:fail:out-of-memory
    exn:fail:unsupported
    exn:fail:user
  exn:break
    exn:break:hang-up
    exn:break:terminate

각 타입을 하나씩 살펴봐요.

(struct exn:fail exn ()
  #:extra-constructor-name make-exn:fail
  #:transparent)

exn:break와 달리 "오류"를 나타내는 예외들에 대해 발생합니다.

(struct exn:fail:contract exn:fail ()
  #:extra-constructor-name make-exn:fail:contract
  #:transparent)

함수나 문법 형식의 부적절한 런타임 사용으로 인한 오류에 대해 발생합니다.

(struct exn:fail:contract:arity exn:fail:contract ()
  #:extra-constructor-name make-exn:fail:contract:arity
  #:transparent)

프로시저가 잘못된 개수의 인자로 호출될 때 발생합니다.

(struct exn:fail:contract:divide-by-zero exn:fail:contract ()
  #:extra-constructor-name make-exn:fail:contract:divide-by-zero
  #:transparent)

정확한 0으로 나누기(division by exact zero)에서 발생합니다.

(struct exn:fail:contract:non-fixnum-result exn:fail:contract ()
  #:extra-constructor-name make-exn:fail:contract:non-fixnum-result
  #:transparent)

fx+ 같은 함수가 결과가 fixnum이 아니게 될 때 발생합니다.

(struct exn:fail:contract:continuation exn:fail:contract ()
  #:extra-constructor-name make-exn:fail:contract:continuation
  #:transparent)

점프가 연속(continuation) 경계를 가로지르게 될 때 연속을 적용할 때 발생합니다.

(struct exn:fail:contract:variable exn:fail:contract (id)
  #:extra-constructor-name make-exn:fail:contract:variable
  #:transparent)
; id : symbol?

아직 정의되지 않은 최상위 변수(top-level variable)나 모듈 수준 변수(module-level variable)에 대한 참조에서 발생합니다.

(struct exn:fail:syntax exn:fail (exprs)
  #:extra-constructor-name make-exn:fail:syntax
  #:transparent)
; exprs : (listof syntax?)

read 오류가 아닌 문법 오류에 대해 발생합니다. exprs는 관련 소스 표현을 "가장 덜 구체적인 것에서 가장 구체적인 것" 순으로 가리킵니다. 이 구조체 타입은 prop:exn:srclocs 프로퍼티를 구현합니다.

(struct exn:fail:syntax:unbound exn:fail:syntax ()
  #:extra-constructor-name make-exn:fail:syntax:unbound
  #:transparent)

#%topset!이 모듈 안의 바인딩되지 않은 식별자에 대해 발생시킵니다.

(struct exn:fail:syntax:missing-module exn:fail:syntax (path)
  #:extra-constructor-name make-exn:fail:syntax:missing-module
  #:transparent)
; path : module-path?

기본 모듈 이름 해석기(module name resolver)나 기본 로드 핸들러(load handler)가 구현 파일을 찾을 수 없는 모듈 경로를 보고할 때 발생합니다. 이 구조체는 prop:exn:missing-module 프로퍼티를 구현합니다.

기본 모듈 이름 해석기는 두 번째 인자로 구문 객체를 받을 때만 이 예외를 발생시키고, 기본 로드 핸들러는 current-module-path-for-load 값이 구문 객체일 때만 발생시키며(이때 exprs와 path 필드 모두 구문 객체로 결정됨), exn:fail:filesystem:missing-module은 그 반대 경우에 발생합니다.

(struct exn:fail:read exn:fail (srclocs)
  #:extra-constructor-name make-exn:fail:read
  #:transparent)
; srclocs : (listof srcloc?)

read 오류에 대해 발생합니다. srclocs는 관련 소스 표현을 가리킵니다.

(struct exn:fail:read:eof exn:fail:read ()
  #:extra-constructor-name make-exn:fail:read:eof
  #:transparent)

read 오류 중 특히 예상치 못한 파일 끝(end-of-file) 때문에 발생하는 오류입니다.

(struct exn:fail:read:non-char exn:fail:read ()
  #:extra-constructor-name make-exn:fail:read:non-char
  #:transparent)

read 오류 중 특히 입력 스트림의 예상치 못한 비문자(="특수") 요소 때문에 발생하는 오류입니다.

(struct exn:fail:filesystem exn:fail ()
  #:extra-constructor-name make-exn:fail:filesystem
  #:transparent)

파일시스템 관련 오류(예: 파일을 찾을 수 없음)에 대해 발생합니다.

(struct exn:fail:filesystem:exists exn:fail:filesystem ()
  #:extra-constructor-name make-exn:fail:filesystem:exists
  #:transparent)

이미 존재하는 파일을 만들려 할 때 발생합니다.

(struct exn:fail:filesystem:version exn:fail:filesystem ()
  #:extra-constructor-name make-exn:fail:filesystem:version
  #:transparent)

확장을 로드할 때 버전 불일치 오류에서 발생합니다.

(struct exn:fail:filesystem:errno exn:fail:filesystem (errno)
  #:extra-constructor-name make-exn:fail:filesystem:errno
  #:transparent)
; errno : (cons/c exact-integer? (or/c 'posix 'windows 'gai))

시스템 오류 코드를 얻을 수 있는 파일시스템 오류에 대해 발생합니다. errno 필드의 심볼 부분은 오류 코드의 범주입니다. 'posix는 C/Posix errno 값, 'windows는 Windows 시스템 오류 코드(Windows에서만), 'gai는 getaddrinfo 오류 코드를 나타냅니다. exn-classify-errno도 참고하세요.

(struct exn:fail:filesystem:missing-module exn:fail:filesystem (path)
  #:extra-constructor-name make-exn:fail:filesystem:missing-module
  #:transparent)
; path : module-path?

기본 모듈 이름 해석기나 기본 로드 핸들러가 구현 파일을 찾을 수 없는 모듈 경로를 보고할 때 발생합니다. 위에서 설명한 것처럼, 기본 모듈 이름 해석기는 두 번째 인자가 구문 객체가 아닐 때, 기본 로드 핸들러는 current-module-path-for-load 값이 구문 객체가 아닐 때 이 예외를 발생시킵니다. prop:exn:missing-module 프로퍼티를 구현합니다.

(struct exn:fail:network exn:fail ()
  #:extra-constructor-name make-exn:fail:network
  #:transparent)

TCP/UDP 오류에 대해 발생합니다.

(struct exn:fail:network:errno exn:fail:network (errno)
  #:extra-constructor-name make-exn:fail:network:errno
  #:transparent)
; errno : (cons/c exact-integer? (or/c 'posix 'windows 'gai))

시스템 오류 코드를 얻을 수 있는 TCP/UDP 오류에 대해 발생하며, errno 필드는 exn:fail:filesystem:errno와 같습니다. exn-classify-errno를 참고하세요.

(struct exn:fail:out-of-memory exn:fail ()
  #:extra-constructor-name make-exn:fail:out-of-memory
  #:transparent)

메모리 부족으로 인한 오류에 대해 발생합니다. 단, 예외를 발생시키기 위한 메모리는 최소한 확보되어 있는 경우입니다.

(struct exn:fail:unsupported exn:fail ()
  #:extra-constructor-name make-exn:fail:unsupported
  #:transparent)

현재 플랫폼이나 구성에서 지원되지 않는 기능으로 인한 오류에 대해 발생합니다.

(struct exn:fail:user exn:fail ()
  #:extra-constructor-name make-exn:fail:user
  #:transparent)

최종 사용자가 보게 될 의도로 발생하는 오류입니다. 특히 기본 오류 출력기가 오류 메시지를 출력할 때 프로그램 컨텍스트를 보여주지 않습니다.

(struct exn:break exn (continuation)
  #:extra-constructor-name make-exn:break
  #:transparent)
; continuation : continuation?

브레이크 요청에 대한 응답으로 (가능할 때) 비동기적으로 발생합니다. continuation 필드는 중단된 계산을 uncaught-exception-handlercall-with-exception-handler(어떤 조건자나 핸들러를 평가하기 전에 예외 컨텍스트에서 벗어나므로 with-handlers는 제외)에서 재개하는 데 쓸 수 있습니다.

(struct exn:break:hang-up exn:break ()
  #:extra-constructor-name make-exn:break:hang-up
  #:transparent)

hang-up 브레이크에 대해 비동기적으로 발생합니다. 기본 uncaught-exception 핸들러가 이 타입에 반응해 exit handler를 호출합니다.

(struct exn:break:terminate exn:break ()
  #:extra-constructor-name make-exn:break:terminate
  #:transparent)

종료 요청 브레이크에 대해 비동기적으로 발생합니다. 기본 uncaught-exception 핸들러가 이 타입에 반응해 exit handler를 호출합니다.

소스 위치 (Source Locations)

struct-type-property/prop:exn:srclocs

srcloc 값들의 목록을 제공하는 구조체 타입을 식별하는 프로퍼티입니다. 보통 예외 정보를 나타내는 구조체 타입에 붙습니다. 프로퍼티 값은 단일 값(소스 위치를 추출할 구조체 인스턴스)을 받아 srcloc 목록을 돌려주는 프로시저여야 합니다. 일부 에러 표시 핸들러는 첫 번째 위치만 사용합니다.

예를 들어, prop:exn:srclocs 프로토콜을 지원하는 구조체를 만들어볼 수 있어요. 다음과 같이 범인(guilty)인 문법의 위치를 함께 운반하는 구조체를 정의합니다.

#lang racket

;; prop:exn:srcloc 프로토콜을 지원하는 구조체를 만든다.
;; 그 구조체는 "죄가 있는" 문법의 위치를 함께 실어 나른다.
(struct exn:fail:he-who-shall-not-be-named exn:fail
  (a-srcloc)
  #:property prop:exn:srclocs
  (lambda (a-struct)
    (match a-struct
      [(exn:fail:he-who-shall-not-be-named msg marks a-srcloc)
       (list a-srcloc)])))

;; 식별자를 살펴보고 특정한 것만 지적하는 형태로 장난쳐보자.
(define-syntax (skeeterize stx)
  (syntax-case stx ()
    [(_ expr)
     (cond
       [(and (identifier? #'expr) (eq? (syntax-e #'expr) 'voldemort))
        (quasisyntax/loc stx
          (raise (exn:fail:he-who-shall-not-be-named
                  "oh dear don't say his name"
                  (current-continuation-marks)
                  (srcloc '#, (syntax-source #'expr)
                          '#, (syntax-line #'expr)
                          '#, (syntax-column #'expr)
                          '#, (syntax-position #'expr)
                          '#, (syntax-span #'expr)))))]
       [else
        ;; 그 외에는 식을 그대로 둔다.
        #'expr])]))

(define (f x) (* (skeeterize x) x))
(define (g voldemort) (* (skeeterize voldemort) voldemort))

;; 예시:
(f 7)
(g 7)
;; 오류는 g 안에서 voldemort의 사용을 강조해야 한다.
(exn:srclocs? v) → boolean?
  v : any/c

vprop:exn:srclocs 프로퍼티를 가지면 #t, 아니면 #f를 반환합니다.

(exn:srclocs-accessor v)
→ (exn:srclocs? . -> . (listof srcloc))
  v : exn:srclocs?

v와 연관된 srcloc-얻기 프로시저를 반환합니다.

(struct srcloc (source line column position span)
  #:extra-constructor-name make-srcloc
  #:transparent)
; source : any/c
; line   : (or/c exact-positive-integer? #f)
; column : (or/c exact-nonnegative-integer? #f)
; position : (or/c exact-positive-integer? #f)
; span   : (or/c exact-nonnegative-integer? #f)

소스 위치는 대부분 srcloc 구조체로 나타냅니다. 더 일반적으로, 소스 위치는 srcloc 구조체와 같은 정보를 갖되 표현·접근 방식이 다를 수 있습니다. 예를 들어 구문 객체의 소스 위치 정보는 syntax-source·syntax-line 같은 함수로 접근하고, datum->syntax는 소스 위치를 목록·벡터·다른 구문 객체로 받습니다. 포트의 경우 object-nameport-next-location의 조합이 위치 정보를 제공합니다(특히 port-count-lines!로 카운트가 켜진 포트에서).

srcloc 인스턴스의 필드:

  • source — 소스를 식별하는 임의의 값. 흔히 경로(path)입니다.
  • line — 줄 번호(1부터 셈) 또는 #f(모름).
  • column — 열 번호(0부터 셈) 또는 #f(모름).
  • position — 시작 위치(1부터 셈) 또는 #f(모름).
  • span — 덮힌 위치의 수(0부터 셈) 또는 #f(모름).

컴파일된 코드에 임베드된 srcloc 값의 처리에 대해서는 "Printing Compiled Code"를 참고하세요.

(srcloc->string srcloc) → (or/c string? #f)
  srcloc : srcloc?

srcloc을 오류 보고에 적합한 문자열로 형식화합니다. srcloc 안의 경로 source는 current-directory-for-user 값에 상대적입니다. srcloc이 문자열을 형식화할 충분한 정보를 가지지 못하면 결과는 #f입니다.

누락 모듈 프로퍼티

struct-type-property/prop:exn:missing-module

모듈을 찾지 못해 로드가 실패한 경우 그 모듈 경로를 제공하는 구조체 타입을 식별하는 프로퍼티입니다. 프로퍼티 값은 단일 값(구조체 인스턴스)을 받아 모듈 경로를 돌려주는 프로시저여야 합니다.

(exn:missing-module? v) → boolean?
  v : any/c

vprop:exn:missing-module 프로퍼티를 가지면 #t, 아니면 #f.

(exn:missing-module-accessor v)
→ (exn:missing-module? . -> . module-path?)
  v : exn:srclocs?

v와 연관된 모듈 경로-얻기 프로시저를 반환합니다.

errno 분류

(exn-classify-errno exn/errno) → (or/c symbol? #f)
  exn/errno : (or/c exn? (cons/c exact-integer? (or/c 'posix 'windows 'gai)))

exn/errno를 심볼로 정규화해서, 다른 플랫폼의 같은 종류의 오류가 같은 심볼로 변환되도록 시도합니다. 정규화가 알려지지 않으면 결과는 #f입니다. 현재는 exn/errnoexn:fail:filesystem:errno 인스턴스, exn:fail:network:errno 인스턴스, 또는 exn:fail:filesystem:errno-errno·exn:fail:network:errno-errno가 만든 값이 아니면 #false입니다.

심볼이 반환되면 Posix류 관례를 씁니다. 가능한 결과 심볼로 'ENOENT("파일을 찾을 수 없음"), 'EEXIST("파일이 이미 존재함"), 'EACCESS("권한 거부")가 있습니다. (base 9.0.0.7 추가)

추가 예외 함수 (Additional Exception Functions)

(require racket/exn)

이 섹션의 바인딩은 racket/exn 라이브러리가 제공하며, racket/baseracket은 제공하지 않습니다. (base 6.3 추가)

(exn->string exn) → string?
  exn : (or/c exn? any/c)

exn을 문자열로 형식화합니다. exnexn?이면 현재 error-display-handler의 출력을 모아 반환하고, 아니면 (format "~s\n" exn)으로 문자열로 변환합니다.

리얼름과 에러 메시지 조정기 (Realms and Error Message Adjusters)

리얼름(realm)은 함수의 이름과 인자·결과에 대한 컨트랙트를 지정하는 관례를 식별하는 것입니다. 리얼름은 Racket 위에 구현된 언어들 사이의 계층화와 상호운용성을 개선하기 위해 고안되었습니다.

리얼름은 주로 한 언어가 구현의 하위 계층이 만든 에러 메시지를 인식하고 다시 쓰게 해줍니다. 예를 들어 "배열"을 구현한 언어가 Racket 벡터를 직접 쓴다고 해봐요. 벡터에 대해 객체 타입이나 원시 범위 검사가 실패하면, 생성된 에러 메시지는 "vector"와 vector? 같은 컨트랙트, vector-ref 같은 함수 이름을 언급합니다. 이 메시지들이 'racket/primitive 리얼름에서 온 것임이 식별되므로, 언어 구현은 'racket/primitive를 찾아 에러 메시지를 감지·다시 쓸 수 있으면서도 응용 프로그램의 다른 부분(새 언어로 구현되었을 수도 있음)에서 "vector"라는 단어를 우연히 쓴 에러 메시지를 망칠 위험을 최소화할 수 있습니다.

각 프로시저와 각 모듈도 리얼름을 갖습니다. 프로시저의 리얼름은 예를 들어 잘못된 개수의 인자로 적용될 때 관련이 있습니다. 이 경우 arity 오류 메시지 자체는 'racket/primitive 리얼름에서 오지만, 메시지에는 다른 리얼름에서 올 수 있는 프로시저 이름도 포함돼야 합니다. 이와 비슷하게 continuation-mark-set->context는 연속 안의 각 프레임(의 프로시저)과 연관된 리얼름을 보고할 수 있는데, 이는 경계를 가로지르는 지점을 식별하는 데 유용할 수 있습니다.

에러 메시지의 구성은 에러 메시지 조정(error-message adjusting)과 명시적으로 협력해야 합니다. 가장 기본적인 협력 방법은 error-message->adjusted-string·error-contract->adjusted-string 같은 함수를 쓰는 것인데, 이들은 current-error-message-adjuster 매개변수와, error-message-adjuster-key를 연속 마크 키로 사용해 현재 연속과 연관된 다른 조정기들을 통해 에러 메시지 조정기를 실행합니다. raise-argument-error·raise-arity-error 같은 함수는 기본 리얼름 'racket으로 error-message->adjusted-string·error-contract->adjusted-string을 사용합니다. raise-argument-error*·raise-arity-error* 같은 함수는 명시적인 리얼름 인자를 받습니다.

모든 에러 함수가 자동으로 에러 메시지 조정과 협력하는 것은 아닙니다. 예를 들어 raise-reader-errorraise-syntax-error는 조정기를 호출하지 않는데, 이는 문법과 밀접하게(그리고 보다 정적인 성격으로) 얽힌 오류를 보고하기 때문입니다.

(error-message->adjusted-string name name-realm message message-realm)
→ string?
  name : (or/c symbol? #f)
  name-realm : symbol?
  message : string?
  message-realm : symbol?

name(#f가 아니면)과 ": "를 결합한 다음 message를 붙여 에러 메시지 문자열을 만들되, 그 전에 에러 메시지 조정기들이 name과/또는 message를 조정할 기회를 받습니다.

error-message-adjuster-key를 연속 마크로 사용해 현재 연속과 연관된 모든 조정기 함수를 먼저 실행합니다(얕은 것부터 깊은 것 순). 그다음 current-error-message-adjuster의 조정기 값을 사용합니다.

각 조정기는 먼저 'message 프로토콜로 시도됩니다. 조정기가 'message에 대해 #f로 응답하면 'name 프로토콜을 시도합니다. current-error-message-adjuster에 대한 프로토콜 설명을 참고하세요. 둘 다 #f로 응답하는 조정기는 건너뜁니다. error-message-adjuster-key를 사용해 연속 마크로 연관된 값으로서 한 인자를 받는 프로시저가 아닌 값도 건너뜁니다. 게다가 (조정된) name이 #f이면 'name 프로토콜도 건너뜁니다. (base 8.4.0.2 추가)

(error-contract->adjusted-string contract-str contract-realm) → string?
  contract-str : string?
  contract-realm : symbol?

error-message->adjusted-string와 비슷하지만 에러 메시지의 컨트랙트 부분에만 해당합니다. 결과 문자열은 보통 더 큰 에러 메시지에 통합되어 더 조정될 수 있습니다.

컨트랙트 문자열의 조정은 current-error-message-adjuster에 설명된 'contract 프로토콜을 사용합니다. (base 8.4.0.2 추가)

(current-error-message-adjuster)
→ (symbol? . -> . (or/c procedure? #f))
(current-error-message-adjuster proc) → void?
  proc : (symbol? . -> . (or/c procedure? #f))

error-message-adjuster-key를 통해 현재 연속과 연관된 조정기들 이후에 적용될 에러 메시지 조정기를 결정하는 매개변수입니다.

조정기 프로시저는 프로토콜을 식별하는 심볼을 받고, 그 프로토콜을 통한 조정을 수행할 프로시저 또는 #f를 반환해야 합니다. 현재 정의된 프로토콜은 다음과 같습니다(앞으로 더 추가될 수 있음):

  • 'name: 프로시저는 두 인자(이름 심볼, 리얼름 심볼)를 받고 조정된 이름 심볼과 조정된 리얼름 심볼을 반환합니다.
  • 'message: 프로시저는 네 인자(이름 심볼 또는 #f(메시지에 이름이 앞붙지 않음을 뜻함), name-리얼름 심볼, 메시지 문자열, message-리얼름 심볼)를 받고 조정된 네 값을 반환합니다.
  • 'contract: 프로시저는 두 인자(컨트랙트 문자열, 리얼름 심볼)를 받고 조정된 컨트랙트 문자열과 조정된 리얼름 심볼을 반환합니다.

새 라이브러리나 언어도 추가 모드 심볼을 도입할 수 있습니다. 충돌을 피하려면 모드 심볼 앞에 콜렉션 또는 라이브러리 이름과 /를 붙이세요.

조정기 프로시저가 어떤 프로토콜에 대해 #f를 반환하면, 그건 조정을 수행하지 않고 인자를 반환하는 함수를 반환하는 것과 같습니다. 이 매개변수의 기본값은 위에서 나열한 프로토콜을 제외한 어떤 심볼 인자에 대해서도 #f를 반환하고, 나열된 프로토콜에 대해서는 인자를 검사해 조정 없이 반환하는 프로시저를 반환합니다. (base 8.4.0.2 추가)

error-message-adjuster-key : symbol?

에러 조정기 프로시저 값과 함께 continuation mark 키로 사용하기 위한 uninterned 심볼입니다. 이 키와 연관된 에러 조정기는 current-error-message-adjuster의 값과 같은 프로토콜을 따라야 합니다.

이 키를 사용하는 마크가 에러 메시지를 어떻게 조정할 수 있는지에 대한 설명은 error-message->adjusted-string을 참고하세요. (base 8.4.0.2 추가)