단일 파일에 명확한 책임을 부여하고 다른 파일에서 해당 기능을 사용해야 할 때 모듈을 활용하세요. .tpz 파일 하나가 독립된 모듈 하나를 구성합니다. 별도의 module 선언은 존재하지 않습니다.
파일 두 개로 프로그램 하나 만들기
학습 계획 애플리케이션의 소스 코드 구조는 다음과 같습니다.
study-plan/
├── topaz.toml
├── topaz.lock
└── src/
├── main.tpz
└── plan.tpzsrc/plan.tpz는 데이터 모델과 재사용 가능한 연산을 담당합니다.
export record 학습과제 {
제목: string,
분: int,
완료: bool = false,
}
function 분해석(텍스트: string) -> Result<int, string> {
match toInt(텍스트) {
case Some(값) if 값 > 0 => Ok(값)
case _ => Err("minutes must be a positive integer")
}
}
export function 과제만들기(
제목: string,
분문자열: string,
완료: bool = false,
) -> Result<학습과제, string> {
let 분 = 분해석(분문자열)?
Ok(학습과제 { 제목: 제목, 분: 분, 완료: 완료 })
}
export function 요약(과제들: Array<학습과제>) -> string {
let mut 남음 = 0
for 과제 in 과제들 {
if !과제.완료 {
남음 = 남음 + 과제.분
}
}
"tasks={과제들.length}, remaining={남음} minutes"
}src/main.tpz는 필요한 식별자 세 개만 선택하여 가져옵니다.
import src.plan { 학습과제, 과제만들기, 요약 }
export function main(인자: Array<string>, 표준입력: string) -> Result<int, string> {
let 과제들: Array<학습과제> = [
과제만들기("Run first program", "10", true)?,
과제만들기("Build application", "25", false)?,
]
print(요약(과제들))
Ok(0)
}패키지 루트를 기준으로 구성된 컴파일 단위를 검사하고 실행합니다.
topaz check --root . --locked
topaz run --root . --lockedtasks=2, remaining=25 minutes지정한 루트를 기준으로 src.plan은 src/plan.tpz 경로를 참조합니다. 컴파일 단위는 엔트리 포인트 모듈과 import 구문으로 도달 가능한 모든 모듈로 구성됩니다. 엔트리 포인트에서 도달할 수 없는 파일은 컴파일 단위에 포함되지 않으므로 검사 대상에서 제외됩니다.
다른 모듈에 필요한 경계만 공개하기
선언 앞에 export가 없는 식별자는 비공개 항목입니다. 학습과제, 과제만들기, 요약은 모듈 경계 외부로 노출되며, 분해석은 모듈 내부의 구현 상세로 유지됩니다. src/main.tpz에서 분해석을 선택하거나 접근하면 정적 오류가 발생합니다. 가져온 바인딩은 읽기 전용입니다.
선택적 import 구문은 공개된 이름을 직접 바인딩합니다.
| 목적 | 정본 구문 |
|---|---|
| 필요한 이름 일부 선택 | import src.plan { 학습과제, 과제만들기 } |
| 선택한 이름 별칭 지정 | import src.plan { 요약 as 요약그리기 } |
| 네임스페이스 바인딩 | import src.plan as 계획 |
네임스페이스 형식에서는 공개된 값을 계획.과제만들기(...) 형태로 사용합니다. 네임스페이스는 컴파일 시점에 이름을 해석하는 수단이며 런타임 값으로 존재하지 않습니다. 계획.학습과제와 같은 공개 타입은 타입 표기 위치에 사용할 수 있습니다. 단, 계획.학습과제 { ... }는 인스턴스를 생성하는 구문이 아닙니다. 명목형 레코드를 직접 생성하려면 학습과제를 선택하여 가져오세요.
흔한 실수 바로잡기. import src.plan as 계획 { 요약 }과 같이 두 가지 import 구문을 결합할 수는 없습니다. 네임스페이스 import와 선택적 import 중 하나를 선택하세요.
루트, 초기화, 패키지는 서로 다른 결정입니다
- 모듈 경로는 지정한 루트 하위의 경로를 점(.)으로 구분하여 가리키는 주소입니다. 파일 및 디렉터리 이름이 정확히 일치해야 합니다.
- 모든 import 구문은 최상단 import 헤더에 위치해야 합니다. 이 헤더는 다른 요소보다 먼저 작성해야 합니다.
- 의존 모듈은 자신을 가져오는 모듈보다 먼저 단 한 번 초기화됩니다. 가져오는 모듈에는 선언과 바인딩을 작성할 수 있으며, 최상위 루프나 대입문처럼 임의로 실행되는 문장은 작성할 수 없습니다.
topaz.toml은 툴체인에서 패키지 식별자, 엔트리 포인트, 의존성, 빌드 타겟을 설정하는 파일입니다. 별도의 모듈 선언 문법을 추가하지 않습니다.
위 예제와 같이 패키지 명령어 실행 시 엔트리 포인트와 루트를 지정할 수 있습니다. 소스 파일을 직접 지정하면 해당 진입 파일이 위치한 디렉터리가 기본 루트가 됩니다. --root 옵션으로 해당 파일을 포함하는 루트 경로를 직접 명시할 수도 있습니다.
순환 import는 거부됩니다
a.tpz가 b.tpz를 가져온다고 가정해 봅시다.
import b
export function 에이에서() -> int {
1
}그리고 b.tpz가 다시 a.tpz를 가져옵니다.
import a
export function 비에서() -> int {
2
}topaz check a.tpz --root . 명령을 실행하면 검사기가 순환 참조를 보고합니다.
error[TPZ3006]: import cycle: a -> b -> a
--> a.tpz:1:8
|
1 | import b
| ^
a.tpz: 1 diagnostic가져온 식별자를 타입으로만 사용하더라도 모든 import 관계가 순환 참조 검사 대상에 포함됩니다. 순환 참조를 해제하려면 공통 선언을 순환이 발생하지 않는 제3의 모듈로 이동하세요. 의존성 방향을 단방향으로 구성하는 방법도 있습니다. 타입 전용 순환 참조 예외는 없습니다.
정확한 경계
현재 모듈 문법은 use, 문자열·템플릿 경로, 별칭과 선택 목록의 결합, 공개 목록, 와일드카드 공개, 재공개, 공개 지점 이름 변경, 부수 효과만을 위한 import 구문을 제공하지 않습니다. export let mut 구문 역시 거부됩니다. import 경로는 루트 외부로 나갈 수 없으며, 네임스페이스 또한 비공개 멤버를 노출하지 않습니다.
전체 테스트를 거친 패키지 예제는 첫 애플리케이션 문서에서 볼 수 있습니다. 매니페스트와 잠금 파일은 패키지와 배포 문서에서 다룹니다. 토파즈가 의도적으로 지원하지 않는 모듈 표기법은 금지·유예 형식 문서에서 이어서 확인하세요.