rokdocs
v0.1

2. 타입 시스템 개요

이 장에서는 rok 타입 시스템이 책임지는 범위와 표준 라이브러리에 위임하는 범위를 정합니다. 구체적인 타입 종류와 문법은 후속 문서에서 정의합니다.

목차

위치 정의 (positional definition)

타입은 값에 어떤 모양과 동작이 허용되는지를 컴파일러가 결정하는 정보입니다. rok 의 모든 값은 어떤 타입에 속하며, 타입이 없는 값은 존재하지 않습니다.

다만 변수의 경우에는 사정이 조금 다릅니다. 변수는 한 가지 타입에 고정되거나 (정적), 여러 타입을 담을 수 있는 슬롯이거나 (동적) 둘 중 하나이며, 두 모드는 같은 언어 안에 공존합니다.

두 모드: 정적과 점진

정적 (명시)         let x: i32 = 42i32;     x 는 항상 i32
정적 (추론)         let z = 42i32;          z 는 초기값의 타입 i32 로 고정
점진 (any)          let y: any = 42i32;     y 는 모든 타입을 담을 수 있는 슬롯

정적 변수는 선언 시 타입을 명시하거나, 타입을 생략해 초기값에서 추론한 경우에 해당하며, 그 변수는 항상 그 타입의 값만 포함합니다. 반면 점진 변수는 타입 자리에 any 를 명시한 경우로, 이때 변수는 동적 슬롯이 됩니다. 정적이 기본이고, 동적은 any 라고 적힌 곳에만 있습니다.

이 장에서는 두 모드가 같은 언어 안에 있다는 사실까지만 짚어두기로 합니다.

이러한 결정의 동기는 실행 엔진과 사용자 표면 에 있습니다. REPL/Jupyter 탐색과 AOT 정적 컴파일이 같은 코드를 공유해야 하기 때문입니다. 그 결과 탐색용 코드는 타입을 생략한 추론 스타일로, 라이브러리 코드는 타입을 명시한 스타일로 자연스럽게 섞여 쓰이며, 동적 슬롯은 정말 필요한 곳에만 any 로 적어 둡니다.

언어가 정의하는 것

타입에 관해 언어 사양이 직접 정의하는 범위는 다음과 같습니다.

먼저 타입 표현식의 문법, 즉 타입 이름을 적는 방식을 정의합니다. 그리고 타입을 만드는 컨테이너 문법, 곧 struct, enum, union, trait, impl, 제네릭, 합 타입(|) 을 다룹니다. 또한 동적 슬롯 (any) 의 의미를 규정하며, 제네릭의 단형화 규칙으로서 호출 지점에서 타입 별로 specialize 하는 방식을 명시합니다. 마지막으로 컴파일러가 인식해야 하는 타입 시스템 불변, 가령 트레잇-as-타입 금지나 niche 최적화 규칙 등을 정의합니다.

표준 라이브러리가 정의하는 것

타입에 관해 표준 라이브러리가 책임지는 범위는 다음과 같습니다.

먼저 모든 원시 타입의 이름과 의미, 즉 i32, f64, bool, str 등이 여기에 해당합니다. 그리고 List, Dict, Set, Tuple 등 모든 컬렉션 타입이 표준 라이브러리의 영역입니다. 또한 타입 간 cast, from, into 같은 모든 변환을 다루며, 트레잇의 내용 - 어떤 트레잇이 어떤 메서드를 가지고 어떤 타입에 구현되는가 - 역시 표준 라이브러리에서 결정됩니다. 끝으로 리터럴이 어떤 타입으로 평가되는가 또한 핸들러를 통해 표준 라이브러리에서 정해집니다.

중요

본 2장은 모양만 정의합니다. 내용은 라이브러리 사양에서 정의합니다.

점진 타이핑의 경계

점진 타이핑은 any 를 명시한 자리에만 적용됩니다. 타입 명시를 생략한 자리는 어디에서도 동적 슬롯이 되지 않습니다.

변수에 타입을 적지 않은 경우, 초기값의 타입으로 추론된 정적 슬롯이 됩니다. 함수 매개변수에 타입을 적지 않은 경우, 이는 제네릭 함수로 해석되어 호출 지점에서 단형화됩니다. 동적 슬롯이 되지는 않으므로, 매개변수가 점진 동작을 하도록 하려면 명시적으로 any 를 적어야 합니다. 구조체 필드의 경우에는 타입 명시가 필수입니다. 한편 함수 반환 타입을 적지 않은 경우에는 body 에서 추론하며, 추론할 수 없으면 컴파일 에러가 발생합니다.

이러한 경계를 두는 까닭은 실행 시 dispatch 비용이 생기는 곳을 any 라고 적힌 자리로 한정하기 위함입니다. 변수, 매개변수, 필드 모두 정적이거나 명시적 any 입니다.

트레잇-as-타입 금지

트레잇 이름을 그대로 변수, 매개변수, 반환 타입으로 적을 수는 없습니다. 다형성이 필요한 경우에는 제네릭, 합 타입, 또는 제약 붙은 any Trait 을 사용합니다.

런타임 dispatch 는 any 라고 적힌 자리 (any, any Trait) 로만 한정됩니다.

단형화 (monomorphization)

제네릭 함수와 타입은 호출이나 인스턴스화 지점의 구체 타입 튜플마다 별도 컴파일 결과를 생성합니다.

예컨대 같은 제네릭 함수가 i32 와 f64 두 호출지에서 쓰이면, 두 개의 고유한 함수 본체가 생성됩니다. 이 방식의 결과로 간접 호출 비용은 0 이 되어 직접 호출과 동일한 성능을 얻습니다. 비용은 컴파일 시간과 바이너리 크기로 나타나지만, MLIR + LLVM 의 dead-code-elimination 으로 대부분 상쇄됩니다.

GitHub에서 이 페이지 고치기