diff --git a/src/SUMMARY.md b/src/SUMMARY.md index c404230..adf993f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -19,7 +19,7 @@ - [式の実装についての注意](./expressions/implementing_expressions.md) - [宣言](./declarations/declarations.md) - [帰納型の知られざる一生](./declarations/inductive.md) -- [Type Inference](./type_checking/type_inference.md) +- [型推論](./type_checking/type_inference.md) - [Definitional Equality](./type_checking/definitional_equality.md) - [Reduction](./type_checking/reduction.md) - [Open issues & future work](./future_work.md) diff --git a/src/type_checking/type_inference.md b/src/type_checking/type_inference.md index e5c05f3..d29f175 100644 --- a/src/type_checking/type_inference.md +++ b/src/type_checking/type_inference.md @@ -1,29 +1,73 @@ + +# 型推論 + + + +型推論は与えられた式の型を決定するための手続きであり、Leanのカーネルのコア機能の1つです。型推論は `Nat.zero` が `Nat` 型の要素であることや、`(fun (x : Char) => var(0))` が `Char -> Char` 型の要素であることを決定する方法です。 + +本節ではまず型推論のためのもっとも単純で完全な手続きを確かめ、次いでそれぞれの手続きより性能の高い、しかし少し複雑なバージョンについて確認します。 + + + +また、Lean のカーネルが型推論中に行ういくつかの付加的な正しさの主張についても見ていきます。 + +## 束縛変数 + + + +Lean の実装に従ってローカルに名前を持たないアプローチを使う場合、型推論中で束縛変数に遭遇することはないでしょう。というのもすべての開いた束縛子は適切な自由変数でインスタンス化されているからです。 + + +束縛子に遭遇した場合、本体の型を決定するために本体を走査する必要があります。束縛子の型に関する情報を保持するには主に2つのアプローチがあります;Lean が正式に採用しているものは、束縛子の型情報を保持する自由変数を作成し、インスタンス化を対応する束縛変数を自由変数に置き換えてから本体に入るというものです。これは型付けコンテキストのために個別の状態を追跡する必要がないため良い方法です。 + +クロージャに基づく実装では、一般的に開いた束縛子を追跡するための別の型付けコンテキストを持つことになります;束縛変数に遭遇すると、その変数の型を取得するために、型付けコンテキストにインデックスを作ることになります。 + + + +## 自由変数 + + +自由変数が作られる際には、それが表す束縛子から型情報が与えられるため、型推論の結果としてその型情報を受け取ればよいです。 ``` infer FVar id binder: binder.type ``` + + +## 関数適用 ``` infer App(f, arg): @@ -34,13 +78,29 @@ infer App(f, arg): | _ => error ``` + +ここで追加の主張として必要なものは、`arg` の型が `binder` の型と一致することです。例えば、次の式 + + + +`(fun (n : Nat) => 2 * n) 10` について、`defEq(Nat, infer(10))` を主張しなければなりません。 + + +既存の実装ではこのチェックをインラインで実行することを好んでいますが、他の場所で処理するためにこの同値の主張を保存することもできます。 + + +## ラムダ式 ``` infer Lambda(binder, body): @@ -64,18 +124,34 @@ inferSortOf e: | _ => error ``` + + +## ソート + + +任意の `Sort n` の型は `Sort (n+1)` です。 ``` infer Sort level: Sort (succ level) ``` + + +## 定数 + + +`const` 式は他の宣言を名前で参照するために使われ、参照される任意の他の宣言はこれより以前に宣言され、型がチェックされていなければなりません。したがって、参照される宣言の型が何であるかはすでに分かっているため、それを環境から探してくるだけで良いのです。ただし、現在の宣言の宇宙レベルを添字付けられた定義の宇宙パラメータに代入する必要があります。 ``` infer Const name levels: @@ -92,15 +168,35 @@ infer Let binder val body: infer (instantiate body val) ``` + + +## 射影 + + +ここで `Proj (projIdx := 0) (structure := Prod.mk A B (a : A) (b : B))` のようなものの型を推論しようとしているとしましょう。 + +まず渡される構造体の型を推測することから始めます;そこから構造体の名前を取得し、構造体とコンストラクタの型を環境で調べることができます。 + + + +コンストラクタの型のテレスコープを走査し、`Prod.mk` のパラメータをコンストラクタ型のテレスコープに代入します。`A -> B -> (a : A) -> (b : B) -> Prod A B` というコンストラクタ型に着目すると、A と B を代入することで、`(a : A) -> (b : B) -> Prod A B` というテレスコープが残ります。 + + +コンストラクタのテレスコープの残りの部分は構造体のフィールドを表しており、束縛子に型情報を持っているため、`telescope[projIdx]` を調べて束縛子の型を取ればよいです。やることはあと1つ残っています;後続の構造体のフィールドはその前の構造体のフィールドに依存することがあるため、残りのテレスコープ(各ステージの本体)を `proj thisFieldIdx s` でインスタンス化する必要があります。ここで `s` は推測する対象の射影の式中のオリジナルの構造体です。 ``` infer Projection(projIdx, structure): @@ -127,18 +223,34 @@ infer Projection(projIdx, structure): | _ => error ``` + + +## 数値リテラル + + +数値リテラルは `Nat` 宣言を参照する定数として推測されます。 ``` infer NatLiteral _: Const(Nat, []) ``` + + +## 文字列リテラル + + +文字列リテラルは `String` 宣言を参照する定数として推測されます。 ``` infer StringLiteral _: