型レベルでの連結の表現


8

スタックに基づいて、連結パラダイムに従って、小さな単純な言語を作成することにより、連結プログラミングについて詳しく学びたいと思います。

残念ながら、私は連結言語とそれらの実装に関する多くのリソースを見つけていません。そのため、私の可能なことを前もって申し訳ありません。

したがって、私は言語を関数の連結の単純なシーケンスとして定義し、ASTでリストとして表しています。

data Operation
    = Concat [Operation]
    | Quotation Operation
    | Var String
    | Lit Literal
    | LitOp LiteralOperation

data Literal
    = Int Int
    | Float Float

data LiteralOperation
    = Add | Sub | Mul | Div

次のプログラム4 2 swap dup * +(に対応2 * 2 + 4)が解析されると、次のASTが得られます。

Concat [Lit (Int 4), Lit (Int 2), Var "swap", Var "dup", LitOp Mul, LitOp Add]

次に、タイプを推測して確認する必要があります。

私はこの型システムを書きました:

data Type
    = TBasic BasicType   -- 'Int' or 'Float'
    | TVar String        -- Variable type
    | TQuoteE String     -- Empty stack, noted 'A'
    | TQuote String Type -- Non empty stack, noted 'A t'
    | TConc Type Type    -- A type for the concatenation
    | TFun Type Type     -- The type of functions

それが私の質問の出番です。なぜなら、その式から推測するタイプがわからないからです。結果の型は明白ですがInt、型レベルでこのプログラムを実際に完全にチェックする方法がわかりません。

最初は、上記のように、TConc型がTFun関数を表すのと同じ方法で連結を表す型について考えました。結局、連結シーケンスは一意の関数を形成するためです。

まだ検討していない別のオプションは、この式シーケンスの各要素に関数構成推論規則を適用することです。スタックベースでどのように機能するかわかりません。

問題はそうです:どうやってそれを行うのですか?使用するアルゴリズム、および型レベルでのどのアプローチを優先する必要がありますか?

回答:


9

連結言語の主要なアイデアは、構文と意味論的ドメインがモノイドを形成し、意味論がモノイド同型であるということです。構文は、基本操作によって生成される無料のモノイドであり、リストとしてよく知られています。その操作はリストの連結、つまり(++)Haskellです。型付けされていないコンテキストでは、セマンティックドメインは、構成として操作を行う(スタック上の)内部機能のモノイドにすぎません。つまり、インタープリターは次のようになります。

data Op = PushInt Int| Call Name | Quote Code | Add | ... -- etc.
type Code = [Op]

-- Run-time values
data Value = Q (Endo Stack) | I Int | ... -- etc.
type Stack = [Value]

-- You'd probably add an environment of type Map Name (Endo Stack)
interpretOp :: Op -> Endo Stack
interpretOp (PushInt n) = Endo (I n:)
interpretOp (Quote c) = Endo (Q (interpetCode c):)
interpretOp op = ... -- etc.

interpretCode :: Code -> Endo Stack
interpretCode = foldMap interpretOp

runCode :: Code -> Stack
runCode code = case interpretCode code of Endo f -> f []

非常にナイーブな)コンパイラを作成するのも同じくらい簡単です。変更される唯一のものは、ターゲットモノイドです。ターゲットモノイドは、ターゲット言語の構文のフラグメントから構築された構文モノイドになり、したがってinterpretOpになりcompileOpます。このターゲットモノイドは、順次合成の操作を伴うステートメントのシーケンスである場合があります;しかし、あなたはかなりもっと洗練されることができます。

連結言語の型システムはそれほど明白ではなく、型付き連結言語はほとんどありません。は私が知っている最も重要な例です。これに取り組み始め、発生するいくつかの問題を経験する1つの方法は、Haskellに連結言語を組み込むことです。add :: (Int, Int) -> Intこれは構成されないので、不要なことがすぐにわかります。代わりに、あなたが持っていadd :: (Int, (Int, s)) -> (Int, s)ます。これは単純なものに対して非常にうまく機能します。これもまた、明らかに貧しい男性の行タイプです。最初に遭遇する最も重要なハードルの1つは、見積もりの​​処理です。問題は、より高いランクの型と命令型のインスタンス化を必要[add]とするような型の何かに対応するs -> ((forall s'. (Int, (Int, s')) -> (Int, s')), s)必要があることです。猫は両方を持っているようです。それは確かにランクの高い型を持ち、型変数の代わりにポリタイプを使用します。それは、無制限に理解できる方法で物事を行っているかもしれません。これをHaskellに埋め込むことでこれを達成するには、型レベルのリスト、(閉じた)型ファミリー、およびローカルなユニバーサル数量化を使用すると実行できる場合があります。ただし、現時点では、カスタム型システムを作成する方が理にかなっています。

不均一なスタック効果のある操作も問題になる可能性がありますが、ほとんどの場合、それらを省略して、一貫したスタックを保証することを行う代替手段を提供することは理にかなっています。

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