elab 設計
elab パッケージは stella のカーネルです。ユニット型、Π、Σ、同一性型、W 型、宇宙の累積的階層を持つ Martin-Löf 流の依存型理論を実装し、評価による正規化(NbE)で定義的等価性を計算する双方向検査器によって型付けを判定します。このページでは、コードが実装する規則を述べ、それらが機能するための性質を導き、実装が不完全な箇所を記録します。
設計目標
- 構造が理論を反映した、小さく読みやすいカーネル。判断ごとに 1 つの関数、規則ごとに 1 つの match アーム。
- 少ない注釈で決定可能な検査。ユーザーは検査器が推論できない箇所にだけ注釈を付けます。
- 計算による型の等価性。2 つの型は、構文を書き換えるのではなく、評価して正規形を比較することで比較されます。
- プロジェクトが参照する文献に近い実装。Löh、McBride、Swierstra によるチュートリアル実装 λΠ と、Agda に関する Norell の博士論文です。11 A. Löh, C. McBride, W. Swierstra, “A tutorial implementation of a dependently typed lambda calculus”, Fundamenta Informaticae 102 (2010). U. Norell, Towards a practical programming language based on dependent type theory, PhD thesis, Chalmers (2007).
数学的背景
構文
項は、それを扱う判断によって分けられます。推論可能な項(TermInf)を e、検査可能な項(TermChk)を t と書くと、次のようになります。
e::=∣t::=#i∣x∣1∣Ui∣(t:t)∣Π(t,t)∣et∣Σ(t,t)∣π1e∣π2eId(t,t,t)∣J(t,t,t,t,t,e)∣W(t,t)∣wrec(t,t,t,t,e)e∣⋆∣λ.t∣(t,t)∣reflt∣sup(t,t)
束縛変数は de Bruijn インデックス #i(Bound(i))です。#0 は最も近い外側の束縛子を指します。束縛子は λ と、Π、Σ、W の第 2 引数です。束縛変数には名前がないため、2 つの項が α 同値であるのは木として等しいときに限ります。したがって TermInf と TermChk の導出された Eq は α 同値そのものです。
値と中立項
評価は項を意味領域 D(Value)に写します。
v,A::=n::=n∣1∣⋆∣Ui∣λf∣Π(A,F)∣Σ(A,F)∣(v,v)∣Id(A,v,v)∣reflv∣W(A,F)∣sup(v,F)x∣nv∣π1n∣π2n∣J(A,v,v,v,v,n)∣wrec(A,F,v,v,n)
ここで f,F:D→D は MoonBit の関数です。束縛子の本体は値上の関数になります。Π(A,F) は型 Πx:AF(x) です。中立項 n(Neutral)は自由変数 x で行き詰まった除去です。
すべての値は弱頭部正規形にあります。評価器はそのような簡約基を構築した時点で簡約するため、導入形式に除去が適用されたものは存在しません。
評価
評価 [[t]]ρ(eval_inf、eval_chk)は、i 番目のエントリが #i の値である環境 ρ を受け取ります。
[[#i]]ρ[[λ.t]]ρ[[et]]ρ=ρ(i)=λ(v↦[[t]]v::ρ)=[[e]]ρ⋅[[t]]ρ[[(t:T)]]ρ[[Π(A,B)]]ρ[[πke]]ρ=[[t]]ρ=Π([[A]]ρ,v↦[[B]]v::ρ)=πk⋅[[e]]ρ
他のコンストラクタも同様です。意味論的除去(val_app、val_fst、val_snd、val_j_elim、val_w_rec)が計算規則を担います。
(λf)⋅vπ1⋅(v,w)=v,π2⋅(v,w)J(A,x,P,d,y,reflz)wrec(A,B,P,s,sup(a,f))=f(v)=w=d=s⋅a⋅λf⋅λ(z↦wrec(A,B,P,s,f(z)))(β)(Σβ)(Jβ)(Wβ)
中立な引数に対してはスパインを延長します。たとえば n⋅v=nv です。注釈は消去されます。
読み戻し ql(quote、neutral_quote)は、l 個の束縛子の下にある値を項に変換します。関数は新しい変数に適用することで読み戻されます。
ql(λf)=λ.ql+1(f(Quote(l))),ql(Π(A,F))=Π(ql(A),ql+1(F(Quote(l)))),
そして、新しい変数はインデックスに戻されます。
ql(Quote(k))=#(l−k−1).
なぜ l−k−1 なのか。 読み戻しは束縛子を外側からレベルで番号付けします。深さ k で開かれた束縛子は Quote(k) を導入します。深さ l では、それより後に開かれた束縛子のレベルは k+1,…,l−1 なので、出現箇所とその束縛子の間には l−1−k 個の束縛子があり、これがその de Bruijn インデックスです。深さ l でスコープにあるのは Quote(0),…,Quote(l−1) だけなので、この変数は新しいものです。レベルにより新しさの保証は自明になり(名前の付け替えもシフトも不要)、インデックスにより出力は標準的になります。
閉じた項の正規形は nf(t)=q0([[t]]ε) です。
評価が β を尊重する理由
NbE の中心的な補題は、β 等価な項は同じ値を持つ、というものです。簡約基については、
[[(λ.t:T)u]]ρ=[[λ.t]]ρ⋅[[u]]ρ=(v↦[[t]]v::ρ)([[u]]ρ)=[[t]][[u]]ρ::ρ=[[t[u/#0]]]ρ,
ここで最後のステップは代入補題で、t に関する帰納法で証明されます。#0 に u を代入して ρ のもとで評価することは、ρ を u の値で拡張したもとで評価することと同じ値を与えます。他の簡約基についても、上記の Σβ、Jβ、Wβ を用いて同じ計算ができます。項の値はその β 同値類だけに依存するので、正規形も同様です:t=βu⇒nf(t)=nf(u)。逆に、nf(t) は t から β ステップで到達されるので、正規形が等しければ β 等価です。これらを合わせると、「正規形を比較する」ことは、評価が停止する項における β 等価性の決定手続きになります。22 U. Berger と H. Schwichtenberg の「An inverse of the evaluation functional for typed λ-calculus」(LICS 1991)が NbE を導入しました。A. Abel の Normalization by Evaluation: Dependent Types and Impredicativity(教授資格論文、LMU Munich、2013)は、η を持つ Martin-Löf 型理論に対する NbE の健全性と完全性を証明しています。これらは理論に関する結果であり、この実装については証明ではなくテストで確認されています。
双方向の判断
検査器には 2 つの判断があり、それぞれが 1 つの関数です。
- Γ;ρ⊢le⇒A(推論、
type_inf):e は型 A を持ち、検査器がそれを計算します。
- Γ;ρ⊢lt⇐A(検査、
type_chk):t は与えられた型 A を持ちます。
文脈 Γ は名前を型(値)に写し、ρ は現在の位置の環境、l は入った束縛子の数です。束縛子の下では、検査器はこの 3 つすべてを新しい変数 xl=Local(l) で拡張し、Γ,xl:A; ρ,xl⊢l+1 と書きます。したがって ρ は常に #i を変数 xl−1−i に写し、Γ がその型を与えます。以下では [[t]] は [[t]]ρ の略記です。
変数、定数、注釈。
Γ;ρ⊢#i⇒Aρ(i)=x(x:A)∈Γ(Var)Γ;ρ⊢x⇒A(x:A)∈Γ(Free)Γ;ρ⊢(t:T)⇒[[T]]Γ;ρ⊢T⇒UjΓ;ρ⊢t⇐[[T]](Ann)
宇宙と型形成子。 ここ以降、前提 T⇒Ui は、T が推論可能な項であり、その推論された型が宇宙そのものであることを要求します。これらの前提に包摂(subsumption)はありません。
Γ;ρ⊢1⇒U0(1-F)Γ;ρ⊢Ui⇒Ui+1(U-F)Γ;ρ⊢lΠ(A,B)⇒Umax(i,j)Γ;ρ⊢lA⇒UiΓ,xl:[[A]]; ρ,xl⊢l+1B⇒Uj(Π-F)
規則 (Σ-F) と (W-F) は、Π を Σ と W に置き換えた同じものです。同一性型はその台の宇宙に属します。
Γ;ρ⊢Id(A,x,y)⇒UiΓ;ρ⊢A⇒UiΓ;ρ⊢x⇐[[A]]Γ;ρ⊢y⇐[[A]](Id-F)
導入は検査される。 期待される型が、項が省略したもの、たとえば λ の定義域を補います。
Γ;ρ⊢lλ.t⇐Π(A,F)Γ,xl:A; ρ,xl⊢l+1t⇐F(xl)(Π-I)Γ;ρ⊢(t,u)⇐Σ(A,F)Γ;ρ⊢t⇐AΓ;ρ⊢u⇐F([[t]])(Σ-I)Γ;ρ⊢⋆⇐1(1-I)
Γ;ρ⊢lreflt⇐Id(A,v,w)Γ;ρ⊢lt⇐A[[t]]≡Av[[t]]≡Aw(Id-I)Γ;ρ⊢sup(a,f)⇐W(A,F)Γ;ρ⊢a⇐AΓ;ρ⊢f⇐Π(F([[a]]), _↦W(A,F))(W-I)
除去は推論される。 除去される項の型を推論し、それを分解します。
Γ;ρ⊢ft⇒F([[t]])Γ;ρ⊢f⇒Π(A,F)Γ;ρ⊢t⇐A(Π-E)Γ;ρ⊢π1e⇒AΓ;ρ⊢e⇒Σ(A,F)(Σ-E1)Γ;ρ⊢π2e⇒F(π1⋅[[e]])Γ;ρ⊢e⇒Σ(A,F)(Σ-E2)
パス帰納法。モチーフ P は推論され、Aˉ=[[A]]、xˉ=[[x]]、yˉ=[[y]]、Pˉ=[[P]] とします。
Γ;ρ⊢J(A,x,P,d,y,p)⇒Pˉ⋅yˉ⋅[[p]]Γ;ρ⊢A⇒UiΓ;ρ⊢x⇐AˉΓ;ρ⊢y⇐AˉΓ;ρ⊢p⇐Id(Aˉ,xˉ,yˉ)Γ;ρ⊢P⇒Π(D1,F1)Γ⊢lAˉ≤D1F1(z)=Π(D2,F2)Γ,z:Aˉ⊢l+1Id(Aˉ,xˉ,z)≤D2F2(w)=UkΓ;ρ⊢d⇐Pˉ⋅xˉ⋅reflxˉ(J)
motive の宇宙レベル k は固定せずに推論する。これは累積的宇宙に必要な性質である。ただし二つの定義域は引き続き検査する。P が適用されるのは Aˉ の点と xˉ から出るパスだけなので、その定義域はこれらの引数を受け付けなければならず、Π は定義域について反変である。形 Π(D1,Π(D2,Uk)) だけを検査すると、検査中に P が誤った型の引数に適用されうる。
W 再帰。B は推論可能な関数 A→Uk で、Bˉ(v)=[[B]]⋅v、Wˉ=W(Aˉ,Bˉ) とします。
Γ;ρ⊢wrec(A,B,P,s,w)⇒Pˉ⋅[[w]]Γ;ρ⊢A⇒UiΓ;ρ⊢B⇒Π(D,G), Aˉ≤D, G(z)=UkΓ;ρ⊢w⇐WˉΓ;ρ⊢P⇒Π(D′,G′), Wˉ≤D′, G′(z)=UmΓ;ρ⊢s⇐Πa:AˉΠf:Bˉ(a)→WˉΠh:Πb:Bˉ(a)Pˉ⋅f(b)Pˉ⋅sup(a,f)(W-E)
方向の切り替え。 推論可能な項は、その型が期待される型の部分型であれば検査モードで受理されます。
Γ;ρ⊢le⇐AΓ;ρ⊢le⇒A′Γ⊢lA′≤A(Sub)
逆方向は (Ann) です。検査可能な項は、その型を書き下せば推論可能になります。
部分型付けと変換
関係 A≤A′(subtype_nf。空の文脈向けには def_eq として公開)は累積性です。
Ui≤Uji≤jΓ⊢lΠ(A,F)≤Π(A′,F′)A′≤AΓ,xl:A′⊢l+1F(xl)≤F′(xl)Γ⊢lΣ(A,F)≤Σ(A′,F′)A≡A′Γ,xl:A⊢l+1F(xl)≤F′(xl)A≤A′A≡A′
Π 規則は定義域について反変です。A のすべての要素を受け付ける関数は部分型 A′≤A のすべての要素を受け付け、その F(x) における結果は上位型 F′(x) における結果でもあります。Σ 規則は第 1 成分を不変に保ちます。共変にしても健全ですが、実装とそのテストはそこで変換を要求します。
変換 A≡A′(conv_type)は型を構造的に比較し、新しい変数で束縛子に入り、同一性型の端点を η を加えた型主導の ≡A(conv_nf)で比較します。
f≡Π(A,F)g⟺f⋅xl≡F(xl)g⋅xl,p≡Σ(A,F)r⟺π1p≡Aπ1r∧π2p≡F(π1p)π2r,u≡1u′.
中立項はスパインごとに比較する(conv_neu)。適用の引数を正しい型で比較するために、先頭変数の型を Γ から調べる。先頭の型が Γ にない場合、たとえばコンテキストを持たない def_eq では、二つのスパインを読み戻しの結果で比較する。読み戻しが等しい値は定義的に等しいので、これは健全だが、引数に η は使わない。それ以外はすべて読み戻しの比較 ql(v)=ql(v′) に帰着する。
宇宙
宇宙は可述的かつ Russell 流です。型それ自体が項であり、Ui:Ui+1 です。自分自身を含む宇宙は理論を矛盾させる(Girard のパラドックス)ため、規則 Ui:Ui はありません。33 J.-Y. Girard, Interprétation fonctionnelle et élimination des coupures de l’arithmétique d’ordre supérieur, thèse d’État (1972)。短い証明として A. J. C. Hurkens, “A simplification of Girard’s paradox”, TLCA 1995 があります。 型形成子は構成要素の大きい方の宇宙 max(i,j) に属し、これが可述性の要求するところです。ΠA:U0A→A は U0 上で量化するので U1 に属します。累積性 Ui≤Ui+1 は型付け規則ではなく部分型付けの一部で、(Sub) で使われます。
設計上の決定
双方向検査
問題。 依存型理論で注釈のない λ の型を推論するには定義域を推測する必要があり、これは一般に高階単一化を意味し、高階単一化は決定不能です。
選択肢。 (a) すべての束縛子に注釈を付ける、λ(x:A).t。(b) 単一化変数で推論する。(c) 項を検査されるものと推論されるものに分ける。
選択。 (c)。導入形式(λ、ペア、⋆、refl、sup)は、型が欠けている情報を決定するので検査されます。除去と型形成子は、先頭の型が全体の型を決定するので推論されます。注釈が必要なのは導入が除去と出会う箇所、すなわち (λ.t:T)u のような β 簡約基か、モチーフが関数でなければならない箇所だけです。正規形の項は、モチーフを除けば注釈をまったく必要としません。この区別を型 TermInf と TermChk に符号化することで、注釈のない簡約基は実行時エラーではなく表現不可能になります。
クロージャを持つ値
問題。 型を比較するには、束縛子の下も含めて型を評価する必要があります。
選択肢。 (a) 代入によって構文を書き換える。これには各ステップで捕獲回避代入とインデックスのシフトが必要です。(b) 束縛子がホスト言語の関数である意味領域へ評価する。
選択。 (b)。本体は MoonBit の関数 (Value) -> Value で表現されるため、β 簡約はホスト関数の呼び出しであり、構文上の代入は決して起こりません。読み戻しは必要なときだけ構文を復元します。表示のため、ql による比較のため、そして正規形を比較する検査の中でです。
項ではインデックス、値ではレベル
項はインデックスを使うので、α 同値は構造的等価性となり、閉じた部分項はその位置に依存しません。値の中の新しい変数はレベル(検査器では Local(l)、読み戻しでは Quote(l))を使うので、新しい変数の作成はカウンタのインクリメントであり、値がシフトを必要とすることはありません。2 種類の新しい変数は別々の Name コンストラクタなので、検査器が導入した変数が読み戻し中に導入された変数と取り違えられることはありません。
明示的な持ち上げの代わりに部分型付け
累積性は、明示的な持ち上げ演算子 ↑:Ui→Ui+1 で表現することもできます。代わりにそれを方向の切り替え (Sub) に組み込むことで、U0 で書かれた型を項レベルの型強制なしに U1 で使えます。type_chk(..., Inf(UnitType), VUniverse(1)) のようにです。
正しさと不変条件
- 環境の不変条件。 レベル l では、
env はちょうど l 個のエントリを持ち、エントリ i は xl−1−i であり、ctx はすべての xk を宣言しています。束縛子の下に入る規則だけが状態を拡張し、その際 3 つすべてを同時に拡張します。(Var) はこの不変条件に依存しています。これを破る type_inf の呼び出し側は Internal error: Bound variable not in environment を受け取ります。
- 検査済みのものだけを評価する。 どの規則でも、部分項はそれを検査する前提の後で評価されます。たとえば (Π-E) の引数や (Ann) の型です。評価器は型の誤った簡約基で panic するので、この順序こそが型の誤った入力に対して検査器を全域的に保つものです。検査器は評価する前に
TypeError を送出します。
- 型の安定性。 検査器が返す型はすべて値なので、呼び出し側が再び正規化する必要はなく、型の比較はすべて値の上で行われます。
- 停止性。 型の正しい項の評価は、W 型と可述的宇宙を持つ Martin-Löf 型理論の正規化定理により停止します。検査器は検査済みの項だけを評価する(不変条件 2)ので、規則が健全であるすべての入力で停止します。以下に挙げる欠落が例外です。
既知の欠落
実装は開発途中であり、いくつかの規則は上記の理論より弱かったり強かったりします。ユーザーが避けられるようにここに記録しますが、コードは変更していません。
- 部分型付けは Π、Σ、宇宙だけ。 W と同一性型は、成分に累積性を持たない変換によって比較されます。
採用しなかった代替案
- 名前付きの型付き項。 名前付き変数は捕獲回避代入を必要とし、α 同値を別の検査にしてしまいます。de Bruijn インデックスはその両方を避けます。
- 代入に基づく正規化。 構文的代入の繰り返しは、クロージャへの評価より遅く、正しく実装するのも難しく、しかも別途変換の検査が必要です。
- 非可述的な宇宙や自分自身を含む宇宙。 U:U は矛盾しており、非可述的な Prop は stella が従う理論には含まれません。
- 帰納族。 W 型は単一の除去子で整礎木を提供し、カーネルを小さく保ちます。一般の帰納的定義には正値性検査器が必要になります。
境界
このパッケージは意図的に次のことを行いません。
- 表層構文の解析、暗黙引数のエラボレーション、単一化問題の求解。項はコア構文の MoonBit 値として構築します。
- 定義、
let、本体を持つグローバル定義のサポート。文脈には公理(型を持つ名前)だけが入ります。
- 宇宙多相、帰納族、空型、直和型の提供。
- 一価性、高次帰納型、その他ホモトピー型理論の機能の実装。論考ではそれらをプロジェクトの目標として述べていますが、実装はされていません。
- 評価器への型の誤った入力に対する保証。
eval_inf、eval_chk、val_ 関数は panic することがあります。