Hindley-Milnerのルールを理解するにはどうすればよいですか?
Hindley-Milnerは、後続の計算(自然な演繹ではない)の形式の一連のルールであり、明示的な型宣言なしでプログラムの構造からプログラムの(最も一般的な)型を推定できることを示します。
記号と表記
まず、記号について説明し、演算子の優先順位について説明します
- 𝑥は識別子(非公式には変数名)です。
- :はのタイプです(非公式には、インスタンス、または「is-a」)。
- 𝜎(シグマ)は、変数または関数の式です。
- したがって、𝑥:𝜎は「 " is -a 𝜎」と読み取られます
- ∈は「の要素」であることを意味します
- 𝚪(ガンマ)は環境です。
- ⊦(アサーション記号)は、アサート(または証明しますが、状況に応じて「アサート」の方が読み取りが良い)を意味します。
- ⊦Γ 𝑥 : σ、読み取られる"Γは、その𝑥をアサートは、σ "
- 𝑒は、型の実際のインスタンス(要素)ですσ。
- 𝜏(タウ)はタイプ:基本、変数(𝛼)、機能𝜏→𝜏 'または製品𝜏×𝜏'(製品はここでは使用されません)
- 𝜏→𝜏 'は関数型であり、 𝜏と𝜏'は潜在的に異なる型です。
λ𝑥.𝑒手段λ(ラムダ)は、引数を取る匿名関数で𝑥、発現、返し𝑒を。
聞かせて 𝑥 =𝑒₀ で 𝑒₁式の手段、𝑒₁、代替𝑒₀どこ𝑥が表示されます。
⊑は、前の要素が後者の要素のサブタイプ(非公式にはサブクラス)であることを意味します。
- 𝛼は型変数です。
- ∀ α.σはタイプ、∀(すべてのための)引数の変数であり、 α、帰国σ式を
- ∉free (𝚪)は、外部コンテキストで定義された𝚪の自由型変数の要素ではないことを意味します。(バインドされた変数は置換可能です。)
線より上のすべてが前提であり、下のすべてが結論です(Martin-Löfによる)
優先順位、例による
ルールからより複雑な例をいくつか取り、優先順位を示す冗長な括弧を挿入しました:
- 𝑥:σ ∈Γ書くことができます(𝑥:σ) ∈Γ
Γ⊦ 𝑥 : σは Γを書くことができ⊦(𝑥 : σ)
Γは⊦ せ 𝑥 =𝑒₀ で 𝑒₁:τは、
((⊦等価Γあるせ(𝑥 =𝑒₀)で 𝑒₁):τ)
Γ⊦ λ𝑥.𝑒:τ→τ 'である等価Γ⊦((λ𝑥.𝑒):(τ→τ' ))
次に、アサーションステートメントと他の前提条件を区切る大きなスペースは、そのような前提条件のセットを示し、最後に前提と結論を区切る水平線が優先順位の終わりを示します。
ルール
ここに続くのは、規則の英語の解釈であり、それぞれに緩やかな言い直しと説明が続きます。
変数
𝑥がtype(シグマ)のタイプ、Given(ガンマ)の要素で
あるとすると、Givenは𝑥がaであると断定します。
言い換えると、𝚪は、𝜎が𝜎の型であるため、𝑥がtypeの型であることを知っています。
これは基本的にトートロジーです。識別子名は変数または関数です。
機能アプリケーション
与えられた𝚪アサート𝑒₀は関数型であり、𝚪アサート𝑒₁は𝜏
結論であるluアプライする関数applyingを𝑒₁にアサートする𝑒₁は型𝜏である
ルールを言い換えると、関数applicationはタイプ𝜏 'を返すことがわかっています。これは、関数がタイプ𝜏→𝜏'を持ち、タイプargumentの引数を取得するためです。
つまり、関数が型を返すことがわかっていて、それを引数に適用すると、結果は、その関数が返す型のインスタンスになります。
関数の抽象化
タイプ𝚪の𝚪とGivenが、𝑒がタイプであることを表明し、𝜏 'が
匿名関数を表明したと結論付け、𝑥が式を返す𝜆の𝜆、タイプtype→𝜏'を𝑒
繰り返しますが、𝑥を受け取り、式returnsを返す関数がある場合、type(a 𝜏)は𝑒が𝜏 'であると断言するため、it→𝜏'型であることがわかります。
𝑥がタイプtypeであり、したがって式𝑒がタイプ𝜏 'であることがわかっている場合、式𝑥を返すofの関数はタイプ𝜏→𝜏'です。
変数宣言をしましょう
Γは式σの、𝑒₀をアサートし、所与及び Γと𝑥は、タイプσのタイプの𝑒₁をτアサート
Γがアサート結論let
𝑥=𝑒₀ in
型τの𝑒₁を
大まかに言うと、𝑒₀は𝑒₁(a 𝜏)の𝑒₀にバインドされています。これは、𝑒₀が𝜎であり、𝑥が𝑒₁が𝜏であることを表明する𝑥だからです。
つまり、a(変数または関数)である式beと、名前𝑥、また𝜎、およびタイプ𝑒₁の式haveがある場合、𝜏の代わりにsubstituteを𝑒₀に置き換えることができますofの。
インスタンス化
Givenがタイプ𝜎 'のアサート𝚪であり、𝜎'が𝜎のサブタイプである
とすると、結論𝚪アサート𝑒はタイプofである
式𝑒は親タイプofです。式𝜎はサブタイプ𝜎 'であり、𝜎は𝜎'の親タイプであるためです。
インスタンスが別のタイプのサブタイプであるタイプである場合、そのインスタンスはそのスーパータイプ(より一般的なタイプ)のインスタンスでもあります。
汎化
Givenが𝑒を表明し、𝑒が𝜎であり、 𝛼がvariablesの自由変数の要素ではないことを
前提に、𝚪が𝚪を表明し、すべての引数式の型𝛼 𝛼 𝛼式を返す
つまり、generalは𝑒であり、𝛼は自由変数ではないことがわかっているため、一般に、returningはすべての引数変数(𝛼)に対してtypeと入力され、𝜎を返します。
これは、包含スコープでまだバインドされていない引数(ローカルでない変数)のすべての型を受け入れるようにプログラムを一般化できることを意味します。これらのバインドされた変数は置換可能です。
すべてを一緒に入れて
特定の仮定(自由/未定義の変数がない、既知の環境など)を前提として、次のタイプを知っています。
- プログラムのアトミック要素(変数)、
- 関数(関数アプリケーション)から返される値
- 関数構造(関数抽象化)、
- letバインディング(変数宣言を許可)、
- インスタンスの親タイプ(インスタンス化)、および
- すべての式(汎化)。
結論
これらのルールを組み合わせることで、型注釈を必要とせずに、アサートされたプログラムの最も一般的なタイプを証明できます。