출력기

출력기 (The Printer)

Racket의 출력기는 write, display, print 세 가지 모드를 지원해요. 데이터 타입별로 출력 규칙이 정해져 있고, print 모드는 표현식으로 다시 평가해도 같은 값이 나오도록 출력하는 것이 특징이에요.

출처: Racket Reference

본문

1.4 출력기

Racket 출력기는 세 가지 모드를 지원해요.

  • write 모드는 핵심 데이터 타입을, 출력에 read를 적용하면 출력된 값과 equal?한 값이 나오는 방식으로 출력해요.
  • display 모드는 핵심 데이터 타입을 "프로그래머" 스타일보다 "최종 사용자" 스타일에 가깝게 출력해요. 예를 들어 문자열은 주변에 "나 이스케이프 없이 그 내용 문자 그대로 display돼요.
  • print 모드는 기본적으로—print-as-expression#t일 때—대부분의 데이터 타입을, 출력을 표현식으로 평가하면 출력된 값과 equal?한 값이 나오는 방식으로 출력해요. print-as-expression#f로 설정되면 print 모드는 write 모드와 같아요.

print 모드에서 print-as-expression#t(기본값)이면, 값은 인용 깊이(quoting depth) 0(인용 안 됨) 또는 1(인용됨)으로 출력돼요. 초기 인용 깊이는 print의 선택적 인자로 받아들여지며, 일부 복합 데이터 타입의 출력은 구성 요소 값의 인쇄 깊이를 조정해요. 예를 들어 리스트가 인용 깊이 0으로 출력되고 모든 원소가 인용 가능하면, 그 리스트는 ' 접두사로 출력되고 원소들은 인용 깊이 1로 출력돼요.

print-graph 파라미터가 #t로 설정되면 출력기는 객체를 먼저 스캔해 순환을 감지해요. 이 스캔은 페어, 가변 페어, 벡터, 박스(print-box#t일 때), 해시 테이블(print-hash-table#t이고 키가 강하게 보유될 때), struct->vector로 노출되는 구조체 필드(print-struct#t일 때), 그리고 구조체 타입이 prop:custom-write 속성을 가질 때 그 출력으로 노출되는 구조체 필드를 순회해요. print-graph#t이면 이 정보로 그래프 정의와 참조를 통해 공유가 출력돼요(그래프 구조 읽기(Reading Graph Structure) 참고). 초기 스캔에서 순환이 감지되면 print-graph이 효과적으로 자동으로 #t로 설정돼요.

바이트 문자열 표시를 제외하면, 출력은 유니코드 문자로 정의돼요. 문자 스트림이 포트의 기본 바이트 스트림에 어떻게 쓰이는지는 포트(Ports)에서 확인할 수 있어요.

1.4.1 심볼 출력

공백이나 특수 문자를 포함한 심볼은 \ 이스케이프와 | 인용을 사용해 write돼요. read-case-sensitive 파라미터가 #f로 설정되면 대문자를 포함한 심볼도 \ 이스케이프와 | 인용을 사용해요. 또한 심볼이 숫자 상수나 구분된 .(read-accept-dot#t일 때)과 같게 출력될 수 있다면 | 인용이나 앞의 \로 인용돼요.

read-accept-bar-quote#t이면, 시작에 | 하나, 끝에 | 하나면 충분히 심볼을 올바르게 출력할 수 있을 때 그 |들을 사용해요. 그렇지 않으면 항상 \를 사용해 특수 문자를 |로 인용하는 대신 이스케이프해요.

read-accept-bar-quote#f이면 |는 특수 문자로 취급되지 않아요. 다음은 항상 특수 문자예요:

( ) [ ] { } " , ' `
; \

또한 #는 심볼의 시작에 나타나고 %가 뒤따르지 않을 때 특수 문자예요.

심볼은 특수 문자를 이스케이프하거나 인용하지 않고 display돼요. 즉, 심볼의 표시 형태는 그 심볼에 symbol->string을 적용한 표시 형태와 같아요.

심볼은 print-as-expression#t(기본값)로 설정되고 현재 인용 깊이가 0이 아닌 한 write와 같게 print돼요. 그 경우 심볼의 print된 형태는 ' 접두사가 붙어요. 바깥 데이터 타입의 출력이라는 관점에서 심볼은 인용 가능(quotable)해요.

1.4.2 숫자 출력

숫자는 write, display, print 모드에서 같은 방식으로 출력돼요. 바깥 데이터 타입의 출력이라는 관점에서 숫자는 인용 가능해요.

실수가 아닌 복소수는 항상 ‹m›+‹n›i 또는 ‹m›-‹n›i로 출력돼요. 여기서 ‹m›과 ‹n›은 각각 허수부가 음이 아닐 때 그 실수부와 허수부의 출력 형태이고, 허수부가 음일 때는 -‹n›이에요.

정확한 00으로 출력돼요. 양의 정확한 정수는 0으로 시작하지 않는 숫자들의 나열로 출력돼요. 양의 정확한 실수 비정수는 ‹m›/‹n›으로 출력돼요. 여기서 ‹m›과 ‹n›은 numeratordenominator가 결정한 분자와 분모의 출력 형태예요. 음의 정확한 숫자는 그 숫자의 정확한 부정(negation)의 출력 형태에 - 접두사가 붙어 출력돼요. 숫자(예: number->string을 통한)를 16진수로 출력할 때는 a부터 f까지의 숫자가 소문자로 출력돼요. #e#d 같은 기수 표시는 숫자 앞에 붙지 않아요.

유리수인 배정밀도 부정확 숫자(즉 flonum)는 . 소수점, e 지수 표시와 0이 아닌 지수, 또는 둘 다로 출력돼요. 형태는 출력을 짧게 유지하도록 선택되며, 출력된 형태를 다시 읽으면 equal?한 숫자가 나와야 한다는 제약이 있어요. #i는 숫자 앞에 붙지 않고, #는 숫자로 대신 사용되지 않아요. 양수 앞에는 +가 붙지 않지만, e가 있으면 지수 앞에 +-가 출력돼요. 양의 무한대는 +inf.0, 음의 무한대는 -inf.0, not-a-number는 +nan.0으로 출력돼요.

유리수인 단정밀도 부정확 숫자는 배정밀도 숫자처럼 출력되지만 항상 지수를 포함하고, 정밀도를 나타내기 위해 e 대신 f를 사용해요. 지수 없이 출력될 숫자라면 0(+ 없이)이 지수 부분으로 출력돼요. 단정밀도 양의 무한대는 +inf.f, 음의 무한대는 -inf.f, not-a-number는 +nan.f로 출력돼요.

1.4.3 Extflonum 출력

extflonum은 write, display, print 모드에서 같은 방식으로 출력돼요. 바깥 데이터 타입의 출력이라는 관점에서 extflonum은 인용 가능해요.

extflonum은 단정밀도 부정확 숫자(숫자 출력)처럼 출력되지만, 항상 t 또는 T 지수 표시가 붙거나 +inf.t, -inf.t, +nan.t의 접미사로 붙어요. extflonum 연산이 지원되면 출력은 항상 소문자 t를 사용해요. extflonum 연산이 지원되지 않으면, 읽기가 extflonum을 만드는 유일한 방법이므로 extflonum은 리더(리더(The Reader)) 소스와 같게 출력돼요.

1.4.4 불리언 출력

불리언 상수 #t는 모든 모드(display, write, print)에서 print-boolean-long-form 값에 따라 #true 또는 #t로 출력되고, 상수 #f#false 또는 #f로 출력돼요. 바깥 데이터 타입의 출력이라는 관점에서 심볼은 인용 가능해요.

1.4.5 페어와 리스트 출력

writedisplay 모드에서 빈 리스트는 ()로 출력돼요. 페어는 보통 (로 시작해 car의 출력 형태가 뒤따라요. 나머지 출력 형태는 cdr에 따라 달라져요.

  • cdr이 페어이거나 빈 리스트이면, 페어의 출력 형태는 cdr의 출력 형태로 완성돼요. 다만 cdr의 출력 형태의 앞의 (는 생략돼요.
  • 그 외에는 페어의 출력 형태는 공백, ., 공백, cdr의 출력 형태, 그리고 )로 이어져요.

print-reader-abbreviations#t로 설정되면, write 모드의 페어 출력은 첫 원소가 'quote, 'quasiquote, 'unquote, 'unquote-splicing, 'syntax, 'quasisyntax, 'unsyntax, 'unsyntax-splicing 중 하나인 두 원소 리스트를 시작하는 페어의 경우 조정돼요. 그 경우 페어는 해당하는 리더 문법으로 출력돼요: 각각 ', `, ,, ,@, #', `#, #,, #,@. 리더 문법 뒤에 리스트의 두 번째 원소가 출력돼요. 리스트가 바깥 리스트의 꼬리일 때, 그 꼬리는 바깥 리스트의 두 원소로 포함되는 대신 바깥 리스트 안의 . 뒤(그 뒤에서 리더 약어가 동작)에 출력돼요. 리더 문법 , 또는 #, 뒤에 앞에 @를 붙여 출력되는 심볼이 오면, 출력기는 @ 앞에 추가 공백을 넣어요.

페어의 출력 형태는 writedisplay 모드 모두에서 같고, 다만 페어의 carcdr 출력 형태가 모드에 따라 달라져요. print 형태도 print-as-expression#f이거나 인용 깊이가 1이면 같아요.

print 모드에서 print-as-expression#t이고 인용 깊이가 0이면, 빈 리스트는 '()로 출력돼요. carcdr가 인용 가능한 페어는 write 모드로 출력되지만 ' 접두사가 붙고, 페어 내용은 인용 깊이 1로 출력돼요. carcdr가 인용 가능하지 않으면 페어는 여는 ( 뒤에 cons(cdr이 페어가 아닐 때), list(페어가 리스트일 때), 또는 list*(그 외)로 출력되며, 출력되었을 .는 억제되고 페어 내용은 인용 깊이 0으로 출력돼요. 모든 경우에 print 모드에서 print-as-expression#t이면 print-reader-abbreviations 값은 무시되고 리더 약어가 항상 인용 깊이 1로 출력되는 리스트에 사용돼요.

기본적으로 mcons로 만든 가변 페어는 writedisplay에서 페어와 같게 출력되지만, () 대신 {}를 사용해요. 리더는 입력에서 {...}(...)를 동등하게 취급해 두 경우 모두 불변 페어를 만들어요는 점에 주의하세요. print 모드에서 print-as-expression#f이거나 인용 깊이가 1인 가변 페어도 {}를 사용해요. print 모드에서 print-as-expression#t이고 인용 깊이가 0이면, 가변 페어는 (mcons , 인용 깊이 0으로 출력되고 공백으로 구분된 mcarmcdr, 닫는 )로 출력돼요.

print-pair-curly-braces 파라미터가 #t로 설정되면, print 모드(print-as-expression#t, 인용 깊이 0)를 사용하지 않을 때 페어가 {}로 출력돼요. print-mpair-curly-braces 파라미터가 #f이면 그 모드에서 가변 페어가 ()로 출력돼요.

바깥 데이터 타입의 출력이라는 관점에서 빈 리스트는 항상 인용 가능하고, 페어는 carcdr가 인용 가능할 때 인용 가능하며, 가변 리스트는 절대 인용 가능하지 않아요.

package base 6.9.0.6에서 변경: , 또는 #, 뒤에 앞에 @가 붙어 출력되는 심볼이 올 때 공백을 추가함.

1.4.6 문자열 출력

모든 문자열은 그 문자 그대로의 나열로 display돼요.

문자열의 write 또는 print 형태는 "로 시작해 또 다른 "로 끝나요. "들 사이에서 각 문자를 표현해요. 그래픽 문자나 공백 문자(char-graphic?char-blank?에 따름)는 그 자체로 표현되며, 두 가지 예외가 있어요: "\"로, \\\로 출력돼요. 그래픽 문자로 시작하는 그래핌 시퀀스의 일부인 비그래픽·비공백 문자도 그 자체로 표현돼요. 그 외의 비그래픽·비공백 문자 각각은 문자열 읽기(Reading Strings)에 설명된 이스케이프 시퀀스로 출력되며, 가능하면 \a, \b, \t, \n, \v, \f, \r, \e를 사용하고, 그렇지 않으면 네 자리 16진수의 \u 또는 여덟 자리 16진수의 \U를 사용해요(문자 값이 네 자리에 안 들어갈 때만 \U 사용).

모든 바이트 문자열은 그 바이트 그대로의 나열로 display돼요. 이 바이트 나열은 유효한 UTF-8 인코딩이 아닐 수 있으므로 문자들의 나열에 대응하지 않을 수 있어요.

바이트 문자열의 write 또는 print 형태는 #"로 시작해 "로 끝나요. "들 사이에서 각 바이트는, 바이트가 0과 127 사이이고 문자가 그래픽이거나 공백이면(char-graphic?char-blank?에 따름) 해당 ASCII 디코딩으로 쓰여져요. 그 외에는 바이트가 가능하면 \a, \b, \t, \n, \v, \f, \r, \e로 쓰여지고, 그렇지 않으면 \ 뒤에 한 자리에서 세 자리(필요한 만큼만) 8진수로 쓰여져요.

바깥 데이터 타입의 출력이라는 관점에서 문자열이나 바이트 문자열은 인용 가능해요.

1.4.7 벡터 출력

display 모드에서 벡터의 출력 형태는 # 뒤에 벡터에 vector->list를 적용한 출력 형태가 따라오는 거예요. write 모드에서도 출력 형태는 같지만, print-vector-length 파라미터가 #t일 때 # 뒤에 십진 정수가 출력되고, 반복되는 마지막 원소는 한 번만 출력돼요.

벡터는 print-as-expression#t로 설정되고 현재 인용 깊이가 0이 아닌 한 write와 같게 print돼요. 그 경우 벡터의 모든 원소가 인용 가능하면 벡터의 print된 형태는 ' 접두사가 붙고 원소들은 인용 깊이 1로 출력돼요. 원소가 전부 인용 가능하지 않으면 벡터는 (vector , 인용 깊이 0의 원소들, 닫는 )print돼요. 벡터는 모든 원소가 인용 가능할 때 인용 가능해요.

writedisplay 모드에서 flvector는 벡터처럼 출력되지만 # 대신 #fl 접두사가 붙어요. fxvector도 마찬가지로 # 대신 #fx 접두사로 출력해요. print-vector-length 파라미터는 flvector와 fxvector 출력에 벡터 출력과 같은 방식으로 영향을 줘요. print 모드에서 flvector와 fxvector는 인용 가능하지 않고, 인용 깊이 0의 벡터처럼 각각 (flvector 또는 (fxvector 접두사를 사용해 출력돼요.

1.4.8 구조체 출력

print-struct 파라미터가 #t로 설정되면 구조체의 출력 방식은 그 구조체가 인스턴스인 구조체 타입의 세부 사항에 따라 달라져요.

  • 구조체 타입이 prefab 구조체 타입이면 writedisplay 모드에서 #s( 뒤에 prefab 구조체 타입 키, 구조체의 각 필드 출력 형태, 그리고 )로 출력돼요. print 모드에서 print-as-expression#t로 설정되고 현재 인용 깊이가 0이면, 구조체 내용이 전부 인용 가능할 때 구조체의 print된 형태에 ' 접두사가 붙고 내용은 인용 깊이 1로 출력돼요. 내용 중 인용 불가능한 것이 있으면 구조체 타입은 non-prefab 구조체 타입과 같게 출력돼요. prefab 구조체 타입의 인스턴스는 내용이 전부 인용 가능할 때 인용 가능해요.
  • 구조체가 prop:custom-write 속성 값을 가지면, print-unreadable 파라미터가 #f로 설정되지 않는 한 그와 연결된 프로시저가 구조체를 출력하는 데 사용돼요. print 모드에서 prop:custom-write 속성을 가진 구조체 타입의 인스턴스는, prop:custom-print-quotable 속성 값이 'always이면 인용 가능한 것으로 취급돼요. 속성 값이 'maybe이면 구조체 내용이 인용 가능할 때 인용 가능한 것으로 취급돼요. 여기서 내용은 구조체의 prop:custom-write 프로시저가 재귀적으로 출력하는 값들로 결정돼요. 마지막으로 속성 값이 'self이면 인용 가능한 것으로 취급돼요. print 모드에서 print-as-expression#t이면 구조체의 prop:custom-write 프로시저는 보통 구조체의 prop:custom-print-quotable 속성 값에 따라 인용 깊이 0 또는 1로 호출돼요. 속성 값이 'always이면 인용 깊이는 보통 1이에요. 속성 값이 'maybe이면 구조체가 인용 가능하면 인용 깊이 1, 그렇지 않으면 보통 0이에요. 속성 값이 'self이면 인용 깊이는 0 또는 1일 수 있는데, 구조체가 인용 가능한 것으로 취급되더라도 바깥의 인용 가능한 값의 일부로 출력되지 않으면 보통 0이에요. 마지막으로 속성 값이 'never이면 인용 깊이는 보통 0이에요. 구조체가 명시적 인용 깊이 1로 출력되면 인용 깊이는 정상 값과 달라질 수 있어요.
  • 구조체 타입이 투명(transparent)하거나 조상 중 하나가 투명하면(즉, 인스턴스에 대한 struct?#t를 산출하면), 구조체는 display 모드, write 모드, 또는 print-as-expression#f이거나 인용 깊이가 0print 모드에서 struct->vector가 만든 벡터로 출력돼요. print 모드에서 print-as-expression#t이고 인용 깊이가 0이면, 구조체 내용은 ( 뒤에 구조체 타입 이름(object-name으로 결정)이 write 모드로, 나머지 원소는 인용 깊이 0으로 print되고 공백으로 구분되며, 마지막에 닫는 )가 붙은 형태로 출력돼요. prefab 구조체 타입이 아닌 투명 구조체 타입은 절대 인용 가능하지 않아요.
  • 그 외의 구조체 타입은 구조체가 읽을 수 없는 값으로 출력돼요. 자세한 내용은 읽을 수 없는 값 출력(Printing Unreadable Values)을 보세요.

print-struct 파라미터가 #f로 설정되면 prop:custom-write 속성이 없는 모든 구조체가 읽을 수 없는 값으로 출력되고(읽을 수 없는 값 출력) 인용 가능한 것으로 간주돼요.

1.4.9 해시 테이블 출력

print-hash-table 파라미터가 #t이면 writedisplay 모드에서 해시 테이블은, 키를 강하게 보유하는 한, equal?, eqv?, eq? 키 비교를 각각 사용하는 테이블에 대해 #hash(, #hasheqv(, #hasheq(로 시작해 출력돼요. 접두사 뒤에 각 키-값 매핑이 (, 키의 출력 형태, 공백, ., 공백, 해당 값의 출력 형태, )로 표시되고, 키-값 쌍이 마지막으로 출력되는 것이 아니면 공백이 하나 더 추가돼요. 모든 키-값 쌍 뒤에 출력 형태는 )로 완성돼요.

print 모드에서 print-as-expression#f이거나 인용 깊이가 1이면 출력 형태는 write와 같아요. 그 외에 해시 테이블의 키와 값이 전부 인용 가능하면 테이블은 ' 접두사로 출력되고 키와 값은 인용 깊이 1print돼요. 일부 키나 값이 인용 불가능하면 해시 테이블은 (hash , (hasheqv , 또는 (hasheq 뒤에 인용 깊이 1print되고 공백으로 구분되는 키와 값이 번갈아 나오고, 마지막에 닫는 )가 붙은 형태로 출력돼요. 해시 테이블은 모든 키와 값이 인용 가능할 때 인용 가능해요.

print-hash-table 파라미터가 #f로 설정되거나 해시 테이블이 키를 약하게(weakly) 보유하면, 해시 테이블은 #<hash>로 출력되고 인용 가능한 것으로 간주돼요.

1.4.10 박스 출력

print-box 파라미터가 #t이면 박스는 print-as-expression#f이거나 인용 깊이가 1write, display, print 모드에서 #& 뒤에 내용의 출력 형태가 붙은 것으로 출력돼요.

print 모드에서 print-as-expression#t이고 인용 깊이가 0이면, 내용이 인용 가능할 때 박스는 ' 접두사로 출력되고 값은 인용 깊이 1로 출력돼요. 그렇지 않으면 박스는 (box 뒤에 인용 깊이 0의 내용과 닫는 )로 출력돼요. 박스는 내용이 인용 가능할 때 인용 가능해요.

print-box 파라미터가 #f로 설정되면 박스는 #<box>로 출력되고 인용 가능한 것으로 간주돼요.

1.4.11 문자 출력

문자 읽기(Reading Characters)에 설명된 특수 이름을 가진 문자들은 같은 이름으로 writeprint돼요. (일부 문자는 이름이 여러 개예요; #\newline#\nul 이름이 #\linefeed#\null 대신 사용돼요.) 그 외의 그래픽 문자(char-graphic?에 따름)는 #\ 뒤에 단일 문자로 write되고, 그 외의 모든 문자는 네 자리의 #\u 표기 또는 여덟 자리의 #\U 표기로 쓰여져요(문자 값이 네 자리에 안 들어갈 때만 \U 사용).

모든 문자는 그 자체로 직접 display돼요(즉 단일 문자).

바깥 데이터 타입의 출력이라는 관점에서 문자는 인용 가능해요.

1.4.12 키워드 출력

키워드는 심볼(심볼 출력)과 같게 write, print, display되지만, 앞에 #:(print 모드에서 추가되는 ' 접두사 뒤)가 붙고, 시작 #에 대한 특수 처리가 없으며, 출력 형태가 숫자나 구분된 .과 일치할 때도 특수 처리가 없어요(#:가 키워드를 구분해 주기 때문이에요).

바깥 데이터 타입의 출력이라는 관점에서 키워드는 인용 가능해요.

1.4.13 정규 표현식 출력

regexp 값은 #px(pregexp 기반 regexp) 또는 #rx(regexp 기반 regexp)로 시작해 regexp의 소스 문자열이나 바이트 문자열의 write 형태가 뒤따르는 방식으로 write, display, print돼요.

바깥 데이터 타입의 출력이라는 관점에서 regexp 값은 인용 가능해요.

1.4.14 경로 출력

경로는 #<path:....>write되고 print돼요. 경로는 path->string이 만든 문자열과 같게 display돼요. 바깥 데이터 타입의 출력이라는 관점에서 경로는 인용 가능한 것으로 간주돼요.

경로는 path->string으로 문자열로, path->bytes로 바이트 문자열로 변환할 수 있지만, 경로를 출력했다가 다시 읽기 위한 선택으로 둘 다 명확히 옳지는 않아요. 경로 값이 플랫폼 간에 옮겨질 예정이면 문자열이 아마 올바른 선택이지만, 경로를 문자열로 변환할 때 정보가 손실될 가능성이 있어요. 같은 플랫폼에서 다시 읽을 경로라면 바이트 문자열이 아마 올바른 선택인데, 이는 정보를 이식 불가능한 방식으로 보존하기 때문이에요. 경로는 프로그래머가 항상 어느 쪽이든 적절하다고 오해하지 않도록 읽을 수 있는 방식으로 출력되지 않아요.

1.4.15 읽을 수 없는 값 출력

다른 출력 규정이 없는 값은, print-unreadable 파라미터가 #t로 설정되어 있다고 가정하면, #<‹something›> 형태로 출력돼요. 여기서 ‹something›은 값의 타입에 따라, 때로는 값 자체에 따라 달라요. print-unreadable#f이면 읽을 수 없는 값을 출력하려고 하면 exn:fail이 발생해요.

바깥 데이터 타입의 출력이라는 관점에서 읽을 수 없게 출력되는 값도 인용 가능한 것으로 간주돼요.

1.4.16 컴파일된 코드 출력

compile이 만든 컴파일된 코드는 #~를 사용해 출력돼요. #~로 출력된 컴파일된 코드는 본질적으로 Racket용 어셈블리 코드이며, read-accept-compiled 파라미터가 #t로 설정되어 있으면 그런 형태를 읽으면 컴파일된 형태가 산출돼요.

#~에서 파싱된 컴파일된 코드는, 현재 코드 인스펙터(current-code-inspector 참고)가 원래 코드 인스펙터가 아니면 실행 불가능(non-runnable)으로 표시돼요. 실행 불가능한 바이트코드를 평가하거나 재최적화하려면 exn:fail 예외가 발생해요. 그 외에는 #~에서 파싱된 컴파일된 코드에 모듈에서 내보내지지 않았거나 보호된 바인딩에 대한 참조가 포함될 수 있어요. 개념적으로 바이트코드의 참조는 현재 코드 인스펙터와 연결되며, 그 인스펙터가 관련 모듈 호출을 제어할 때만 코드가 실행돼요(코드 인스펙터(Code Inspectors) 참고)—하지만 어쨌든 원래 코드 인스펙터가 다른 모든 인스펙터를 제어해요.

컴파일된 형태 객체에는 gensym이나 string->uninterned-symbol이 만든 미인턴(내부화 안 된) 심볼(심볼(Symbols) 참고)이 포함될 수 있어요. 컴파일된 객체가 #~로 읽힐 때 원본 형태의 각 미인턴 심볼은 새 미인턴 심볼로 매핑되고, 하나의 심볼의 여러 인스턴스는 일관되게 같은 새 심볼로 매핑돼요. 원본과 새 심볼은 같은 출력 표현을 가져요. 일반적으로 확장과 컴파일 중에 간접적으로 생성되는 읽을 수 없는 심볼은 #~를 통해 일관되게 저장되고 복원돼요.

미인턴 심볼의 동적 특성과 #~ 안에서의 지역화는, gensym이나 string->uninterned-symbol이 최상위 또는 모듈 바인딩을 위한 식별자를 구성할 때(식별자와 그 참조가 컴파일되는 방식에 따라) 문제를 일으킬 수 있어요. 문제를 피하려면 generate-temporaries로 서로 다른 식별자를 만들거나 make-syntax-introducer의 결과를 기존 식별자에 적용하세요. 그 함수들은 읽을 수 없는 심볼 이름을 가진 최상위 및 모듈 변수로 이어지며, 확장이 그 외에 결정적이라면 이름은 결정적이에요.

컴파일된 형태 객체에 문자열과 바이트 문자열 리터럴이 있으면, 컴파일된 형태를 다시 읽을 때 datum-intern-literal을 사용해 인턴(내부화)돼요. 그러나 read-syntax가 인턴할 숫자와 그 외 값은 컴파일된 객체에서 인용된 리터럴로 다시 읽힐 때 인턴되지 않아요.

컴파일된 형태에는 경로 리터럴이 포함될 수 있어요. 경로는 보통 다시 읽을 수 있는 방식으로 출력되지 않지만, 경로 리터럴은 컴파일된 코드의 일부로 쓰여지고 읽힐 수 있어요. current-write-relative-directory 파라미터가 쓰여질 때 경로를 상대 경로로 변환하는 데 사용되고, 그러면 current-load-relative-directory 파라미터(current-directory로 폴백)가 읽힐 때 상대 경로를 다시 변환하는 데 사용돼요.

문법 객체의 소스에 있는 경로의 경우, current-write-relative-directory 파라미터가 설정되지 않았거나 경로가 current-write-relative-directory 파라미터 값에 상대적이지 않으면, 경로는 경로의 일부만 보존하는 문자열로 강제 변환돼요(빌드 시 파일시스템에 덜 묶이도록 하려는 노력이며, 실행 시 파일시스템과 다를 수 있어요).

마지막으로 컴파일된 형태는 srcloc 구조체를 포함할 수 있어요. 구조체의 소스 필드가 어떤 시스템에 대한 경로, 문자열, 바이트 문자열, 심볼 또는 #f일 때예요. 경로 값(현재 플랫폼 관례와 일치하는)의 경우 경로가 current-write-relative-directory에 기반한 상대 경로로 기록될 수 없으면, 최대 두 개의 경로 원소를 가진 문자열로 변환돼요. 경로가 둘보다 많은 원소를 포함하면 문자열은 .../, 뒤에서 두 번째 원소, /, 마지막 원소를 포함해요. srcloc 값의 제약과 소스 필드 변환의 의도는 일부 소스 정보를 보존하되, 다른 파일시스템이나 플랫폼에서 의미 없는 경로를 노출하거나 기록하지 않는 거예요.

Racket의 BC 구현에서 내부 테스트 목적으로, PLT_VALIDATE_LOAD 환경 변수가 설정되면 리더가 #~에서 파싱된 바이트코드에 검증기를 실행해요. 검증기는 잘못된 컴파일이나 바이트코드 파일 손상을 잡을 수 있어요. 검증기는 지연 실행될 수 있는데, 예를 들어 프로시저가 호출될 때만 프로시저를 검사하는 식이에요.

package base 6.90.0.21에서 변경: 코드 인스펙터 변경이 파싱된 바이트코드에 미치는 영향을 조정해, 리더가 읽기 시간에 안전하지 않은 연산에 대한 참조를 거부하는 대신 로드된 코드를 일반적으로 실행 불가능으로 표시하도록 함. 7.0에서 변경: 컴파일된 코드에 내장된 일부 srcloc 값을 허용함.

더 알아보기