web-dev-qa-db-ja.com

HaskellとIdr​​isの違い:タイプユニバースでのランタイム/コンパイル時間の反映

したがって、イドリスでは、次のように書くことは完全に有効です。

item : (b : Bool) -> if b then Nat else List Nat
item True  = 42
item False = [1,2,3] // cf. https://www.youtube.com/watch?v=AWeT_G04a0A

型署名がないと、これは動的に型付けされた言語のように見えます。しかし、確かに、Idrisは依存型です。 item bの具体的なタイプは、実行時にのみ決定できます。

もちろん、これはHaskellプログラマーの話です。Idrisの意味でのitem bのタイプはコンパイル時に与えられ、それはif b then Nat ...です。

さて、私の質問です。Haskellでは、ランタイムとコンパイル時の境界は、値の世界(False、 "foo"、3)と型の世界(Bool、String、Integer)の間で正確に実行されると結論付けるのは正しいですか? Idrisでは、ランタイムとコンパイル時間の境界は宇宙を越えていますか?

また、Haskellの依存型(DataKindsとTypeFamiliesを使用、 この記事 を参照)でも、HaskellはIdrisとは反対に値を許可しないため、上記の例はHaskellでは不可能であると仮定するのは正しいですか?タイプレベルにリークしますか?

29
ruben.moor

はい、Idrisでの型と値の区別が、コンパイル時のみの区別と実行時​​とコンパイル時の区別と一致していないことに注意してください。それはいいことだ。プログラムロジックの場合と同様に、仕様でのみ使用される「ゴースト変数」があるのと同様に、コンパイル時にのみ存在する値があると便利です。実行時に型表現を用意して、データ型のジェネリックプログラミングを可能にすることも役立ちます。

Haskellでは、DataKinds(およびPolyKinds)で

type family Cond (b :: Bool)(t :: k)(e :: k) :: k where
  Cond 'True  t e = t
  Cond 'False t e = e

そして、それほど遠くない将来に、私たちは書くことができるでしょう

item :: pi (b :: Bool) -> Cond b Int [Int]
item True  = 42
item False = [1,2,3]

しかし、そのテクノロジーが実装されるまでは、次のような依存関数タイプのシングルトン偽造を処理する必要があります。

data Booly :: Bool -> * where
  Truey  :: Booly 'True
  Falsey :: Booly 'False

item :: forall b. Booly b -> Cond b Int [Int]
item Truey  = 42
item Falsey = [1,2,3]

あなたはそのような偽物でかなり遠くまで行くことができます、しかし私たちが本物を持っていればそれはすべてはるかに簡単になるでしょう。

重要なことに、Haskellの計画は、forallpiを維持および分離し、それぞれパラメトリック多相性とアドホック多相性をサポートすることです。 forallに付随するラムダとアプリケーションは、今と同じようにランタイムコード生成で消去できますが、piのものは保持されます。また、実行時型の抽象化pi x :: * -> ...を使用して、Data.Typeableである複雑さのネズミの巣をゴミ箱に捨てることも理にかなっています。

28
pigworker

今私の質問:Haskellでは、ランタイムとコンパイル時の境界は、値の世界(False、 "foo"、3)と型の世界(Bool、String、Integer)の間で正確に実行されると結論付けるのは正しいですか? Idrisでは、ランタイムとコンパイル時間の境界は宇宙を越えていますか?

Idrisは Epic にコンパイルされます (更新:いいえ、Spearmanが以下のコメントで述べているように、Episにコンパイルされなくなりました):

名前がスコープ内にあるかどうかを確認する以外に、セマンティックチェックはありません---高水準言語が型チェックを実行したと想定されます。いずれの場合も、Epicは高水準型システムまたはユーザーが行った変換について想定しないでください。適用されます。型の注釈は必須ですが、コンパイラーにヒントを与えるだけです(これを変更する可能性があります)。

したがって、タイプは表記上重要ではありません。つまり、用語の意味はそのタイプに依存しません。さらに、いくつかの値レベルのものを消去することができます。 in Vect n A(ここで、Vectは静的に既知の長さのリストのタイプです)n(長さ)は消去できます なぜなら

Brady、McBride、McKinnaによって BMM04 で説明されている方法があり、データ構造からインデックスを削除し、それらを操作する関数がすでに適切なインデックスのコピーを持っているか、インデックスをすばやく作成できるという事実を利用しています必要に応じて再構築。

ここで重要なのは、IdrisのpiはHaskellのforallとほぼ同じように型に対して機能するということです。この場合、両方ともパラメトリックです(ただし、これらのパラメトリックは異なります)。コンパイラーは型を使用してコードを最適化できますが、どちらの言語でも制御フローは型に依存しません。つまり、if A == Int then ... else ...とは言えません(ただし、Aが正規形式の場合は、 Intであるかどうかを静的に認識しているため、 can write A == Intですが、すべての決定は実行前に行われるため、制御フローには影響しません)。 item bの具体的なタイプは、実行時には問題になりません。

ただし、 pigworker が言うように、型がパラメトリックである必要はありません。また、必ずしも値がノンパラメトリックである必要はありません。タイプレベル—値レベルとパラメトリック—ノンパラメトリックは完全に直交する二分法です。詳細については、 this answerを参照してください。

したがって、HaskellとIdr​​isは、ランタイム/コンパイルコンテンツでの値レベルのものの処理方法が異なります(Idrisでは、引数を.でマークして消去可能にすることができるため)が、型はほぼ同じ方法で処理されます。

9
user3237465