2009-09-07 [長年日記]

[Haskell] Type Families

Data Familiesは、型パラメータに依って実装を変えたいときに使えます。C++のテンプレートでの特殊化みたいなものでしょうか*1

{-# LANGUAGE TypeFamilies #-}
import qualified Data.Sequence as Seq

data family Vector a

-- BoolのVectorはリストで表現 
data instance Vector Bool = BoolVector [Bool]
-- IntのVectorはSeqで表現
data instance Vector Int = IntVector (Seq.Seq Int)

-- 関数はクラスにする必要があります
class VectorElem a where
  add :: a -> Vector a -> Vector a
  first :: Vector a -> a

instance VectorElem Bool where
  add v (BoolVector l) = BoolVector $ v:l
  first (BoolVector l) = head l

instance VectorElem Int where
  add v (IntVector s) = IntVector $ v Seq.<| s
  first (IntVector s) = Seq.index s 0

はじめからクラスの中にdata宣言を書くことも出来ます。

class VectorElem a where
  data Vector a
  add :: a -> Vector a -> Vector a
  first :: Vector a -> a

instance VectorElem Bool where
  data Vector Bool = BoolVector [Bool]
  add v (BoolVector l) = BoolVector $ v:l
  first (BoolVector l) = head l

instance VectorElem Int where
  data Vector Int = IntVector (Seq.Seq Int)
  add v (IntVector s) = IntVector $ v Seq.<| s
  first (IntVector s) = Seq.index s 0

classを伴わないdata familyの宣言が実際にどういう場面で使われるのか良くわからないのですが、上の例のようなケースではclass宣言内に書いた方がわかりやすい気がします。

*1  もっともデフォルトの実装は持てませんけど

[Haskell] Type Families (2)

上の例だと、GADTを使っても同じような事ができます。

{-# LANGUAGE GADTs #-}
import qualified Data.Sequence as Seq

data Vector a where
  BoolVector :: [Bool] -> Vector Bool
  IntVector :: Seq Int -> Vector Int

add :: a -> Vector a -> Vector a
add v (BoolVector l) = BoolVector $ v:l
add v (IntVector s) = IntVector $ v Seq.<| s

こっちの場合だと、関数は単なるパターンマッチで書くことができるので型クラスにする必要はありません。

Data Familiesはオープンなので他のモジュールで新しい型に適用する事ができますが、GADTの場合にはデータコンストラクタを変更しないと新しい型を追加する事ができません。

また、(たぶん)GADTの場合、引数に対象の型が無いと実装を別ける事ができませんが、Data Familiesの場合には可能です。

例えば、GADTでは、

empty :: Vector a

という関数は実装できませんが、Data Familiesの場合には、

class VectorElem a where
  data Vector a
  empty :: Vector a

instance VectorElem Bool where
  data Vector Bool = BoolVector [Bool]
  empty = BoolVector []

instance VectorElem Int where
  data Vector Int = IntVector (Seq.Seq Int)
  empty = IntVector Seq.empty

のように定義することができます。

[]

トップ «前の日記(2009-08-31) 最新