しないでください。v
すべてのインスタンスをクラスに追加すると、インスタンスListable
が重複するため、使用するのが面倒になります。
AVector v a => v
は、どの項目がリストの要素になり得るかによって制約されるため、リストと同型ではありません。この制約をキャプチャするクラスが必要です。
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Constraint
class ConstrainedList l where
type Elem l a :: Constraint
toList :: Elem l a => l a -> [a]
fromList :: Elem l a => [a] -> l a
ConstrainedList
すべてのタイプのインスタンスを追加Vector v a => v
してインスタンスの領域が重複するのではなく、関心のあるタイプに対してのみ定義します。以下Vector
では、ベクター パッケージ内のインスタンスを持つすべてのタイプをカバーします。
import qualified Data.Vector.Primitive as VP
import qualified Data.Vector.Generic as VG
instance ConstrainedList VP.Vector where
type Elem VP.Vector a = VG.Vector VP.Vector a
toList = VG.toList
fromList = VG.fromList
他のタイプのインスタンス
要素に空の制約のみを必要とするConstrainedList
通常のリストのインスタンスを作成できます。[]
instance ConstrainedList [] where
type Elem [] a = ()
toList = id
fromList = id
インスタンスを使用するtoList
、またはインスタンスfromList
も必要とする場所Elem l a
。
cmap :: (ConstrainedList l, Elem l a, Elem l b) => (a -> b) -> l a -> l b
cmap f = fromList . map f . toList
リストと要素の具体的な型がわかっている場合、これらの関数は、制約をいじることなく簡単に使用できます。
cmap (+1) [1,2,3,4]
ヒア・ビー・ドラゴンズ
次のことは試さないでください。追加の制約なしでリストに同型であるもののクラスに興味がある場合は、別のクラスを作成してください。これは、窮地に立たされたときにできること、つまりドラゴンを召喚することを示しています。
の要素に制約がないことの証明を必要とする関数を作成することもできますConstrainedList
。これはconstraints
、GHC で実際にはサポートされていないパッケージとプログラミング スタイルの領域からはずれていますが、十分なconstraints
例がないため、ここでは省略します。
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
map' :: forall l a b. (ConstrainedList l, () :=> Elem l a, () :=> Elem l b) =>
(a -> b) -> l a -> l b
map' f = case (ins :: () :- Elem l a) of { Sub Dict ->
case (ins :: () :- Elem l b) of { Sub Dict ->
fromList . map f . toList
}}
を確認するConstrainedList
だけで a に制約がないことを確認できElem l a ~ ()
ますが、その制約が別の方法で記述されている場合は機能しません。
{-# LANGUAGE FlexibleInstances #-}
class Any a
instance Any a
data AList a = AList {getList :: [a]}
deriving (Show)
instance ConstrainedList AList where
type Elem AList a = Any a
toList = getList
fromList = AList
()
と同じタイプではありませんがAny a
、を()
意味しAny a
ます。制約パッケージは、型クラスに具体化することで、このような関係をキャプチャしClass
、:=>
{-# LANGUAGE MultiParamTypeClasses #-}
-- class () => Any a
instance Class () (Any a) where
cls = Sub Dict
-- instance () => Any a
instance () :=> Any a where
ins = Sub Dict
そのすべての作業により、具体的なリスト型がわかっている場合、それらの辞書をすべて提供しなくても、関数を簡単に再利用できます。
map'' :: (a -> b) -> AList a -> AList b
map'' = map'