Haskellのパラメトリック多形は、t :: *
型のすべての値がランタイムオブジェクトへのポインタとして一様に表現されているという事実に依存しています。したがって、同じマシンコードが多態性値のすべてのインスタンス化に対して機能します。
RustまたはC++の対照多型関数。例えば、そこにあるアイデンティティ関数は、forall a. a -> a
に類似した型を持ちますが、異なるa
型の値が異なる可能性があるため、コンパイラは各instatiationに対して異なるコードを生成する必要があります。
data Id = Id (forall a. a -> a)
を、このような機能は、任意のサイズのオブジェクトのために正しく動作しなければならないので、これはまた、我々は、ランタイム・ボックスの周りに多型の関数を渡すことができないことを意味します。例えば、ランタイムforall a. a -> a
関数が、a
値のサイズとコンストラクタ/デストラクタに関する情報を保持する暗黙の引数を必要とする場合など、この機能を使用するには追加のインフラストラクチャが必要です。
さて、newtype Vec = Vec (# Float#, Float# #)
の問題は、たとえVec
は、いくつかのt :: *
の値は、それを扱うことができない見込ん種類*
、ランタイムコードを持っていることです。 Haskellオブジェクトへのポインタではなく、スタックで割り当てられたfloatのペアであり、Haskellオブジェクトを期待するコードに渡すと、segfaultまたはエラーが発生します。
一般に(# a, b #)
はポインタサイズである必要はないため、ポインタサイズのデータフィールドにはコピーできません。
#
タイプを返すタイプファミリーは、関連する理由で許可されていません。次のことを考えてみましょう:
type family Foo (a :: *) :: # where
Foo Int = Int#
Foo a = (# Int#, Int# #)
data Box = forall (a :: *). Box (Foo a)
当社Box
Foo a
が異なるa
-sため、異なるサイズを有しているので、表現のランタイムではありません。一般に、#
を超える多型は、Rustのように異なるインスタンシエーションに対して異なるコードを生成する必要がありますが、これは通常のパラメトリック多型と悪影響を及ぼし、多態性値の実行時表現を難しくするのでGHCはこれを気にしません。
ああ、それは私の第三質問してきたはずです
"です。なぜそれは種類 '*'を持たなければならないのですか? 'Vec =(#Float#、Float##) 'と書くと、Vecはkindが'# 'なので、newtypeには'# 'もあると思います。 –
'type Vec = ... 'は単なる同義語です。 'newtype'は、ラップされた値の型とは異なる新しい型を定義します。新しい型を定義するためのすべてのHaskellの構造体がkind *で返るように起こります。 unboxed型の 'newtype'-sは、異なるサイズのオブジェクトのポリモーフィズムを可能にするために前述したインフラストラクチャの大きなチャンクを必要とします。そうでなければ、単調にしか使用できず、ラップされた型。 –
@AndrásKovácsこれは、種類「#」で作成できましたが、その後、 'id :: a - > a'は' a ::〜 'のために' a〜Vec'に特化することはできません。この制限は、未処理のボックス化されていないデータを渡すときにランタイムがボックス化されたデータを一様に(ポインタ付きで)処理することに関連するため、ボックス化されていない各タイプに対してカスタムの 'id'が必要です。 'int 'は参照型ではないので、Javaが' T'のようなジェネリックを持つことができない点とあまり違いはありません。 –
chi