制約の種類と型ファミリがあれば簡単に実行できます。DataKinds
まず、明確にするために使用することを好むと言いましょう
data HList ls where
HNil :: HList '[]
HCons :: x -> HList xs -> HList (x ': xs)
type family ConstrainAll (c :: * -> Constraint) (ls :: [*]) :: Constraint
type instance ConstrainAll c '[] = ()
type instance ConstrainAll c (x ': xs) = (c x, ConstrainAll c xs)
showAll :: ConstrainAll Show xs => HList xs -> [String]
showAll HNil = []
showAll (HCons x xs) = (show x) : showAll xs
新しい拡張機能を使用しない場合は可能ですが、はるかに醜いです。1 つのオプションは、すべてのカスタム クラスを定義することです。
class ShowAll ls where
showAll :: HList ls -> [Show]
instance ShowAll () where
showAll _ = []
instance (ShowAll xs, Show x) => ShowAll (x,xs)
showAll (HCons x xs) = (show x) : (showAll xs)
私は醜いと思います。より賢いアプローチは、制約の種類を偽造することです
class Constrained tag aType where
isConstained :: tag aType
data HListT tag ls where
HNilT :: HListT tag ()
HConsT :: x -> tag x -> HListT tag xs -> HListT tag (x,xs)
data Proxy (f :: * -> *) = Proxy
class ConstainedAll tag ls where
tagThem :: Proxy tag -> HList ls -> HListT tag ls
instance ConstainedAll tag () where
tagThem _ _ = HNilT
instance (ConstainedAll tag xs, Constrained tag x) => ConstainedAll tag (x,xs) where
tagThem p (HCons x xs) = HConsT x isConstained (tagThem p xs)
その後、次のように使用できます
data Showable x where Showable :: Show x => Showable x
instance Show x => Constrained Showable x where isConstained = Showable
--inferred type showAll' :: HListT Showable xs -> [String]
showAll' HNilT = []
showAll' (HConsT x Showable xs) = (show x) : showAll' xs
--inferred type: showAll :: ConstainedAll Showable xs => HList xs -> [String]
showAll xs = showAll' (tagThem (Proxy :: Proxy Showable) xs)
example = showAll (HCons "hello" (HCons () HNil))
GADT、MPTC、Flexible Contexts/Instances、および Kind Signatures を備えた任意の GHC で動作するはずです (最後のものは簡単に取り除くことができます)。
編集:GHC 7.6+では、使用する必要があります
type family ConstrainAll (c :: k -> Constraint) (ls :: [k]) :: Constraint
(k
の代わりに*
) を使用して PolyKinds をオンにしますが、これは GHC 7.4 の PolyKinds の実装では機能しません (したがって、モノモーフィック コードになります)。同様に、
data HList f ls where
HNil :: HList f '[]
HCons :: !(f x) -> !(HList f xs) -> HList f (x ': xs)
怠惰な HList と厳密な HList のようなものが必要な場合、または辞書のリストが必要な場合、またはより高い種類の型のユニバーサルバリアントなどが必要な場合に、コードの重複を回避できます。