모듈 이름과 로딩
모듈 이름과 로딩 (Module Names and Loading)
모듈 이름을 해석하고, 컴파일된 모듈을 다루고, 모듈에 동적으로 접근하는 방법까지 살펴볼게요. Racket이 모듈을 어떻게 이름 짓고 로드하는지 이해하는 데 핵심이 되는 절이에요.
출처: Racket Reference
본문
14.4.1 모듈 이름 해석 (Resolving Module Names)
syntax/modresolve 라이브러리는 모듈 이름을 해석하고 조작하는 추가 연산을 제공해요.
선언된 모듈의 이름은 해석된 모듈 경로(resolved module path)로 표현되는데, 이는 심볼 또는 완전한 파일 시스템 경로(Paths 참고)를 캡슐화해요. 심볼은 보통 사전 정의된 모듈이나 반영적 평가(예: eval)를 통해 선언된 모듈을 가리켜요. 파일 시스템 경로는 보통 require나 다른 폼을 통해 온디맨드로 로드된 모듈 선언을 가리켜요.
모듈 경로(module path)는 require의 module-path 문법과 일치하는 datum이에요. 모듈 경로는 다른 모듈에 상대적이에요.
procedure
(resolved-module-path? v) → boolean?
v : any/c
v가 해석된 모듈 경로면 #t를, 그렇지 않으면 #f를 돌려줘요.
procedure
(make-resolved-module-path path) → resolved-module-path?
path :
(or/c symbol?
(and/c path? complete-path?)
(cons/c (or/c symbol?
(and/c path? complete-path?))
(non-empty-listof symbol?)))
path를 캡슐화하는 해석된 모듈 경로를 돌려줘요. 여기서 리스트 path는 서브모듈 경로에 대응해요. path가 경로이거나 경로로 시작하면, 그 경로는 보통 정화(cleanse-path 참고)되고 단순화(simplify-path 참고, 파일 시스템 검사 포함)되어야 해요.
해석된 모듈 경로는 인턴(interned)돼요. 즉 두 해석된 모듈 경로 값이 equal?한 경로를 캡슐화하면, 그 해석된 모듈 경로 값들은 eq?해요.
procedure
(resolved-module-path-name module-path)
→
(or/c symbol?
(and/c path? complete-path?)
(cons/c (or/c symbol?
(and/c path? complete-path?))
(non-empty-listof symbol?)))
module-path : resolved-module-path?
해석된 모듈 경로가 캡슐화한 경로나 심볼을 돌려줘요. 리스트 결과는 서브모듈 경로에 대응해요.
procedure
(module-path? v) → boolean?
v : any/c
v가 require의 module-path 문법과 일치하는 datum에 대응하면 #t를, 그렇지 않으면 #f를 돌려줘요. path?의 의미로 경로도 모듈 경로라는 점에 주의하세요.
parameter
(current-module-name-resolver)
→
(case->
(resolved-module-path? (or/c #f namespace?) . -> . any)
(module-path?
(or/c #f resolved-module-path?)
(or/c #f syntax?)
boolean?
. -> .
resolved-module-path?))
(current-module-name-resolver proc) → void?
proc :
(case->
(resolved-module-path? (or/c #f namespace?) . -> . any)
(module-path?
(or/c #f resolved-module-path?)
(or/c #f syntax?)
boolean?
. -> .
resolved-module-path?))
다른 종류의 모듈 참조를 해석된 모듈 경로로 변환하는 것을 관리하는 현재 모듈 이름 해석기를 결정하는 파라미터예요. 예를 들어 확장기가 (require module-path)를 만나는데 module-path가 식별자가 아니면, 확장기는 심볼이나 해석된 모듈 경로를 얻기 위해 'module-path를 모듈 이름 해석기에 넘겨요. 그런 require가 모듈 안에 나타나면, 모듈 경로 해석기에는 둘러싸는 모듈의 이름도 주어져서, 상대 참조를 절대 심볼이나 해석된 모듈 경로로 변환할 수 있어요.
기본 모듈 이름 해석기는 collection-file-path를 사용해 lib와 상징적 축약(symbolic-shorthand) 모듈 경로를 파일 시스템 경로로 변환해요. collection-file-path 함수는 차례로 current-library-collection-links와 current-library-collection-paths 파라미터를 사용해요.
모듈 이름 해석기는 두 인자와 네 인자를 받아요:
- 두 인자를 받으면, 첫 번째는 이제 현재 네임스페이스에 선언된 모듈의 이름이고, 두 번째는 선택적으로 그 선언이 복사된 네임스페이스예요. 이 경우 모듈 이름 해석기의 결과는 무시돼요.
namespace-attach-module이나 namespace-attach-module-declaration이 현재 모듈 이름 해석기를 두 인자로 호출해, 모듈 선언이 현재 네임스페이스에 붙었다는 것(그래서 앞으로 네임스페이스의 모듈 레지스트리를 위해 로드되면 안 된다는 것)을 알려줘요. 모듈 선언의 평가도 현재 모듈 이름 해석기를 두 인자로 호출하는데, 첫 번째가 선언된 모듈이고 두 번째가 #f예요. 다른 어떤 Racket 연산도 모듈 이름 해석기를 두 인자로 호출하지 않지만, DrRacket 같은 다른 도구는 중복 모듈 로드를 피하려고 이 모드로 이 해석기를 호출할 수 있어요.
- 네 인자를 받으면, 첫 번째는
require의 인용된module-path와 동등한 모듈 경로예요. 두 번째는 경로가 상대적인(있으면) 소스 모듈의 이름이고, 두 번째 인자가#f면 모듈 경로는(or (current-load-relative-directory) (current-directory))에 상대적이에요. 세 번째는#f가 아니면 오류 보고에 쓸 수 있는 구문 객체예요. 마지막 인자가#t면 모듈 선언이 (아직 없으면) 로드되어야 하고, 그렇지 않으면 모듈 경로는 단순히 이름으로 해석되어야 해요. 결과는 해석된 이름이에요.
두 번째 경우에 표준 모듈 이름 해석기는 각 모듈 레지스트리마다 로드된 모듈 이름을 담는 테이블을 유지해요. 해석된 모듈 경로가 테이블에 없고 #f가 모듈 이름 해석기의 네 번째 인자로 제공되지 않으면, 그 이름이 테이블에 들어가고 해당 파일이 load/use-compiled의 변형으로 로드되는데, 그 변형은 기대 모듈 이름을 compiled-load 핸들러에 전달해요.
파일을 로드하는 동안 기본 모듈 이름 해석기는 current-module-declare-name 파라미터를 해석된 모듈 이름으로 설정해요(compiled-load 핸들러는 current-module-declare-source를 설정하는 반면). 또한 기본 모듈 이름 해석기는 로드 중인 모듈을 private continuation 마크로 기록하고, 그런 마크가 이미 존재하는지 검사해요. 현재 continuation에 그런 continuation 마크가 이미 있으면 exn:fail 예외가 의존 순환(dependency cycle)에 대한 메시지로 발생해요.
기본 모듈 이름 해석기는 기본 compiled-load 핸들러와 협력해요. 모듈 부착(module-attach) 알림에서 compiled-load 핸들러가 소스 네임스페이스의 모듈 레지스트리에 기록한 bytecode 파일 정보가 대상 네임스페이스의 모듈 레지스트리로 옮겨져요.
기본 모듈 이름 해석기는 또한 lib와 상징적 모듈 경로를 그 해석으로 매핑하는, 작고 모듈 레지스트리 특화된 캐시를 유지해요. 이 캐시는 current-library-collection-links와 current-library-collection-paths 같은 파라미터를 검사하기 전에 참조되므로, 그 파라미터 값이 바뀌어도 결과가 "고착"될 수 있어요. 캐시에 항목이 추가되는 것은 모듈 이름 해석기의 네 번째 인자가 참(모듈을 로드해야 함을 나타냄)일 때, 그리고 로드가 성공할 때뿐이에요.
마지막으로 기본 모듈 이름 해석기는 submod 경로를 특별히 다룰 수 있어요. submod 폼의 첫 요소인 모듈 경로가 존재하지 않는 컬렉션을 가리키면, 기본 모듈 이름 해석기는 예외를 발생시키는 대신 결과 해석된 모듈 경로에 대해 인턴되지 않은(uninterned) 심볼 모듈 이름을 합성해요. 서브모듈 경로의 이 특별한 처리는 compiled-load 핸들러가 존재하지 않는 서브모듈을 특별히 다루는 것과 일관되어, module-declared?가 서브모듈의 존재를 더 쉽게 검사하는 데 쓰일 수 있어요.
구문 객체(Syntax Objects 참고) 안의 모듈 경로를 해석할 때는 모듈 로드가 억제돼요(즉 네 번째 인자로 #f가 모듈 이름 해석기에 공급돼요). 구문 객체가 조작될 때 현재 네임스페이스가 구문 객체의 원래 네임스페이스와 일치하지 않을 수 있고, 모듈이 반드시 현재 네임스페이스에 로드되어야 하는 것은 아니기 때문이에요.
역사적 이유로 기본 모듈 이름 해석기는 현재 두 인자와 네 인자 외에도 세 인자를 받아들여요. 세 인자는 네 번째 인자가 #t인 네 인자와 같게 취급되지만, 오류도 로그된다는 점이 달라요. 세 인자 지원은 미래 버전에서 제거될 거예요.
current-module-name-resolver 바인딩은 protect-out의 의미로 보호되어 제공돼요.
패키지 base의 버전 6.0.1.12에서 변경됨: 세 인자로 호출될 때 기본 모듈 이름 해석기에 오류 로깅을 추가했어요.
버전 7.0.0.17에서 변경됨: 기본 모듈 이름 해석기가 존재하지 않는 컬렉션을 가진 submod 폼을 특별히 다루도록 추가했어요.
버전 8.2.0.4에서 변경됨: 바인딩을 보호되도록 변경했어요.
parameter
(current-module-declare-name)
→ (or/c resolved-module-path? #f)
(current-module-declare-name name) → void?
name : (or/c resolved-module-path? #f)
모듈 선언을 평가할 때(파라미터 값이 #f가 아닐 때) 사용되는 모듈 이름을 결정하는 파라미터예요. 그 경우 모듈 선언의 id는 무시되고, 파라미터 값이 선언된 모듈의 이름으로 사용돼요.
서브모듈을 선언할 때 current-module-declare-name은 서브모듈의 루트 모듈에 사용되는 이름을 결정하고, 루트 모듈에 대한 서브모듈 경로는 영향을 받지 않아요.
parameter
(current-module-declare-source)
→ (or/c symbol? (and/c path? complete-path?) #f)
(current-module-declare-source src) → void?
src : (or/c symbol? (and/c path? complete-path?) #f)
모듈 선언을 평가할 때 모듈에 연결할 소스 정보를 결정하는 파라미터예요. 소스 정보는 오류 메시지에 사용되고 variable-reference->module-source가 반영해요. 파라미터 값이 #f면, 파라미터 값 대신 (current-module-declare-name이 결정한) 모듈 이름이 소스 이름으로 사용돼요.
parameter
(current-module-path-for-load)
→
(or/c #f module-path?
(and/c syntax?
(lambda (stx)
(module-path? (syntax->datum s)))))
(current-module-path-for-load path) → void?
path :
(or/c #f module-path?
(and/c syntax?
(lambda (stx)
(module-path? (syntax->datum s)))))
기본 load 핸들러가 발생시키는 exn:fail:syntax:missing-module와 exn:fail:filesystem:missing-module 예외에 사용되는 모듈 경로를 결정하는 파라미터예요. 이 파라미터는 보통 모듈 이름 해석기가 설정해요.
14.4.2 컴파일된 모듈과 참조 (Compiled Modules and References)
모듈 선언을 확장하는 동안 확장기는 import에 대한 모듈 경로를 해석해 필요에 따라 모듈 선언을 로드하고 import된 바인딩을 결정해요. 하지만 모듈 선언의 컴파일된 형태는 원래 모듈 경로를 보존해요. 결과적으로 컴파일된 모듈을 다른 파일 시스템으로 옮길 수 있고, 거기서 모듈 이름 해석기가 컴파일된 코드 사이의 모듈 간 참조를 해석할 수 있어요.
모듈 참조가 컴파일된 형태에서 추출되면(module-compiled-imports 참고) 또는 매크로 확장의 구문 객체에서 추출되면(Syntax Object Content 참고), 그 모듈 참조는 모듈 경로 인덱스(module path index)의 형태로 보고돼요. 모듈 경로 인덱스는 반-인턴된(같은 상대 모듈에 대한 여러 참조가 같은 모듈 경로 인덱스 값을 쓰는 경향이 있지만 항상 그런 것은 아님) 불투명 값으로, 모듈 경로(module-path? 참고)와 그것이 상대적인 해석된 모듈 경로 또는 다른 모듈 경로 인덱스를 인코딩해요.
경로와 기본 모듈 경로 인덱스 양쪽에 #f를 사용하는 모듈 경로 인덱스는 "자기 자신(self)"을 나타내요. 즉 모듈 경로 인덱스의 원천이 된 모듈 선언을 말하며, 그런 모듈 경로 인덱스는 컴파일 타임에 모듈 경로 인덱스 체인의 루트로 쓸 수 있어요. 예를 들어 모듈 안 식별자의 바인딩에 대한 정보를 추출할 때, 그 식별자가 같은 모듈 안 정의에 의해 바인딩되면 식별자의 소스 모듈은 "self" 모듈 경로 인덱스를 사용해 보고돼요. 대신 식별자가 (리터럴 모듈 이름이 아닌) 모듈 경로를 통해 import된 모듈에 정의되어 있으면, 식별자의 소스 모듈은 요구된 모듈 경로와 "self" 모듈 경로 인덱스를 담은 모듈 경로 인덱스를 사용해 보고돼요. "self" 모듈 경로 인덱스는 그것이 가리키는 모듈이 서브모듈이면 서브모듈 경로를 가져요.
모듈 경로 인덱스는 상태를 가져요. 해석된 모듈 경로로 해석되면, 그 해석된 모듈 경로가 모듈 경로 인덱스에 저장돼요. 특히 모듈이 로드될 때, 그 루트 모듈 경로 인덱스가 모듈의 선언 시점 이름과 일치하도록 해석돼요. 하지만 이 해석된 경로는, 모듈이 다른 모듈의 컴파일·마셜된 형태에 기여하는 식별자들에서는 잊혀져요. 해석된 이름이 일시적이라는 성질 덕분에, 모듈 코드를 컴파일된 때의 이름과 다른 해석된 이름으로 로드할 수 있어요.
두 모듈 경로 인덱스 값은 equal?한 path와 base 값을 가지면 equal?해요(서로 다른 해석된 값을 가져도).
procedure
(module-path-index? v) → boolean?
v : any/c
v가 모듈 경로 인덱스면 #t를, 그렇지 않으면 #f를 돌려줘요.
procedure
(module-path-index-resolve mpi
[load?
src-stx]) → resolved-module-path?
mpi : module-path-index?
load? : any/c = #f
src-stx : (or/c syntax? #f) = #f
해석된 모듈 이름에 대한 해석된 모듈 경로를 돌려주고, 아직 계산된 적이 없으면 해석된 이름을 계산(및 mpi에 저장)해요.
모듈 경로 인덱스를 해석하는 것은 현재 모듈 이름 해석기(current-module-name-resolver 참고)를 사용해요. mpi가 캡슐화한 모듈 경로의 종류에 따라, 계산된 해석된 이름은 current-load-relative-directory나 current-directory의 값에 의존할 수 있어요. load? 인자는 모듈 이름 해석기의 마지막 인자로 전파되고, src-stx 인자는 그 이전 인자로 전파돼요.
모듈 레지스트리를 공유하는 네임스페이스에서의 동시 해석은 모듈을 로드할 때 경쟁 조건을 만들 수 있다는 점에 주의하세요. namespace-call-with-registry-lock도 참고하세요.
mpi가, 확장기가 이미 해석된 것으로 만들지 않은 "self"(위 참고) 모듈 경로를 나타내면, module-path-index-resolve는 모듈 이름 해석기를 호출하지 않고 exn:fail:contract을 발생시켜요.
resolve-module-path-index도 참고하세요.
패키지 base의 버전 6.90.0.16에서 변경됨: load? 선택 인자를 추가했어요.
버전 8.2에서 변경됨: src-stx 선택 인자를 추가했어요.
procedure
(module-path-index-split mpi)
→
(or/c module-path? #f)
(or/c module-path-index? resolved-module-path? #f)
mpi : module-path-index?
두 값을 돌려줘요: 모듈 경로와, 첫 경로가 상대적인 기본 경로(모듈 경로 인덱스, 해석된 모듈 경로, 또는 #f).
두 번째 결과가 #f라는 것은 경로가 지정되지 않은 디렉터리에 상대적이라는 뜻이에요(즉 그 해석이 current-load-relative-directory 및/또는 current-directory의 값에 의존함).
첫 결과가 #f라는 것은 두 번째 결과도 #f라는 뜻이며, mpi가 "self"(위 참고)를 나타낸다는 의미예요. 그런 모듈 경로 인덱스는 module-path-index-submodule이 보고하는 #f가 아닌 서브모듈 경로를 가질 수 있어요.
procedure
(module-path-index-submodule mpi)
→ (or/c #f (non-empty-listof symbol?))
mpi : module-path-index?
mpi가 서브모듈을 참조하는 "self"(위 참고) 모듈 경로 인덱스면 심볼의 비어 있지 않은 리스트를 돌려줘요. (module-path-index-split mpi)의 결과 중 하나라도 #f가 아니면 결과는 항상 #f예요.
procedure
(module-path-index-join path base [submod]) → module-path-index?
path : (or/c module-path? #f)
base : (or/c module-path-index? resolved-module-path? #f)
submod : (or/c #f (non-empty-listof symbol?)) = #f
path, base, submod를 결합해 새 모듈 경로 인덱스를 만들어요. path 인자는 base도 #f일 때만 #f일 수 있어요. submod 인자는 path와 base가 둘 다 #f일 때만 리스트일 수 있어요.
procedure
(compiled-module-expression? v) → boolean?
v : any/c
v가 컴파일된 모듈 선언이면 #t를, 그렇지 않으면 #f를 돌려줘요. current-compile도 참고하세요.
procedure
(module-compiled-name compiled-module-code)
→ (or/c symbol? (cons/c symbol? (non-empty-listof symbol?)))
compiled-module-code : compiled-module-expression?
(module-compiled-name compiled-module-code
name)
→ compiled-module-expression?
compiled-module-code : compiled-module-expression?
name : (or/c symbol? (cons/c symbol? (non-empty-listof symbol?)))
컴파일된 형태의 모듈 선언을 받아 모듈의 선언된 이름을 얻거나(name이 제공되지 않을 때), 주어진 이름을 가진 수정된 모듈 선언을 돌려줘요.
name은 최상위 모듈에 대해서는 심볼이고, 최상위 모듈의 선언된 이름으로 시작하는 서브모듈 경로를 반영한 심볼 리스트와 짝지어진 심볼이에요.
procedure
(module-compiled-submodules compiled-module-code
non-star?)
→ (listof compiled-module-expression?)
compiled-module-code : compiled-module-expression?
non-star? : any/c
(module-compiled-submodules compiled-module-code
non-star?
submodules)
→ compiled-module-expression?
compiled-module-code : compiled-module-expression?
non-star? : any/c
submodules : (listof compiled-module-expression?)
컴파일된 형태의 모듈 선언을 받아 모듈의 서브모듈을 얻거나(submodules가 제공되지 않을 때), 주어진 서브모듈을 가진 수정된 모듈 선언을 돌려줘요. non-star? 인자는 결과 또는 새 서브모듈 리스트가 (non-star?가 참일 때) module 선언에 대응하는지, 아니면 (non-star?가 #f일 때) module* 선언에 대응하는지를 결정해요.
procedure
(module-compiled-imports compiled-module-code)
→
(listof (cons/c (or/c exact-integer? #f)
(listof module-path-index?)))
compiled-module-code : compiled-module-expression?
컴파일된 형태의 모듈 선언을 받아, 위상 레벨 이동(위상 #f는 label 위상 레벨로의 이동에 대응)을 모듈의 명시적 import에 대한 모듈 참조에 매핑하는 연관 리스트를 돌려줘요.
procedure
(module-compiled-exports compiled-module-code
[verbosity])
→
(listof (cons/c phase+space? list?))
(listof (cons/c phase+space? list?))
compiled-module-code : compiled-module-expression?
verbosity : (or/c #f 'defined-names) = #f
위상 레벨과 바인딩 공간의 조합을 대응하는 위상·공간의 export에 매핑하는 두 연관 리스트를 돌려줘요. 첫 번째 연관 리스트는 export된 변수용이고, 두 번째는 export된 구문용이에요. 다만 rename 트랜스포머를 통해 재export된 값 바인딩은 값 리스트가 아니라 구문 리스트에 있다는 점에 주의하세요. 위상·공간 표현에 대한 정보는 phase+space?를 참고하세요.
각 연관 리스트는 위 결과 계약에서 list?로 표현되는데, 더 정확히는 다음 계약과 일치해요:
(listof (list/c symbol?
(listof
(or/c module-path-index?
(list/c module-path-index?
phase+space?
symbol?
phase+space?)))
; only if verbosity is 'defined-names:
symbol?))
리스트의 각 요소에서 앞에 오는 심볼은 export의 이름이에요.
두 번째 부분(모듈 경로 인덱스 값들의 리스트 등)은 export된 식별자의 기원을 기술해요. 기원 리스트가 null이면 export된 식별자는 모듈 안에서 정의된 거예요. export된 식별자가 대신 재export된 것이면, 기원 리스트는 재export된 import에 대한 정보를 제공해요. 기원 리스트는 바인딩이 (아마도) 다른 소스에서 여러 번 import됐다면 요소가 하나보다 많아요.
마지막 부분인 심볼은 verbosity가 'defined-names일 때만 포함돼요. 그 경우 포함된 심볼은 정의하는 모듈 안에서의 정의 이름(export된 이름과 다를 수 있음)이에요.
각 기원에 대해, 모듈 경로 인덱스 자체는 바인딩이 위상 레벨 이동 0(for-meta, for-syntax 등의 없는 평범한 require)으로 기본 바인딩 공간(for-space 없이)으로 import되었고, import된 식별자가 재export된 이름과 같은 이름을 가진다는 뜻이에요. 리스트로 표현된 기원은 import, import된 식별자가 바인딩된 위상 레벨+바인딩 공간(표현에 대한 자세한 정보는 phase+space? 참고), import 모듈에 바인딩된 대로의 import의 상징적 이름, 그리고 export 모듈로부터의 식별자의 위상 레벨+바인딩 공간을 명시적으로 가리켜요.
예시를 볼게요:
> (module-compiled-exports
(compile
'(module banana racket/base
(require (only-in racket/math pi)
(for-syntax racket/base))
(provide pi
(rename-out [peel wrapper])
bush
cond
(for-syntax compile-time))
(define peel pi)
(define bush (* 2 pi))
(begin-for-syntax
(define compile-time (current-seconds)))))
'defined-names)
'((0
(bush () bush)
(pi (#<module-path-index:racket/math>) pi)
(wrapper () peel))
(1 (compile-time () compile-time)))
'((0 (cond (#<module-path-index:racket/base>) cond)))
패키지 base의 버전 7.5.0.6에서 변경됨: verbosity 인자를 추가했어요.
버전 8.2.0.3에서 변경됨: 결과를 위상-공간 조합으로 일반화했어요.
procedure
(module-compiled-indirect-exports compiled-module-code)
→ (listof (cons/c exact-integer? (listof symbol?)))
compiled-module-code : compiled-module-expression?
위상 레벨 값을 모듈 안의 변수를 나타내는 심볼에 매핑하는 연관 리스트를 돌려줘요. 이 정의들은 소스에서 직접 접근할 수 없지만 바이트코드에서는 접근할 수 있고, 각 리스트의 심볼 순서는 바이트코드 접근 순서에 대응해요.
패키지 base의 버전 6.5.0.5에서 추가됨.
procedure
(module-compiled-language-info compiled-module-code)
→ (or/c #f (vector/c module-path? symbol? any/c))
compiled-module-code : compiled-module-expression?
The Racket Guide의 Module-Handling Configuration도 참고하세요.
모듈 선언의 구문에 'module-language 구문 프로퍼티를 통해 원래 붙어 있던 모듈 구현의 "언어"를 반영하기 위한 정보를 돌려줘요. module도 참고하세요.
모듈에 대한 정보가 없으면 결과는 #f예요. 그렇지 않으면 결과는 (vector mp name val)인데, ((dynamic-require mp name) val)이 두 인자를 받는 함수를 돌려줘야 해요. 함수의 인자는 반영된 정보의 키와 기본값이에요. 허용되는 키와 결과의 해석은 DrRacket 같은 외부 도구에게 달려 있어요. 주어진 키에 대한 정보가 없으면 결과는 주어진 기본값이어야 해요.
module->language-info와 racket/language-info도 참고하세요.
procedure
(module-compiled-cross-phase-persistent? compiled-module-code)
→ boolean?
compiled-module-code : compiled-module-expression?
compiled-module-code가 교차 위상 영속(cross-phase persistent) 모듈을 나타내면 #t를, 그렇지 않으면 #f를 돌려줘요.
procedure
(module-compiled-realm compiled-module-code) → symbol?
compiled-module-code : compiled-module-expression?
compiled-module-code가 나타내는 모듈의 영역(realm)을 돌려줘요.
패키지 base의 버전 8.4.0.2에서 추가됨.
14.4.3 동적 모듈 접근 (Dynamic Module Access)
procedure
(dynamic-require mod
provided
[fail-thunk
syntax-thunk]) → (or/c void? any/c)
mod :
(or/c module-path?
resolved-module-path?
module-path-index?)
provided : (or/c symbol? #f 0 void?)
fail-thunk : (or/c 'error (-> any)) = 'error
syntax-thunk : (or/c 'eval (-> any)) = 'eval
dynamic-require는 프로시저이므로, require 표현식에서 하듯 mod에 평범한 S-표현식을 주는 것은 아마 기대한 결과를 주지 못할 거예요. 대신 필요한 것은 S-표현식으로 평가되는 무언가예요. quote를 쓰는 것이 한 방법이에요.
아직 인스턴스화되지 않았다면, 현재 네임스페이스의 레지스트리에서 네임스페이스의 기본 위상으로 mod가 지정한 모듈을 동적으로 인스턴스화해요. 현재 모듈 이름 해석기가 mod를 해석하기 위해 모듈 선언을 로드할 수 있어요(current-module-name-resolver 참고). 경로는 current-load-relative-directory 및/또는 current-directory에 상대적으로 해석돼요. 모듈 레지스트리를 공유하는 네임스페이스에서의 동시 dynamic-require는 경쟁 조건을 만들 수 있다는 점에 주의하세요. namespace-call-with-registry-lock도 참고하세요.
provided가 #f면 결과는 #<void>이고, 모듈은 방문되지 않으며(Module Expansion, Phases, and Visits 참고) 기본 위상보다 높은 위상에서 (온디맨드 방문을 위해) 이용 가능하게조차 되지 않아요.
예시를 볼게요:
> (module a racket/base (displayln "hello"))
> (dynamic-require ''a #f)
hello
이중 인용 ''a는 루트 모듈 경로 'a로 평가돼요(require의 문법 참고). mod에 'a를 쓰면 안 돼요. 그것은 root-module-path a로 평가되고, 이 예시는 컬렉션에 설치된 모듈이 아니기 때문이에요. a를 쓰면 안 돼요. a는 정의되지 않은 변수이기 때문이에요.
읽기-평가-프린트 루프가 아닌 다른 모듈 안에서 (module a ....)를 선언하면 서브모듈이 만들어져요. 그 경우 (dynamic-require ''a #f)는 모듈에 접근하지 못해요. ''a가 서브모듈을 가리키지 않기 때문이에요.
provided가 심볼이면, 주어진 이름의 모듈 export 값이 돌려지고, 여전히 모듈은 더 높은 위상에서 방문되거나 이용 가능하게 만들어지지 않아요.
예시를 볼게요:
> (module b racket/base
(provide dessert)
(define dessert "gulab jamun"))
> (dynamic-require ''b 'dessert)
"gulab jamun"
모듈이 provided를 구문으로 export하면, syntax-thunk가 프로시저일 때 호출되고, 그 결과가 dynamic-require 호출의 결과예요. syntax-thunk가 'eval이면, 바인딩의 사용이 fresh 네임스페이스(모듈이 붙은)에서 확장·평가돼요. 이는 모듈이 fresh 네임스페이스에서 방문된다는 뜻이에요. 확장된 구문은 단일 값을 돌려줘야 해요.
예시를 볼게요:
> (module c racket/base
(require (for-syntax racket/base))
(provide dessert2)
(define dessert "nanaimo bar")
(define-syntax dessert2
(make-rename-transformer #'dessert)))
> (dynamic-require ''c 'dessert2)
"nanaimo bar"
모듈에 그런 export된 변수나 구문이 없으면 fail-thunk가 호출되거나, fail-thunk가 'error면 exn:fail:contract 예외가 발생해요. provided가 이름 짓는 변수가 보호되어 export되면(Code Inspectors 참고), exn:fail:contract 예외가 발생해요.
provided가 0이면, 모듈은 provided가 #f일 때처럼 인스턴스화되지만 방문되지는 않아요. 다만 0에서는 모듈이 더 높은 위상에서 이용 가능하게 돼요.
provided가 #<void>면, 모듈은 방문되지만 인스턴스화되지는 않아요(Module Expansion, Phases, and Visits 참고), 그리고 결과는 #<void>예요.
다른 module-path 문법 표현을 사용한 더 많은 예시는 아래에 있어요:
예시를 볼게요:
> (dynamic-require 'racket/base #f)
예시를 볼게요:
> (dynamic-require (list 'lib "racket/base") #f)
예시를 볼게요:
> (module a racket/base
(module b racket/base
(provide inner-dessert)
(define inner-dessert "tiramisu")))
> (dynamic-require '(submod 'a b) 'inner-dessert)
"tiramisu"
위 예시의 마지막 줄은 대신 이렇게 쓸 수도 있어요:
예시를 볼게요:
> (dynamic-require ((lambda () (list 'submod ''a 'b))) 'inner-dessert)
"tiramisu"
이것은 동등해요.
패키지 base의 버전 8.16.0.3에서 변경됨: syntax-thunk 인자를 추가하고 fail-thunk에 'error를 허용하도록 변경했어요.
procedure
(dynamic-require-for-syntax mod
provided
[fail-thunk
syntax-thunk]) → any
mod : module-path?
provided : (or/c symbol? #f)
fail-thunk : (or/c 'error (-> any)) = 'error
syntax-thunk : (or/c 'eval (-> any)) = 'eval
dynamic-require와 같지만, 네임스페이스의 기본 위상보다 1 높은 위상에서 실행돼요.
패키지 base의 버전 8.16.0.3에서 변경됨: syntax-thunk 인자를 추가하고 fail-thunk에 'error를 허용하도록 변경했어요.
procedure
(module-declared? mod [load?]) → boolean?
mod :
(or/c module-path? module-path-index?
resolved-module-path?)
load? : any/c = #f
mod가 나타내는 모듈이 현재 네임스페이스에 선언되어(반드시 인스턴스화되거나 방문될 필요는 없음) 있으면 #t를, 그렇지 않으면 #f를 돌려줘요.
load?가 #t이고 mod가 해석된 모듈 경로가 아니면, mod를 해석하는 과정에서 모듈이 로드돼요(dynamic-require 및 다른 함수처럼). 서브모듈의 선언을 검사하는 것은, 서브모듈이 존재하지 않아 로드될 수 없을 때(존재하는 루트 모듈 안에서든 루트 모듈이 존재하지 않아서든) 예외를 촉발하지 않아요.
procedure
(module->language-info mod [load?])
→ (or/c #f (vector/c module-path? symbol? any/c))
mod :
(or/c module-path? module-path-index?
resolved-module-path?)
load? : any/c = #f
mod의 구현의 "언어"를 반영하기 위한 정보를 돌려줘요. mod가 해석된 모듈 경로이거나 load?가 #f면, mod가 이름 짓는 모듈은 현재 네임스페이스에 선언되어(반드시 인스턴스화되거나 방문될 필요는 없음) 있어야 해요. 그렇지 않으면 mod는(dynamic-require 및 다른 함수처럼) 로드될 수 있어요. module->language-info가 돌려주는 정보는 module-compiled-language-info를 컴파일된 코드로서의 모듈 구현에 적용했을 때 돌려받았을 것과 같아요.
모듈은 dynamic-require를 사용해 선언될 수 있어요.
예시를 볼게요:
> (dynamic-require 'racket/dict (void))
> (module->language-info 'racket/dict)
#f
procedure
(module->imports mod)
→
(listof (cons/c (or/c exact-integer? #f)
(listof module-path-index?)))
mod :
(or/c module-path? module-path-index?
resolved-module-path?)
module-compiled-imports와 같지만, mod의 import를 만들어 내요. mod는 현재 네임스페이스에 선언되어(반드시 인스턴스화되거나 방문될 필요는 없음) 있어야 해요. 기존 모듈을 선언하는 예시는 module->language-info를 참고하세요.
예시를 볼게요:
> (module banana racket/base
(require (only-in racket/math pi))
(provide peel)
(define peel pi)
(define bush (* 2 pi)))
> (module->imports ''banana)
'((0 #<module-path-index:racket/base> #<module-path-index:racket/math>))
procedure
(module->exports mod [verbosity])
→
(listof (cons/c phase+space? list?))
(listof (cons/c phase+space? list?))
mod :
(or/c module-path? module-path-index?
resolved-module-path?)
verbosity : (or/c #f 'defined-names) = #f
module-compiled-exports와 같지만, mod의 export를 만들어 내요. mod는 현재 네임스페이스에 선언되어(반드시 인스턴스화되거나 방문될 필요는 없음) 있어야 해요. 기존 모듈을 선언하는 예시는 module->language-info를 참고하세요.
예시를 볼게요:
> (module banana racket/base
(require (only-in racket/math pi))
(provide (rename-out [peel wrapper]))
(define peel pi)
(define bush (* 2 pi)))
> (module->exports ''banana)
'((0 (wrapper ())))
'()
패키지 base의 버전 7.5.0.6에서 변경됨: verbosity 인자를 추가했어요.
버전 8.2.0.3에서 변경됨: 결과를 위상-공간 조합으로 일반화했어요.
procedure
(module->indirect-exports mod)
→ (listof (cons/c exact-integer? (listof symbol?)))
mod :
(or/c module-path? module-path-index?
resolved-module-path?)
module-compiled-indirect-exports와 같지만, mod의 간접 export를 만들어 내요. mod는 현재 네임스페이스에 선언되어(반드시 인스턴스화되거나 방문될 필요는 없음) 있어야 해요. 기존 모듈을 선언하는 예시는 module->language-info를 참고하세요.
예시를 볼게요:
> (module banana racket/base
(require (only-in racket/math pi))
(provide peel)
(define peel pi)
(define bush (* 2 pi)))
> (module->indirect-exports ''banana)
'((0 bush))
패키지 base의 버전 6.5.0.5에서 추가됨.
procedure
(module->realm mod) → symbol?
mod :
(or/c module-path? module-path-index?
resolved-module-path?)
module-compiled-realm과 같지만, mod의 영역을 만들어 내요. mod는 현재 네임스페이스에 선언되어(반드시 인스턴스화되거나 방문될 필요는 없음) 있어야 해요.
패키지 base의 버전 8.4.0.2에서 추가됨.
procedure
(module-predefined? mod) → boolean?
mod :
(or/c module-path? module-path-index?
resolved-module-path?)
mod가 실행 중인 Racket 인스턴스에 사전 정의된 모듈을 가리키는지 보고해요. 사전 정의된 모듈은 항상 상징적 해석된 모듈 경로를 가지며, 항상 사전 정의될 수도 있고 특정 실행 파일(raco exe나 create-embedding-executable이 만든 것 같은) 안에서 특히 사전 정의될 수도 있어요.
14.4.4 모듈 캐시 (Module Cache)
확장기는 이전에 선언된 모듈을 로드할 때 시간을 아끼기 위해 place-로컬 모듈 캐시를 유지해요.
procedure
(module-cache-clear!) → void?
place-로컬 모듈 캐시를 비워요.
패키지 base의 버전 8.4.0.5에서 추가됨.