가져오기와 내보내기: `require` 와 `provide`
가져오기와 내보내기: require 와 provide
require는 다른 모듈의 바인딩을 가져오고, provide는 내보낼 바인딩을 선언해요. 모듈 경로를 지정해 자동으로 모듈을 방문·인스턴스화하고, 특정 식별자만 골라오거나 이름을 바꿔 가져올 수 있어요.
출처: Racket Reference
본문
3.2 가져오기와 내보내기: require 와 provide
require 는 The Racket Guide의 "Imports: require" 절에서 소개돼요.
(planet rel-string
(user-string pkg-string vers)
rel-string ...)
syntax
(require require-spec ...)
require-spec = module-path
| (only-in require-spec id-maybe-renamed ...)
| (except-in require-spec id ...)
| (prefix-in prefix-id require-spec)
| (rename-in require-spec [orig-id bind-id] ...)
| (combine-in require-spec ...)
| (relative-in module-path require-spec ...)
| (only-meta-in phase-level require-spec ...)
| (only-space-in space require-spec ...)
| (for-syntax require-spec ...)
| (for-template require-spec ...)
| (for-label require-spec ...)
| (for-meta phase-level require-spec ...)
| (for-space space require-spec ...)
| derived-require-spec
module-path = root-module-path
| (submod root-module-path submod-path-element ...)
| (submod "." submod-path-element ...)
| (submod ".." submod-path-element ...)
root-module-path = (quote id)
| rel-string
| (lib rel-string ...+)
| id
| (file string)
| (planet id)
| (planet string)
|
submod-path-element = id
| ".."
id-maybe-renamed = id
| [orig-id bind-id]
phase-level = exact-integer
| #f
space = id
| #f
vers =
| nat
| nat minor-vers
minor-vers = nat
| (nat nat)
| (= nat)
| (+ nat)
| (- nat)
최상위(top-level) 문맥에서 require 는 모듈을 인스턴스화해요(Modules and Module-Level Variables 참고). 최상위 문맥이나 모듈 문맥에서 require 의 확장은 모듈을 방문(visit)해요(Module Expansion, Phases, and Visits 참고). 두 문맥, 그리고 평가와 확장 양쪽 모두에서 require 는 바인딩을 네임스페이스나 모듈에 도입해요(Introducing Bindings 참고). 표현식 문맥이나 내부-정의(internal-definition) 문맥의 require 폼은 문법 오류예요.
require-spec 은 가져오는 문맥에 묶일 특정 식별자들을 지정해요. 각 식별자는 특정 모듈의 특정 내보내기에 매핑되어요. 묶일 식별자는 원래 내보내진 식별자의 기호적 이름과 달라도 돼요. 또한 각 식별자는 특정 위상 단계(phase level)와 바인딩 공간(binding space)에서 묶여요.
가져오기가 같은 위상 단계와 바인딩 공간 조합에서 어떤 식별자를 여러 번 묶을 수는 없어요. 단, 모든 바인딩이 같은 모듈의 같은 원래 정의를 가리키는 경우는 예외예요. 모듈 문맥에서 어떤 식별자는 주어진 위상 단계와 바인딩 공간에 대해 가져오거나 정의할 수 있지만, 둘 다는 안 돼요.
require-spec 의 문법은 define-require-syntax 로 확장할 수 있고, require 에 여러 require-spec 을 지정하면 각 require-spec 의 바인딩이 이후의 require-spec 을 확장할 때 보이게 돼요. racket/base 가 내보내는 미리 정의된 폼들은 다음과 같아요.
syntax
module-path
지정된 모듈의 모든 내보내진 바인딩을 가져와요. 이때 내보내진 이름을 로컬 식별자로 사용해요.(module-path 에 대한 자세한 내용은 아래 참고) module-path 폼의 어휘 문맥이 도입되는 식별자들의 문맥을 결정하며, 특정 바인딩 공간의 내보내기와 각 내보내기의 위상 단계에 공간 스코프를 더해요.
module-path 가 내보내는 어떤 식별자가 인터넷(참조)되지 않는 기호 형식(uninterned symbol)을 가진다면 그 식별자는 가져오지 않아요(즉, uninterned 기호에 대한 바인딩은 가져올 수 없어요). 이 제약은 모듈이 파일로 저장됐는지 여부에 따라 컴파일 결과가 달라지는 것을 피하려는 의도예요(Printing Compiled Code 참고).
syntax
(only-in require-spec id-maybe-renamed ...)
require-spec 과 같지만, 묶일 식별자가 id-maybe-renamed 와 일치하는 내보내기로 제한돼요(id 로 지정하거나 [orig-id bind-id] 의 orig-id 로 지정). id-maybe-renamed 가 bind-id 를 가질 때는 bind-id 의 어휘 문맥이 바인딩에 사용돼요. 어떤 id-maybe-renamed 의 id 나 orig-id 가 require-spec 이 지정하는 집합에 없으면 문법 오류가 보고돼요.
예:
> (require (only-in racket/tcp
tcp-listen
[tcp-accept my-accept]))
tcp-listen
#<procedure:tcp-listen>
my-accept
#<procedure:tcp-accept>
tcp-accept
tcp-accept: undefined;cannot reference an identifier before its definitionin module: top-level
syntax
(except-in require-spec id ...)
require-spec 과 같지만, id 들이 묶일 식별자인 가져오기를 제외해요. 어떤 id 가 require-spec 이 지정하는 집합에 없으면 문법 오류가 보고돼요.
예:
> (require (except-in racket/tcp
tcp-listen))
tcp-accept
#<procedure:tcp-accept>
tcp-listen
tcp-listen: undefined;cannot reference an identifier before its definitionin module: top-level
syntax
(prefix-in prefix-id require-spec)
require-spec 과 같지만, 묶일 각 식별자에 prefix-id 를 접두사로 붙여요. prefix-id 의 어휘 문맥은 무시되고, 대신 접두사가 붙기 전 식별자들의 어휘 문맥이 보존돼요.
예:
> (require (prefix-in tcp: racket/tcp))
> tcp:tcp-accept
#<procedure:tcp-accept>
> tcp:tcp-listen
#<procedure:tcp-listen>
require 의 확장된 폼에서 로컬 식별자에 'import-or-export-prefix-ranges 키를 가진 문법 속성이 추가돼요.
Changed in version 8.9.0.5 of package base: 'import-or-export-prefix-ranges 문법 속성이 추가됐어요.
syntax
(rename-in require-spec [orig-id bind-id] ...)
require-spec 과 같지만, 묶일 식별자 orig-id 를 bind-id 로 바꿔요. bind-id 의 어휘 문맥이 바인딩에 사용돼요. 어떤 orig-id 가 require-spec 이 지정하는 집합에 없으면 문법 오류가 보고돼요.
예:
> (require (rename-in racket/tcp
(tcp-accept accept)
(tcp-listen listen)))
accept
#<procedure:tcp-accept>
listen
#<procedure:tcp-listen>
syntax
(combine-in require-spec ...)
require-spec 들의 합집합이에요. require-spec 들로부터의 두 개 이상의 가져오기가 같은 식별자 이름을 가지는데 같은 원래 바인딩을 가리키지 않으면 문법 오류가 보고돼요.
예:
> (require (combine-in (only-in racket/tcp tcp-accept)
(only-in racket/tcp tcp-listen)))
tcp-accept
#<procedure:tcp-accept>
tcp-listen
#<procedure:tcp-listen>
syntax
(relative-in module-path require-spec ...)
require-spec 들의 합집합과 같지만, require-spec 에 있는 각 상대 모듈 경로가 둘러싼 문맥 대신 module-path 에 상대적인 것으로 취급돼요.
relative-in 을 구현하는 require 트랜스포머는 current-require-module-path 를 설정해서 require-spec 들의 모듈 경로를 조정해요.
syntax
(only-meta-in phase-level require-spec ...)
require-spec 들의 합집합과 같지만, phase-level 에 해당하지 않는 바인딩은 모두 제거해요. 여기서 phase-level 이 #f 면 레이블 위상 단계에 해당해요.
다음 예는 위상 단계 1(트랜스폼 단계)에서만 바인딩을 가져와요:
> (module nest racket
(provide (for-syntax meta-eggs)
(for-meta 1 meta-chicks)
num-eggs)
(define-for-syntax meta-eggs 2)
(define-for-syntax meta-chicks 3)
(define num-eggs 2))
(require (only-meta-in 1 'nest))
> (define-syntax (desc stx)
(printf "~s ~s\n" meta-eggs meta-chicks)
#'(void))
(desc)
2 3
num-eggs
num-eggs: undefined;cannot reference an identifier before its definitionin module: top-level
다음 예는 위상 단계 0(일반 단계)에서만 바인딩을 가져와요.
> (require (only-meta-in 0 'nest))
> num-eggs
2
syntax
(only-space-in space require-spec ...)
require-spec 들의 합집합과 같지만, space 가 지정하는 바인딩 공간 식별자로 제공되지 않는 바인딩은 모두 제거해요. space 는 보통 식별자지만 #f 는 기본 바인딩 공간에 해당해요.
Added in version 8.2.0.3 of package base.
syntax
(for-meta phase-level require-spec ...)
require-spec 들의 합집합과 같지만, 각 require-spec 이 지정하는 바인딩이 phase-level 만큼 이동돼요. 레이블 위상 단계는 #f 에 해당하고, #f 를 포함하는 이동 조합은 #f 를 만들어내요.
예:
> (module nest racket
(provide num-eggs)
(define num-eggs 2))
(require (for-meta 0 'nest))
num-eggs
2
(require (for-meta 1 'nest))
> (define-syntax (roost stx)
(datum->syntax stx num-eggs))
(roost)
2
syntax
(for-syntax require-spec ...)
(for-meta 1 require-spec ...) 와 같아요.
syntax
(for-template require-spec ...)
(for-meta -1 require-spec ...) 와 같아요.
syntax
(for-label require-spec ...)
(for-meta #f require-spec ...) 와 같아요. 어떤 require-spec 의 한 식별자가 둘 이상의 위상 단계에서 묶이면 문법 오류가 보고돼요.
syntax
(for-space space require-spec ...)
require-spec 들의 합집합과 같지만, 각 require-spec 이 지정하는 바인딩이 space 가 지정하는 바인딩 공간으로 이동돼요. space 는 보통 식별자지만 #f 는 기본 바인딩 공간에 해당해요.
바인딩은 require-spec 이 원래 암시하던 공간 스코프(있으면)를 제거하고, space 의 스코프(있으면)를 더해서 새 공간으로 이동돼요.
Added in version 8.2.0.3 of package base.
syntax
derived-require-spec
require-spec 폼 집합을 확장하는 방법은 define-require-syntax 를 참고해요.
The Racket Guide의 "Module Paths" 절이 모듈 경로를 소개해요.
module-path 는 모듈을 식별해요. 다른 모듈 안에 어휘적으로 선언된 루트 모듈이거나 서브모듈이에요. 루트 모듈은 식별자 형태의 구체적인 이름이나, 모듈 선언의 자동 로딩을 촉발할 수 있는 간접적인 이름으로 식별돼요. 아래의 (quote id) 경우를 제외하면, 루트 모듈 경로의 실제 해석은 현재 모듈 이름 해석기(current-module-name-resolver)에 달려 있고, 아래 설명은 기본 모듈 이름 해석기에 해당해요.
syntax
(quote id)
이름 id 로 이전에 선언된 서브모듈이나, 이름 id 로 상호작용적으로 이전에 선언된 모듈을 가리켜요. id 가 서브모듈을 가리키면 (quote id) 는 (submod "." id) 와 동등해요.
예:
; a module declared interactively as test:
> (require 'test)
syntax
rel-string
둘러싼 소스에 상대적인 경로예요(current-load-relative-directory 나 current-directory 로 결정). 현재 플랫폼과 무관하게 rel-string 은 항상 유닉스 형식의 상대 경로로 해석돼요: / 가 경로 구분자(인접한 / 는 허용되지 않음), .. 가 부모 디렉터리에 접근, . 가 현재 디렉터리에 접근해요. 경로는 비어 있거나 앞·뒤 슬래시를 포함할 수 없고, 마지막 요소 이전의 경로 요소는 파일 접미사(즉, . 나 .. 이 아닌 요소의 .)를 포함할 수 없으며, 허용되는 문자는 ASCII 문자, ASCII 숫자, -, +, _, ., /, % 뿐이에요. 게다가 % 는 두 개의 소문자 16진수 숫자가 뒤따를 때만 허용되고, 그 숫자들은 문자·숫자·-·+·_ 중 하나의 ASCII 값이 아닌 수를 이뤄야 해요.
% 조항은 임의 문자열을 경로 요소로 1:1 인코딩(UTF-8 인코딩 후)하기 위한 거예요. 그런 인코딩은 파일 이름을 얻으려고 디코딩되지 않고, 파일 접근에 보존돼요.
rel-string 이 ".ss" 접미사로 끝나면 ".rkt" 접미사로 변환돼요. ".rkt" 파일이 없고 ".ss" 파일이 있으면 컴파일된-로드 핸들러가 그 변환을 되돌릴 수 있어요.
예:
; a module named "x.rkt" in the same
; directory as the enclosing module's file:
> (require "x.rkt")
; a module named "x.rkt" in the parent directory
; of the enclosing module file's directory:
> (require "../x.rkt")
syntax
(lib rel-string ...+)
컬렉션에 설치된 모듈로 가는 경로예요(Libraries and Collections 참고). lib 의 rel-string 들은 일반 rel-string 경우와 비슷하게 제약되지만, rel-string 이 . 나 .. 디렉터리 표시를 포함할 수 없다는 추가 제약이 있어요.
경로의 구체적 해석은 rel-string 들의 개수와 모양에 따라 달라져요.
rel-string이 하나 주어지고, 그것이 단일 요소(즉/없음)로 파일 접미사(즉.없음)가 없으면,rel-string은 컬렉션 이름이고 "main.rkt" 가 라이브러리 파일 이름이에요.
예:
; the main swindle library:
> (require (lib "swindle"))
; the same:
> (require (lib "swindle/main.rkt"))
rel-string이 하나 주어지고, 여러/로 구분된 요소들로 이뤄지면, 마지막 이전의 각 요소는 컬렉션·하위컬렉션 등을 이름 짓고, 마지막 요소는 파일 이름을 지어요. 마지막 요소에 파일 접미사가 없으면 ".rkt" 가 추가되고, ".ss" 접미사는 ".rkt" 로 변환돼요.
예:
; "turbo.rkt" from the "swindle" collection:
> (require (lib "swindle/turbo"))
; the same:
> (require (lib "swindle/turbo.rkt"))
; the same:
> (require (lib "swindle/turbo.ss"))
rel-string이 하나 주어지고, 파일 접미사(즉.)가 있는 단일 요소로 이뤄지면,rel-string은 "mzlib" 컬렉션 안의 파일을 이름 지어요. ".ss" 접미사는 ".rkt" 로 변환돼요(이 관례는 오래된 Racket 버전과의 호환을 위한 것).
예:
; "tar.rkt" module from the "mzlib" collection:
> (require (lib "tar.ss"))
- 그 외에, 여러
rel-string이 주어지면, 첫rel-string이 실제로 다른 것들 뒤로 옮겨지고, 모든rel-string이/구분자로 이어져요. 결과 경로는 컬렉션, 하위컬렉션 등을 이름 짓고 파일 이름으로 끝나요. 접미사는 자동으로 추가되지 않지만 ".ss" 접미사는 ".rkt" 로 변환돼요(이 관례는 오래된 Racket 버전과의 호환을 위한 것).
예:
; "tar.rkt" module from the "mzlib" collection:
> (require (lib "tar.ss" "mzlib"))
syntax
id
id 의 기호적 형태와 같은 문자들로 된 단일 rel-string 을 가진 lib 폼의 약어예요. lib rel-string 의 제약에 더해, id 는 . 을 포함해선 안 돼요.
예:
> (require racket/tcp)
syntax
(file string)
일반 rel-string 경우와 비슷하지만, string 이 현재 플랫폼의 경로 관례와 expand-user-path 를 사용하는 (아마 절대) 경로예요. ".ss" 접미사는 ".rkt" 로 변환돼요.
예:
> (require (file "~/tmp/x.rkt"))
(planet rel-string
(user-string pkg-string vers)
rel-string ...)
syntax
(planet id)
(planet string)
PLaneT 서버를 통해 사용 가능한 라이브러리를 지정해요.
첫 형태는 마지막 형태의 약어로, id 의 문자 시퀀스가 다음 ‹spec› 문법과 일치해야 해요:
‹spec› ::= ‹owner› / ‹pkg› ‹lib›
‹owner› ::= ‹elem›
‹pkg› ::= ‹elem› | ‹elem› : ‹version›
‹version› ::= ‹int› | ‹int› : ‹minor›
‹minor› ::= ‹int› | <= ‹int› | >= ‹int› | = ‹int›
|
| ‹int› - ‹int›
‹lib› ::= ‹empty› | / ‹path›
‹path› ::= ‹elem› | ‹elem› / ‹path›
여기서 ‹elem› 은 ASCII 문자·ASCII 숫자·-·+·_·%(다른 허용 문자를 인코딩하지 않는 소문자 16진수 숫자가 뒤따르는)의 비어 있지 않은 시퀀스이고, ‹int› 는 ASCII 숫자의 비어 있지 않은 시퀀스예요. 이 약어가 펼쳐질 때 ‹pkg› 에 ".plt" 확장자가, ‹path› 에 ".rkt" 확장자가 추가돼요. ‹path› 가 포함되지 않으면 확장에서 "main.rkt" 가 사용돼요.
(planet string) 폼은 식별자를 문자열로 변환한 (planet id) 폼과 같지만, string 이 ‹path› 의 파일 확장자(즉 .)로 선택적으로 끝날 수 있어요. ".ss" 파일 확장자는 ".rkt" 로 변환돼요.
planet 모듈 경로의 더 일반적인 마지막 형태에서 rel-string 들은 lib 폼과 비슷하지만, (user-string pkg-string vers) 가 컬렉션 대신 PLaneT 기반 패키지를 이름 지어요. 버전 지정은 선택적 메이저·마이너 버전을 포함할 수 있고, 마이너 버전은 특정 숫자나 제약일 수 있어요: (nat nat) 는 포함 범위, (= nat) 는 정확히 일치, (+ nat) 는 최소 버전(그냥 nat 와 동등), (- nat) 는 최대 버전을 지정해요. 마이너 버전 제약의 =, +, - 식별자는 기호적으로 인식돼요.
예:
; "main.rkt" in package "farm" by "mcdonald":
> (require (planet mcdonald/farm))
; "main.rkt" in version >= 2.0 of "farm" by "mcdonald":
> (require (planet mcdonald/farm:2))
; "main.rkt" in version >= 2.5 of "farm" by "mcdonald":
> (require (planet mcdonald/farm:2:5))
; "duck.rkt" in version >= 2.5 of "farm" by "mcdonald":
> (require (planet mcdonald/farm:2:5/duck))
syntax
(submod root-module-path submod-path-element ...)
(submod "." submod-path-element ...)
(submod ".." submod-path-element ...)
root-module-path 가 지정하는 모듈 안의 서브모듈, 또는 (submod "." ....) 의 경우 현재 모듈에 상대적인 서브모듈을 식별해요. 여기서 (submod ".." submod-path-element ...) 는 (submod "." ".." submod-path-element ...) 와 동등해요. 서브모듈은 기호적 이름을 갖고, submod-path-element 들의 식별자 시퀀스가 주어진 이름들의 연속적으로 중첩된 서브모듈 경로를 결정해요. submod-path-element 로서의 ".." 는 서브모듈의 둘러싼 모듈을 이름 짓고, (submod "." ....) 와 (submod ".." ....) 폼에서 사용하도록 의도돼요.
require 가 일련의 require-spec 들을 처리할 준비를 하면서, 현재 로거에 'info 수준에서 'module-prefetch 이름으로 "prefetch" 메시지를 기록해요. 메시지 데이터는 두 요소의 리스트예요: 가져오는 것으로 보이는 모듈 경로들의 리스트와, 상대 모듈 경로에 사용할 디렉터리 경로. 기록된 모듈 경로 리스트는 불완전할 수 있지만, 컴파일 관리자는 대략적인 prefetch 정보로 병렬 컴파일을 시작할 수 있어요.
Changed in version 6.0.1.10 of package base: prefetch 로깅이 추가됐어요.
syntax
(local-require require-spec ...)
require 와 같지만, 내부-정의 문맥에서 로컬 문맥에만 가져올 때 사용돼요. 위상 단계 0의 바인딩만 가져와져요.
예:
> (let ()
(local-require racket/control)
fcontrol)
fcontrol
fcontrol: undefined;cannot reference an identifier before its definitionin module: top-level
Exports: provide 는 The Racket Guide의 "Exports: provide" 절에서 소개돼요.
syntax
(provide provide-spec ...)
provide-spec = id
| (all-defined-out)
| (all-from-out module-path ...)
| (rename-out [orig-id export-id] ...)
| (except-out provide-spec provide-spec ...)
| (prefix-out prefix-id provide-spec)
| (struct-out id)
| (combine-out provide-spec ...)
| (protect-out provide-spec ...)
| (for-meta phase-level provide-spec ...)
| (for-syntax provide-spec ...)
| (for-template provide-spec ...)
| (for-label provide-spec ...)
| (for-space space provide-spec ...)
| derived-provide-spec
phase-level = exact-integer
| #f
space = id
| #f
모듈에서 내보낼 것을 선언해요. provide 폼은 모듈 문맥이나 모듈-시작(module-begin) 문맥에 나타나야 해요.
provide-spec 은 내보낼 하나 이상의 바인딩을 나타내요. 각 내보내진 바인딩에 대해 외부 이름은 모듈 안에 묶인 식별자의 기호적 형태와 다를 수 있는 기호예요. 또한 각 내보내기는 특정 위상 단계에서 가져와 같은 위상 단계로 내보내져요. 기본적으로 관련 위상 단계는 provide 폼을 둘러싼 begin-for-syntax 폼들의 개수예요. 마지막으로 각 내보내기는 바인딩 공간에서 draw되고 같은 바인딩 공간으로 내보내져요.
provide-spec 의 문법은 provide 트랜스포머나 provide 사전-트랜스포머에 대한 바인딩으로 확장될 수 있어요(예: define-provide-syntax). 하지만 미리 정의된 폼들은 다음과 같아요.
syntax
id
관련 위상 단계와 바인딩 공간에서 모듈 안에 묶여(즉 정의되거나 가져와져) 있어야 하는 id 를 내보내요. id 의 기호적 형태가 외부 이름으로 사용되고, 정의되거나 가져와진 식별자의 기호적 형태와 일치해야 해요(그렇지 않으면 외부 이름이 모호해질 수 있어요).
예:
> (module nest racket
(provide num-eggs)
(define num-eggs 2))
(require 'nest)
num-eggs
2
id 가 rename 트랜스포머에 대한 트랜스포머 바인딩을 가지면, 그 트랜스포머가 내보내진 바인딩에 영향을 줘요. 자세한 내용은 make-rename-transformer 를 참고해요.
syntax
(all-defined-out)
내보내는 모듈 안의 관련 위상 단계에 정의되고, (all-defined-out) 폼과 같은 어휘 문맥을 가진 모든 식별자를 내보내요. 단, 대상 식별자가 'not-provide-all-defined 문법 속성을 가진 rename 트랜스포머에 대한 바인딩은 제외해요. 각 식별자의 외부 이름은 식별자의 기호적 형태예요. (all-defined-out) 폼의 어휘 문맥에서 접근 가능한 식별자만 포함돼요. 즉, 매크로에 의해 도입된 가져오기는 (all-defined-out) 폼이 동시에 도입되지 않으면 재내보내지지 않아요.
예:
> (module nest racket
(provide (all-defined-out))
(define num-eggs 2))
(require 'nest)
num-eggs
2
syntax
(all-from-out module-path ...)
각 module-path 에 기반한 require-spec(Importing and Exporting: require and provide 참고)으로, 위상 단계 이동 없이 내보내는 모듈에 가져와진 모든 식별자를 내보내요. 내보내기 위한 기호적 이름은 각 module-path 로부터의 내보내기의 기호적 이름이 아니라, 모듈 안에 묶인 이름에서 파생돼요. module-path 의 어휘 문맥에서 접근 가능한 식별자만 포함돼요. 즉, 매크로에 의해 도입된 가져오기는 module-path 가 동시에 도입되지 않으면 재내보내지지 않아요.
예:
> (module nest racket
(provide num-eggs)
(define num-eggs 2))
> (module hen-house racket
(require 'nest)
(provide (all-from-out 'nest)))
(require 'hen-house)
num-eggs
2
syntax
(rename-out [orig-id export-id] ...)
관련 위상 단계와 바인딩 공간에서 모듈 안에 묶여 있어야 하는 각 orig-id 를 내보내요. 각 내보내기의 기호적 이름은 orig-id 대신 export-id 예요.
예:
> (module nest racket
(provide (rename-out [count num-eggs]))
(define count 2))
(require 'nest)
num-eggs
2
count
count: undefined;cannot reference an identifier before its definitionin module: top-level
syntax
(except-out provide-spec provide-spec ...)
첫 provide-spec 과 같지만, 각 이후 provide-spec 에 나열된 바인딩을 제외해요. 이후 바인딩 중 하나가 초기 provide-spec 에 포함되지 않으면 문법 오류가 보고돼요. 이후 provide-spec 들의 기호적 내보내기 이름 정보는 무시되고, 바인딩만 사용돼요.
예:
> (module nest racket
(provide (except-out (all-defined-out)
num-chicks))
(define num-eggs 2)
(define num-chicks 3))
(require 'nest)
num-eggs
2
num-chicks
num-chicks: undefined;cannot reference an identifier before its definitionin module: top-level
syntax
(prefix-out prefix-id provide-spec)
provide-spec 과 같지만, provide-spec 의 각 기호적 내보내기 이름에 prefix-id 를 접두사로 붙여요.
예:
> (module nest racket
(provide (prefix-out chicken: num-eggs))
(define num-eggs 2))
(require 'nest)
chicken:num-eggs
2
provide 의 확장된 폼에서 내보내진 식별자에 'import-or-export-prefix-ranges 키를 가진 문법 속성이 추가돼요.
Changed in version 8.9.0.5 of package base: 'import-or-export-prefix-ranges 문법 속성이 추가됐어요.
syntax
(struct-out id)
구조체 타입 id 와 관련된 바인딩들을 내보내요. 보통 id 는 (struct id ....) 로 묶여요. 더 일반적으로, id 는 관련 위상 단계에서 구조체 타입 정보의 트랜스포머 바인딩을 가져야 해요(Structure Type Transformer Binding 참고). 게다가 구조체 타입 정보에 언급된 각 식별자에 대해, 둘러싼 모듈은 free-identifier=? 인 식별자 하나를 정의하거나 가져와야 해요. 구조체 타입 정보가 슈퍼타입 식별자를 포함하고, 그 식별자가 구조체 타입 정보의 트랜스포머 바인딩을 가진다면, 슈퍼타입의 접근자와 변경자 바인딩은 struct-out 이 내보내기에 포함하지 않아요.
예:
> (module nest racket
(provide (struct-out egg))
(struct egg (color wt)))
(require 'nest)
(egg-color (egg 'blue 10))
'blue
syntax
(combine-out provide-spec ...)
provide-spec 들의 합집합이에요.
예:
> (module nest racket
(provide (combine-out num-eggs num-chicks))
(define num-eggs 2)
(define num-chicks 1))
(require 'nest)
num-eggs
2
num-chicks
1
syntax
(protect-out provide-spec ...)
provide-spec 들의 합집합과 같지만, 내보내기가 보호돼요. 즉, require 하는 모듈은 이 바인딩들을 참조할 수 있지만, 매크로 확장에서 이 바인딩을 추출하거나 접근 권한 없이 eval 로 접근할 수는 없어요. 자세한 내용은 Code Inspectors 를 참고해요. provide-spec 은 내보내는 모듈 안에 정의된 바인딩만 지정해야 해요.
예:
> (module nest racket
(provide num-eggs (protect-out num-chicks))
(define num-eggs 2)
(define num-chicks 3))
(define weak-inspector (make-inspector (current-code-inspector)))
> (define (weak-eval x)
(parameterize ([current-code-inspector weak-inspector])
(define weak-ns (make-base-namespace))
(namespace-attach-module (current-namespace)
''nest
weak-ns)
(parameterize ([current-namespace weak-ns])
(namespace-require ''nest)
(eval x))))
(require 'nest)
(list num-eggs num-chicks)
'(2 3)
(weak-eval 'num-eggs)
2
(weak-eval 'num-chicks)
?: access disallowed by code inspector to protected variablefrom module: 'nestat: num-chicks
Code Inspectors for Trusted and Untrusted Code 도 참고해요.
syntax
(for-meta phase-level provide-spec ...)
provide-spec 들의 합집합과 같지만, 현재 위상 단계에 상대적인 phase-level (여기서 #f 는 레이블 위상 단계)이 지정하는 위상 단계에 적용되도록 조정돼요. 특히 provide-spec 으로서의 id 나 rename-out 폼은 현재 단계에 상대적인 phase-level 의 바인딩을 참조하고, all-defined-out 은 현재 위상 단계에 상대적인 phase-level 의 정의만 내보내며, all-from-out 은 phase-level 만큼 이동해서 가져와진 바인딩을 내보내요.
예:
> (module nest racket
(begin-for-syntax
(define eggs 2))
(define chickens 3)
(provide (for-syntax eggs)
chickens))
(require 'nest)
> (define-syntax (test-eggs stx)
(printf "Eggs are ~a\n" eggs)
#'0)
(test-eggs)
Eggs are 2
0
chickens
3
> (module broken-nest racket
(define eggs 2)
(define chickens 3)
(provide (for-syntax eggs)
chickens))
eval:7:0: provide: provided identifier is not defined orrequiredat: eggsin: (provide (for-syntax eggs) chickens)
> (module nest2 racket
(begin-for-syntax
(define eggs 2))
(provide (for-syntax eggs)))
> (require (for-meta 2 racket/base)
(for-syntax 'nest2))
> (define-syntax (test stx)
(define-syntax (show-eggs stx)
(printf "Eggs are ~a\n" eggs)
#'0)
(begin
(show-eggs)
#'0))
Eggs are 2
(test)
0
syntax
(for-syntax provide-spec ...)
(for-meta 1 provide-spec ...) 와 같아요.
syntax
(for-template provide-spec ...)
(for-meta -1 provide-spec ...) 와 같아요.
syntax
(for-label provide-spec ...)
(for-meta #f provide-spec ...) 와 같아요.
syntax
(for-space space provide-spec ...)
provide-spec 들의 합집합과 같지만, space 가 지정하는 바인딩 공간에 적용되도록 조정돼요. 여기서 space 는 식별자이거나 기본 바인딩 공간을 뜻하는 #f 예요. 특히 provide-spec 으로서의 id 나 rename-out 폼은 space 의 바인딩을 참조하고, all-defined-out 은 space 의 정의만 내보내며, all-from-out 은 space 로 가져와진 바인딩을 내보내요.
비기본 바인딩 공간에 대한 바인딩을 제공할 때, 보통 모듈은 기본 바인딩 공간에 대한 바인딩도 제공해야 해요. 기본 공간 바인딩이 식별자의 의도된 의미를 나타내기 때문이에요. 나중에 모듈이 이 관례를 따르는 모듈들로부터 다른 공간들에서 같은 이름을 가져올 때, 두 모듈이 기본 공간에서 그 이름에 대해 같은 바인딩을 (재)내보낸다면 가져오기는 대체로 일관돼요. 두 모듈이 기본 공간에서 그 이름에 대해 다른 바인딩을 내보낸다면, 두 모듈을 모두 가져오려는 시도는 충돌하는 가져오기 오류를 촉발하고, 프로그래머가 불일치를 명시적으로 해결할 수 있어요.
Added in version 8.2.0.3 of package base.
syntax
derived-provide-spec
provide-spec 폼 집합을 확장하는 방법은 define-provide-syntax 를 참고해요.
모듈 안에 지정된 각 내보내기는 고유한 기호적 내보내기 이름을 가져야 해요. 다만 같은 바인딩을 여러 기호적 이름으로 지정할 수는 있어요.
syntax
(for-meta phase-level require-spec ...)
require 와 provide 를 참고해요.
syntax
(for-syntax require-spec ...)
require 와 provide 를 참고해요.
syntax
(for-template require-spec ...)
require 와 provide 를 참고해요.
syntax
(for-label require-spec ...)
require 와 provide 를 참고해요.
syntax
(for-space space require-spec ...)
require 와 provide 를 참고해요.
(prefix-all-except prefix-id
raw-module-path id ...)
(planet rel-string
(user-string pkg-string vers ...))
syntax
(#%require raw-require-spec ...)
raw-require-spec = phaseless-spec
| (for-meta phase-level raw-require-spec ...)
| (for-syntax raw-require-spec ...)
| (for-template raw-require-spec ...)
| (for-label raw-require-spec ...)
| (just-meta phase-level raw-require-spec ...)
| (portal portal-id content)
phase-level = exact-integer
| #f
phaseless-spec = spaceless-spec
| (for-space space phaseless-spec ...)
| (just-space space spaceless-spec ...)
space = id
| #f
spaceless-spec = raw-module-path
| (only raw-module-path id ...)
| (prefix prefix-id raw-module-path)
| (all-except raw-module-path id ...)
|
| (rename raw-module-path local-id exported-id)
raw-module-path = raw-root-module-path
| (submod raw-root-module-path id ...+)
| (submod "." id ...+)
raw-root-module-path = (quote id)
| rel-string
| (lib rel-string ...)
| id
| (file string)
|
| literal-path
require 가 확장되는 원시 가져오기 폼이에요. raw-require-spec 은 require 폼의 require-spec 과 비슷하지만, 문법이 더 제약되고, 구성 가능하지 않으며, 확장 가능하지 않아요. 또한 for-syntax 나 lib 같은 하위 폼 이름은 바인딩을 통하지 않고 기호적으로 인식돼요. 일부 중첩 제약은 위 문법에 공식화되지 않아요:
just-meta폼은just-meta폼 안에 나타날 수 없어요.for-meta,for-syntax,for-template,for-label폼은for-meta,for-syntax,for-template,for-label폼 안에 나타날 수 없어요.for-space폼은for-space폼 안에 나타날 수 없어요.portal폼은just-meta폼 안에 나타날 수 없어요.
portal 폼을 제외하면 각 raw-require-spec 은 명백한 require-spec 에 해당하지만, rename 하위 폼은 rename-in 에 비해 식별자 순서가 반대예요.
대부분의 raw-require-spec 에서, raw-require-spec 의 어휘 문맥이 도입되는 식별자들의 문맥을 결정해요. 예외는 rename 하위 폼으로, local-id 의 어휘 문맥이 보존돼요.
raw-root-module-path 로서의 literal-path 는 path? 의 의미로 경로에 해당해요. 경로 값은 read-syntax 가 만들어내지 않으므로, 프로그램적으로 구성된 표현식에만 나타나요. 또한 namespace-require 같은 함수에 인자로 자연스럽게 나타나는데, 그런 함수는 인용된 raw-module-spec 을 받아요.
portal 폼은 어떤 위상 단계에서든 portal 문법을 정의하는 방법을 제공해요. (portal portal-id content) 는 portal-id 를 portal 문법으로 정의하되, content 를 실질적으로 인용해 그 내용으로 삼아요.
Changed in version 8.2.0.3 of package base: for-space 와 just-space 가 추가됐어요.
Changed in version 8.3.0.8: portal 이 추가됐어요.
syntax
(#%provide raw-provide-spec ...)
raw-provide-spec = phaseless-spec
| (for-meta phase-level phaseless-spec ...)
| (for-syntax phaseless-spec ...)
| (for-label phaseless-spec ...)
| (protect raw-provide-spec ...)
phase-level = exact-integer
| #f
phaseless-spec = spaceless-spec
| (for-space space spaceless-spec ...)
| (protect phaseless-spec ...)
space = id
| #f
spaceless-spec = id
| (rename local-id export-id)
| (struct struct-id (field-id ...))
| (all-from raw-module-path)
| (all-from-except raw-module-path id ...)
| (all-defined)
| (all-defined-except id ...)
| (prefix-all-defined prefix-id)
| (prefix-all-defined-except prefix-id id ...)
| (protect spaceless-spec ...)
| (expand (id . datum))
| (expand (id . datum) orig-form)
provide 가 확장되는 원시 내보내기 폼이에요. raw-module-path 는 #%require 의 것과 같아요. protect 하위 폼은 protect 하위 폼 안에 나타날 수 없어요.
#%require 와 마찬가지로 #%provide 의 하위 폼 키워드는 기호적으로 인식되고, 거의 모든 raw-provide-spec 은 provide 를 통한 명백한 동등 provide-spec 을 가져요. 단, struct 와 expand 하위 폼은 예외예요.
(struct struct-id (field-id ...)) 하위 폼은 struct-id, make-struct-id, struct:struct-id, struct-id?, 각 field-id 에 대한 struct-id-field-id, 그리고 각 field-id 에 대한 set-struct-id-field-id! 로 확장돼요. struct-id 의 어휘 문맥이 생성된 모든 식별자에 사용돼요.
#%require 와 달리 #%provide 폼은 명시적 expand 하위 폼을 통해 매크로 확장 가능해요. (id . datum) 부분이 표현식으로서 로컬 확장되고(실제로 표현식은 아니지만) begin 폼이 만들어질 때 멈춰요. 확장 결과가 (begin raw-provide-spec ...) 이면 expand 폼 자리에 스플라이스되고, 그렇지 않으면 문법 오류가 보고돼요. orig-form 부분이 제공되면 "provide identifier is not defined" 같은 문법 오류를 일으킬 때 #%provide 폼 대신 사용돼요. expand 하위 폼은 보통 직접 사용되지 않아요. provide 와 provide 트랜스포머를 구현하기 위한 훅을 제공해요.
all-from 과 all-from-except 폼은 all-from 나 all-from-except 폼 자신의 어휘 문맥에서 접근 가능한 식별자만 재내보내요. 즉, 매크로에 의해 도입된 가져오기는 all-from 이나 all-from-except 폼이 동시에 도입되지 않으면 재내보내지지 않아요. 마찬가지로 all-defined 과 그 변형들은 spaceless-spec 폼의 어휘 문맥에서 접근 가능한 정의만 내보내요.
Changed in version 8.2.0.3 of package base: for-space 가 추가됐어요.
Changed in version 8.2.0.5: expand 에 orig-form 지원이 추가됐어요.
3.2.1 추가적인 require 폼
(require racket/require) package: base
이 절에 문서화된 바인딩은 racket/base 나 racket 이 아니라 racket/require 라이브러리가 제공해요.
다음 폼들은 가져온 식별자 집합의 더 복잡한 선택과 조작을 지원해요.
syntax
(matching-identifiers-in regexp require-spec)
require-spec 과 같지만, 이름이 regexp 와 일치하는 가져오기만 포함해요. regexp 는 리터럴 정규 표현식이어야 해요(Regular Expressions 참고).
예:
> (module zoo racket/base
(provide tunafish swordfish blowfish
monkey lizard ant)
(define tunafish 1)
(define swordfish 2)
(define blowfish 3)
(define monkey 4)
(define lizard 5)
(define ant 6))
(require racket/require)
(require (matching-identifiers-in #rx"\\w*fish" 'zoo))
tunafish
1
swordfish
2
blowfish
3
monkey
monkey: undefined;cannot reference an identifier before its definitionin module: top-level
syntax
(subtract-in require-spec subtracted-spec ...)
require-spec 과 같지만, subtracted-spec 들 중 하나가 가져왔을 가져오기들을 제외해요.
예:
> (module earth racket
(provide land sea air)
(define land 1)
(define sea 2)
(define air 3))
> (module mars racket
(provide aliens)
(define aliens 4))
> (module solar-system racket
(require 'earth 'mars)
(provide (all-from-out 'earth)
(all-from-out 'mars)))
(require racket/require)
(require (subtract-in 'solar-system 'earth))
land
land: undefined;cannot reference an identifier before its definitionin module: top-level
aliens
4
syntax
(filtered-in proc-expr require-spec)
require-spec 의 가져오기 이름(문자열로)에 임의 변환을 적용해요. proc-expr 은 확장 시점에 단일 인자 프로시저로 평가되어야 하며, require-spec 의 각 이름에 적용돼요. 각 이름에 대해 프로시저는 가져오기의 새 이름인 문자열이나, 가져오기를 제외하기 위한 #f 를 반환해야 해요.
filtered-in 의 두 번째 부분은 둘러싼 모듈의 스코프에서 평가되는 확장-시점 코드예요. 따라서 대부분의 용도는 racket/base 가 for-syntax 로 이미 가져와지지 않았다면 (require (for-syntax racket/base)) 가 필요해요. 예를 들어 #lang racket 은 이 가져오기를 자동으로 설정하지만 #lang racket/base 는 그러지 않아요.
예를 들어:
(require (filtered-in
(lambda (name)
(and (regexp-match? #rx"^[a-z-]+$" name)
(regexp-replace #rx"-" (string-titlecase name) "")))
racket/base))
는 racket/base 에서 패턴 #rx"^[a-z-]+$" 와 일치하는 바인딩만 가져오고, 그 이름들을 "카멜 케이스"로 변환해요.
syntax
(path-up rel-string ...)
rel-string 들을 직접 사용하는 것과 비슷하게 rel-string 들이 이름 짓는 모듈로 가는 경로를 지정하지만, 필수 모듈 파일이 둘러싼 소스에 상대적인 곳에서 발견되지 않으면 부모 디렉터리에서, 그다음 조부모 디렉터리에서, 계속해서 루트 디렉터리까지 찾아요. 둘러싼 소스에 상대적인 발견된 경로가 확장된 폼의 일부가 돼요.
이 폼은 "프로젝트 환경"을 설정할 때 유용해요. 예를 들어 프로젝트의 루트 디렉터리에 다음 "config.rkt" 파일을 두고:
#lang racket/base
(require racket/require-syntax
(for-syntax "utils/in-here.rkt"))
(provide utils-in)
(define-require-syntax utils-in in-here-transformer)
같은 루트 디렉터리 아래에 "utils/in-here.rkt"를 두면:
#lang racket/base
(require racket/runtime-path)
(provide in-here-transformer)
(define-runtime-path here ".")
(define (in-here-transformer stx)
(syntax-case stx ()
[(_ sym)
(identifier? #'sym)
(let ([path (build-path here (format "~a.rkt" (syntax-e #'sym)))])
(datum->syntax stx `(file ,(path->string path)) stx))]))
그러면 path-up 는 프로젝트 디렉터리 아래의 다른 어떤 모듈에서도 "config.rkt"를 찾는 데 동작해요:
(require racket/require
(path-up "config.rkt")
(utils-in foo))
예에서 require 의 순서가 중요하므로 주의하세요. 앞의 두 개가 각각 뒤에서 사용되는 식별자를 묶기 때문이에요.
이 시나리오의 대안은 path-up 을 직접 사용해 유틸리티 모듈을 찾는 거예요:
(require racket/require
(path-up "utils/foo.rkt"))
하지만 그러면 "utils"라고 불리는 하위 디렉터리들이 프로젝트 루트의 것을 덮어써요. 다시 말해, 앞의 방법은 단일 고유 이름만 요구해요.
syntax
(multi-in subs ...+)
subs = sub-path
| (sub-path ...)
sub-path = rel-string
| id
디렉터리나 컬렉션의 계층에서 가져올 여러 파일을 지정해요. 필수 모듈 경로 집합은 subs 그룹들의 곱집합(Cartesian product)으로 계산돼요. 각 sub-path 는 / 구분자를 사용해 순서대로 다른 sub-path 들과 결합돼요. subs 로서의 sub-path 는 (sub-path) 와 동등해요. 주어진 multi-in 폼의 모든 sub-path 는 문자열이거나 식별자여야 해요.
예:
(require (multi-in racket (dict list)))
는 (require racket/dict racket/list) 와 동등해요.
(require (multi-in "math" "matrix" "utils.rkt"))
는 (require "math/matrix/utils.rkt") 와 동등해요.
(require (multi-in "utils" ("math.rkt" "matrix.rkt")))
는 (require "utils/math.rkt" "utils/matrix.rkt") 와 동등해요.
(require (multi-in ("math" "matrix") "utils.rkt"))
는 (require "math/utils.rkt" "matrix/utils.rkt") 와 동등해요.
(require (multi-in ("math" "matrix") ("utils.rkt" "helpers.rkt")))
는 동등하게:
(require "math/utils.rkt" "math/helpers.rkt"
"matrix/utils.rkt" "matrix/helpers.rkt")
3.2.2 추가적인 provide 폼
(require racket/provide) package: base
이 절에 문서화된 바인딩은 racket/base 나 racket 이 아니라 racket/provide 라이브러리가 제공해요.
syntax
(matching-identifiers-out regexp provide-spec)
provide-spec 과 같지만, 외부 이름이 regexp 와 일치하는 바인딩의 내보내기만 포함해요. regexp 는 리터럴 정규 표현식이어야 해요(Regular Expressions 참고).
syntax
(filtered-out proc-expr provide-spec)
filtered-in 과 유사하지만, 내보내기를 필터링하고 이름을 바꿔요.
#lang racket/base 와 함께 사용하려면 filtered-in 문서를 참고해요.
예를 들어:
(provide (filtered-out
(lambda (name)
(and (regexp-match? #rx"^[a-z-]+$" name)
(regexp-replace
#rx"-" (string-titlecase name) "")))
(all-defined-out)))
는 패턴 #rx"^[a-z-]+$" 와 일치하는 바인딩만 내보내고, 그 이름들을 "카멜 케이스"로 변환해요.