製品タイプとタイプ推論


15

私は、連結言語用のコンパイラに取り組んでおり、型推論のサポートを追加したいと考えています。Hindley–Milnerは理解していますが、型理論を学んでいるので、それをどのように適応させるかわかりません。次のシステムは健全で、決定的に推測できますか?

用語は、リテラル、用語の構成、用語の引用、またはプリミティブです。

e::=x|ee|[e]|

すべての用語は関数を示します。2つの関数およびe 2の場合、e 1e1e2並置が逆組成を示しています。リテラルはニラディック関数を示します。e1e2=e2e1

構成以外の用語には、基本的なタイプルールがあります。

x:ι[Lit]Γe:σΓ[e]:α.ασ×α[Quot],α not free in Γ

連結言語にはアプリケーションルールがないため、アプリケーションのルールは特にありません。

型は、リテラル、型変数、またはスタックからスタックへの関数のいずれかです。ここで、スタックは右ネストされたタプルとして定義されます。すべての関数は、「残りのスタック」に関して暗黙的にポリモーフィックです。

τ::=ι|α|ρρρ::=()|τ×ρσ::=τ|α.σ

これが疑わしいと思われる最初のことですが、何が問題なのか正確にはわかりません。

読みやすくし、かっこを減らすために型スキームでb = b × a 。また、単一の値ではなく、スタックを示す変数に大文字を使用します。ab=b×(a)

6つのプリミティブがあります。最初の5つはかなり無害です。dup最上位の値を取り、その2つのコピーを生成します。swap上位2つの値の順序を変更します。popトップ値を破棄します。quote値を取り、それを返す引用(関数)を生成します。applyスタックに引用を適用します。

dup::Ab.AbAbbswap::Abc.AbcAcbpop::Ab.AbAquote::Ab.AbA(C.CCb)apply::AB.A(AB)B

最後のcompose結合子、は2つの引用符を取り、それらの連結のタイプ、つまり[ e 1 ]を返す必要があります。静的に型付けされた連結言語Catでは、型は非常に簡単です。[e1][e2]compose=[e1e2]compose

compose::ABCD.A(BC)(CD)A(BD)

ただし、このタイプは制限が厳しすぎます。最初の関数の生成が2番目の関数の消費と正確に一致する必要があります。実際には、異なるタイプを想定してから、それらを統一する必要があります。しかし、そのタイプをどのように書くでしょうか?

compose::ABCDE.A(BC)(DE)A

あなたが聞かせている場合意味の違いの2種類を、私は考えてあなたがの種類書くことができ、正しくを。compose

compose::ABCDE.A(BC)(DE)A((DC)B((CD)E))

これはまだ比較的簡単です。compose関数と1つのf 2D Eを取ります。その結果、消費Bを消費頂上F 2によって生成されていないF 1、及び生成Dをの生産頂上fは1で消費されないF 2。これにより、通常の構成の規則が与えられます。f1:BCf2:DEBf2f1Df1f2

Γe1:AB.ABΓe2:CD.CDΓe1e2:((CB)A((BC)D))[Comp]

しかし、この仮想的な実際に何かに対応していることは知りません。タプルの単純な違いでしょうか?

A.()A=()A.A()=AABCD.ABCD=BD iff A=Cotherwise=undefined

Is there something horribly broken about this that I’m not seeing, or am I on something like the right track? (I’ve probably quantified some of this stuff wrongly and would appreciate fixes in that area as well.)


How do you use variables in your grammar? This question should help you in handling the "subtyping" you seem to need.
jmad

1
@jmad: I’m not sure I understand the question. Type variables are just there for the sake of formally defining type schemes, and the language itself doesn’t have variables at all, just definitions, which can be [mutually] recursive.
Jon Purdy

Fair enough. Can you say why (perhaps with an example) the rule for compose is too restrictive? I have the impression that this is fine like this. (e.g. the restriction C=D could be handled by unification like for application in like in the λ-calculus)
jmad

@jmad: Sure. Consider twice defined as dup compose apply, which takes a quotation and applies it twice. [1 +] twice is fine: you’re composing two functions of type ιι. But [pop] twice is not: if Ab.f1,f2:AbA, the problem is that AAb, so the expression is disallowed even though it ought to be valid and have type Ab.AbbA. The solution is of course to put the qualifier in the right place, but I’m mainly wondering how to actually write the type of compose without some circular definition.
Jon Purdy

回答:


9

The following rank-2 type

compose:ABCδ.δ (αα AαB ββ BβCδ γγ AγC
十分に一般的なようです。質問で提案されているタイプよりもはるかに多態的です。ここで、変数はスタックの連続したチャンクを定量化し、マルチ引数関数をキャプチャします。

ギリシャ文字は、わかりやすくするために、残りのスタック変数に使用されています。

スタック上の最初の要素の出力スタックは、2番目の要素の入力スタックと同じである必要があるという制約を表します。変数を適切にインスタンス化するB 実際の2つの引数は、質問で提案するように、新しい操作を定義するのではなく、制約を適切に機能させる方法です。

ランク2の型をチェックする型は、一般的に決定できませんが、実際には良い結果をもたらすいくつかの作業が行われていると思います(Haskellの場合):

  • Simon L. Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, Mark Shields: Practical type inference for arbitrary-rank types. J. Funct. Program. 17(1): 1-82 (2007)

The type rule for composition is simply:

Γe1:α.α Aα BΓe1:α.α Bα CΓe1 e2:α.α Aα C

To get the type system to work in general, you need the following specialisation rule:

Γe:α.α Aα BΓe:α.C Aα C B

Thanks, this was very helpful. This type is correct for functions of a single argument, but it doesn’t support multiple arguments. For instance, dup + should have type ιι because + has type ιιι. But type inference in the absence of annotations is an absolute requirement, so clearly I need to go back to the drawing board. I have an idea for another approach to pursue, though, and will blog about it if it works out.
Jon Purdy

1
The stack types quantify over stack fragments, so there is no problem dealing with two argument functions. I'm not sure how this applies to dup +, as that does not use compose, as you defined it above.
Dave Clarke

Er, right, I meant [dup] [+] compose. But I read αB as B×α; say B=ι×ι; then you have (ι×ι)×α and not ι×(ι×α). The nesting isn’t right, unless you flip the stack around so that the top is the last (deepest nested) element.
Jon Purdy

I may be building my stack in the wrong direction. I don't think the nesting matters, so long as the pairs building up the stack do not appear in the programming language. (I'm planning to update my answer, but need to do a little research first.)
Dave Clarke

Yeah, nesting is pretty much an implementation detail.
Jon Purdy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.