サーチ…


タイプシノニムファミリー

型シノニムファミリーは単なる型レベル関数です。パラメータ型と結果型を関連付けます。これらは3つの異なる品種があります。

クローズド・タイプ・シノニム・ファミリ

これらは、通常の値レベルのHaskell関数とよく似ています。特定の型を他の型にマッピングするいくつかの句を指定します。

{-# LANGUAGE TypeFamilies #-}
type family Vanquisher a where
    Vanquisher Rock = Paper
    Vanquisher Paper = Scissors
    Vanquisher Scissors = Rock

data Rock=Rock; data Paper=Paper; data Scissors=Scissors

タイプ・シノニム・ファミリーを開く

これらは、型クラスインスタンスによく似ています。誰でも他のモジュールに節を追加できます。

type family DoubledSize w

type instance DoubledSize Word16 = Word32
type instance DoubledSize Word32 = Word64
-- Other instances might appear in other modules, but two instances cannot overlap
-- in a way that would produce different results.

クラス関連型の同義語

オープンタイプのファミリーは、実際のクラスと組み合わせることもできます。これは通常、 関連するデータファミリと同様に、いくつかのクラスメソッドは追加のヘルパーオブジェクトを必要とし、これらのヘルパーオブジェクトインスタンスごとに異なる可能性がありますが、共有することもできます。良い例はVectorSpaceクラスです:

class VectorSpace v where
  type Scalar v :: *
  (*^) :: Scalar v -> v -> v

instance VectorSpace Double where
  type Scalar Double = Double
  μ *^ n = μ * n

instance VectorSpace (Double,Double) where
  type Scalar (Double,Double) = Double
  μ *^ (n,m) = (μ*n, μ*m)
  
instance VectorSpace (Complex Double) where
  type Scalar (Complex Double) = Complex Double
  μ *^ n = μ*n

最初の2つのインスタンスでは、 Scalarの実装はどのように同じであるかに注意してください。これは、関連するデータファミリでは不可能です。データファミリは注入型であり、型同義語ファミリはそうではありません。

非注入性は上記のような可能性を開くが、タイプ推論をより困難にする。たとえば、次のようにタイプチェックは行われません。

class Foo a where
  type Bar a :: *
  bar :: a -> Bar a
instance Foo Int where
  type Bar Int = String
  bar = show
instance Foo Double where
  type Bar Double = Bool
  bar = (>0)

main = putStrLn (bar 1)

この場合、 barの引数自体は単なる多形Numリテラルなので、コンパイラはどのインスタンスを使用するかを知ることができません。また、型関数Barは、逆方向には解決できません。なぜなら、それは注入ではなく、したがって可逆ではないからです( Bar a = String持つ型が複数ある可能性があります)。


これらの2つのインスタンスだけでは、実際にはインジェクティブですが、コンパイラは後でインスタンスを追加して動作を中断することは誰にも分かりません。

データ型ファミリ

データ・ファミリを使用すると、型引数に基づいて異なる実装を持つデータ型を作成できます。

スタンドアロンデータファミリ

{-# LANGUAGE TypeFamilies #-}
data family List a
data instance List Char = Nil | Cons Char (List Char)
data instance List () = UnitList Int

上記の宣言では、 Nil :: List CharUnitList :: Int -> List ()

関連するデータファミリ

データファミリは、タイプメスに関連付けることもできます。これは、ジェネリックなtypeclassメソッドには必要ですが、具体的なインスタンスに応じて異なる情報を含める必要がある「ヘルパーオブジェクト」を持つタイプにはしばしば役に立ちます。たとえば、リスト内の索引付け場所には単一の番号が必要ですが、ツリーでは各ノードのパスを示す番号が必要です。

class Container f where
  data Location f
  get :: Location f -> f a -> Maybe a

instance Container [] where
  data Location [] = ListLoc Int
  get (ListLoc i) xs
    | i < length xs  = Just $ xs!!i
    | otherwise      = Nothing

instance Container Tree where
  data Location Tree = ThisNode | NodePath Int (Location Tree)
  get ThisNode (Node x _) = Just x
  get (NodePath i path) (Node _ sfo) = get path =<< get i sfo

インジェクション

タイプファミリは必ずしも注入型ではありません。したがって、アプリケーションからパラメータを推論することはできません。例えば、ではservant 、型指定されたServer a私たちは型推論することはできませんa 。この問題を解決するために、 Proxyを使用することができます。たとえば、 servantでは、 serve関数の型は... Proxy a -> Server a -> ... 。私たちは推測することができますからa Proxy aのでProxyで定義されdata単射です。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow