型シノニムファミリを使用する利点は明らかです。それは型レベルの関数です。
しかし、データファミリーの場合はそうではありません。私の質問は、データ ファミリーのユースケースとは何ですか? どこで使用すればよいですか?
型シノニムファミリを使用する利点は明らかです。それは型レベルの関数です。
しかし、データファミリーの場合はそうではありません。私の質問は、データ ファミリーのユースケースとは何ですか? どこで使用すればよいですか?
1つの利点は、型族とは異なり、データ型が単射であるということです。
あなたが持っている場合
type family TF a
data family DF a
次に、それは、TFを使用している間は、そうではないことをDF a ~ DF b
意味します-型族が複数をマップできる一方で、それが完全に新しい型であることを確認できます(もちろん、とは異なる型と同じです)。同じ既存のタイプにタイプを入力します。a ~ b
a
DF a
[a]
[b]
a ~ b
2つ目は、他の型構築子と同様に、データ型を部分的に適用できるのに対し、型族は適用できないことです。
これは特に実際の例ではありませんが、たとえば、次のことができます。
data instance DF Int = DInt Int
data instance DF String = DString String
class C t where
foo :: t Int -> t String
instance C DF where -- notice we are using DF without an argument
-- notice also that you can write instances for data families at all,
-- unlike type families
foo (DInt i) = DString (show i)
基本的に、DF
そしてDF a
それ自体は、で宣言する他のタイプと同様に、実際のファーストクラスの正当なタイプですdata
。TF a
型に評価される単なる中間形式です。
しかし、私がデータファミリについて疑問に思っていて、同様のことを読んでいたとき、それはすべてあまり啓発的ではなかったか、少なくとも私にとってはそうではなかったと思います。
これが私が通っている経験則です。型族があるというパターンを繰り返していることに気付いたときはいつでも、すべての入力型data
について、型族がマップする新しい型を宣言するので、仲介者を切り取って、代わりにデータファミリを使用する方がよいでしょう。
ベクターライブラリの実際の例。vector
いくつかの異なる種類のベクトルがあります:ボックス化されたベクトル、ボックス化されていないベクトル、プリミティブベクトル、保存可能なベクトル。タイプごとVector
に、対応する可変MVector
タイプがあります(通常のベクトルは不変です)。したがって、次のようになります。
type family Mutable v :: * -> * -> * -- the result type has two type parameters
module Data.Vector{.Mutable} where
data Vector a = ...
data MVector s a = ...
type instance Mutable Vector = MVector
module Data.Vector.Storable{.Mutable} where
data Vector a = ...
data MVector s a = ...
type instance Mutable Vector = MVector
[etc.]
今ではその代わりに、私はむしろ持っていると思います:
data family Mutable v :: * -> * -> *
module Data.Vector{.Mutable} where
data Vector a = ...
data instance Mutable Vector s a = ...
type MVector = Mutable Vector
module Data.Vector.Storable{.Mutable} where
data Vector a = ...
data instance Mutable Vector s a = ...
type MVector = Mutable Vector
[etc.]
Vector
これは、すべてのタイプに対して正確に1つのMutable Vector
タイプがあり、それらの間に1対1の対応があるという不変条件をエンコードします。可変バージョンのaVector
は常に呼び出されMutable Vector
ます:それはその名前であり、他にはありません。がある場合Mutable Vector
は、対応する不変の型を取得できVector
ます。これは、型引数としてそこにあるためです。を使用type family Mutable
すると、引数に適用すると、指定されていない結果タイプ(おそらく、と呼ばれMVector
ますが、わかりません)に評価され、逆方向にマップする方法がありません。