generic type inference implementaion for go-like grammar language
타입 환경 Γ는 식별자와 타입의 매핑을 나타냅니다:
예:
타입 추론은 주어진 표현식(Expr)과 타입 환경(Env)을 기반으로 해당 표현식의 타입(Type)을 결정하는 과정입니다. 타입 환경(
타입 추론 과정은 다음과 같습니다:
- 리터럴 (Literal):
- 리터럴 값(e.g., 숫자, 문자열 등)의 타입은 해당 리터럴의 타입(
$\tau$ )입니다. - 예:
InferType(42, Γ) = int
- 식별자 (Identifier):
- 식별자의 타입은 타입 환경(
$\Gamma$ )에서 식별자에 매핑된 타입입니다. - 예:
InferType(x, Γ[x ↦ int]) = int
- 함수 적용 (Function Application):
- 표현식
$e_1$ 과$e_2$ 의 타입을 추론하여$e_1$ 의 타입이 함수 타입($\tau_1 \rightarrow \tau_2$ )이고$e_2$ 의 타입이$\tau_1$ 일 때, 전체 표현식$e = e_1\ e_2$ 의 타입은$\tau_2$ 입니다. - 예:
InferType(f(x), Γ[f ↦ (int → string), x ↦ int]) = string
- 함수 정의 (Function Definition):
- 람다 표현식(
$\lambda x:\tau_1. e'$ )의 타입을 추론할 때, 인자$x$ 의 타입을$\tau_1$ 로 가정하고 본문$e'$ 의 타입을 추론합니다. 이 결과$\tau_2$ 가 나오면 전체 표현식의 타입은$\tau_1 \rightarrow \tau_2$ 입니다. - 예:
InferType(\lambda x:int. x + 1, Γ) = int → int
- 제네릭 함수 (Generic Function):
- 제네릭 함수 표현식(
$\Lambda \alpha. e'$ )의 타입을 추론할 때, 타입 변수$\alpha$ 를 새로운 타입 변수로 가정하고 본문$e'$ 의 타입을 추론합니다. 이 결과$\tau$ 가 나오면 전체 표현식의 타입은$\forall \alpha. \tau$ 입니다. - 예:
InferType(Λα. λx:α. x, Γ) = ∀α. α → α
- 슬라이스 (Slice):
- 슬라이스 표현식(e.g., 배열)의 모든 요소
$e_i$ 가 동일한 타입$\tau$ 를 가질 때, 슬라이스의 타입은$\text{SliceType}(\tau)$ 입니다. - 예:
InferType([1, 2, 3], Γ) = SliceType(int)
- 맵 (Map):
- 맵 표현식의 모든 키
$k_i$ 가 타입$\tau_1$ 를 가지고, 모든 값$v_i$ 가 타입$\tau_2$ 를 가질 때, 맵의 타입은$\text{MapType}(\tau_1, \tau_2)$ 입니다. - 예:
InferType(map[string]int{"a": 1, "b": 2}, Γ) = MapType(string, int)
자유 타입 변수 (Free Type Variables)
자유 타입 변수(FTV)는 어떤 타입 표현식 내에서 바인딩되지 않은 타입 변수를 의미합니다. 예를 들어, 타입
타입 통합 (Type Unification)
타입 통합은 두 타입을 비교하여 이들이 동일한지, 혹은 하나의 타입 변수가 다른 타입으로 대체될 수 있는지를 확인하는 과정입니다. 타입 통합 과정에서 타입 환경(
타입 통합은 다음과 같이 정의됩니다:
여기서 FTV
는 자유 타입 변수(Free Type Variables)의 집합을 나타냅니다.
예시:
-
동일 타입
Unify(int, int, Γ) = Γ
- 동일한 두 타입을 비교하면 타입 환경은 변경되지 않습니다.
-
타입 변수 바인딩
Unify(α, int, Γ) = Γ[α ↦ int]
- 타입 변수가 다른 타입으로 바인딩될 수 있으면, 타입 환경을 갱신하여 해당 변수를 새로운 타입으로 매핑합니다.
-
함수 타입
Unify((int → α), (int → string), Γ) = Γ[α ↦ string]
- 함수 타입을 비교할 때, 각 인자와 반환값의 타입을 비교합니다.
-
슬라이스 타입
Unify(SliceType(α), SliceType(int), Γ) = Γ[α ↦ int]
- 슬라이스 타입의 경우, 요소 타입을 비교하여 통합합니다.
-
맵 타입
Unify(MapType(string, α), MapType(string, int), Γ) = Γ[α ↦ int]
- 맵 타입의 경우, 키와 값의 타입을 개별적으로 통합합니다.
복잡한 예:
Unify((α → β) → γ, (int → string) → bool, Γ)
= Unify(α → β, int → string, Unify(γ, bool, Γ))
= Unify(α, int, Unify(β, string, Γ[γ ↦ bool]))
= Γ[α ↦ int, β ↦ string, γ ↦ bool]
제약 조건 검사는 특정 타입이 주어진 제약 조건을 만족하는지 확인하는 과정입니다. 제약 조건은 인터페이스 구현 여부와 특정 타입 집합에 속하는지를 포함할 수 있습니다. 제약 조건 검사는 타입 시스템에서 타입 안전성을 유지하고, 인터페이스를 통한 다형성을 보장하는 데 중요합니다.
제약 조건은 다음과 같이 정의됩니다.
이 정의는 다음과 같은 세 가지 경우를 다룹니다:
-
인터페이스 제약 조건:
$C.\text{Types}$ 가 비어 있는 경우, 타입$\tau$ 는$C.\text{Interfaces}$ 의 모든 인터페이스를 구현해야 합니다. -
타입 제약 조건:
$C.\text{Interfaces}$ 가 비어 있는 경우, 타입$\tau$ 는$C.\text{Types}$ 에 포함된 타입 중 하나와 일치해야 합니다. -
복합 제약 조건: 두 제약 조건이 모두 있는 경우, 타입
$\tau$ 는$C.\text{Interfaces}$ 의 모든 인터페이스를 구현하고, 동시에$C.\text{Types}$ 에 포함된 타입 중 하나와 일치해야 합니다.
implements
함수는 다음과 같이 정의됩니다:
implements
함수는 타입
즉, 인터페이스
예시:
- 인터페이스 제약 조건:
checkConstraint(StringerType, {Interfaces: [Stringer], Types: []}) = true
- 여기서 StringerType은
String() string
메서드를 가지고 있어야 합니다.
- 타입 집합 제약 조건:
checkConstraint(int, {Interfaces: [], Types: [int, float64]}) = true
checkConstraint(string, {Interfaces: [], Types: [int, float64]}) = false
- 첫 번째 예시에서
int
는 주어진 타입 집합에 속하므로 true를 반환합니다. - 두 번째 예시에서
string
은 주어진 타입 집합에 속하지 않으므로 false를 반환합니다.
- 복합 제약 조건:
checkConstraint(StringerInt, {Interfaces: [Stringer], Types: [int, int64]}) = true
- 여기서 StringerInt는
String() string
메서드를 가지고 있으며, int 또는 int64 타입이어야 합니다.
- 다중 인터페이스 제약 조건:
checkConstraint(ReadWriterCloser, {Interfaces: [Reader, Writer, Closer], Types: []}) = true
- 여기서 ReadWriterCloser는 Read, Write, Close 메서드를 모두 구현해야 합니다.