假设我们有以下HList定义:
data HL spec where
HLNil :: HL ()
HLCons :: h -> HL t -> HL (h, t)
是否有可能以某种方式对其项目强制执行共享约束?
例如,以下是我尝试将项目限制为Show
个实例,但Couldn't match type `Char' with `Int'
失败:
class HLSpecEach spec item
instance HLSpecEach () item
instance (HLSpecEach t item, h ~ item) => HLSpecEach (h, t) item
a :: (Show item, HLSpecEach spec item) => HL spec -> Int
a = undefined
b :: HL (Int, (Char, ()))
b = undefined
c = a b
答案 0 :(得分:4)
如果您有约束种类和类型系列,则很容易做到。首先,我想说我更倾向于使用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
如果你不使用新的扩展,那是可能的,但更加丑陋。一种选择是为所有内容定义自定义类
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))
应该(未经测试)与任何GHC一起使用GADT,MPTC,灵活上下文/实例和种类签名(您可以轻松摆脱最后一个)。
编辑:在GHC 7.6+中你应该使用
type family ConstrainAll (c :: k -> Constraint) (ls :: [k]) :: Constraint
(k
而不是*
)并启用PolyKinds,但这不适用于PolyKinds的GHC 7.4实现(因此是单态代码)。以同样的方式,定义
data HList f ls where
HNil :: HList f '[]
HCons :: !(f x) -> !(HList f xs) -> HList f (x ': xs)
可以避免代码重复,如果你想要一个懒惰的vs严格的HLists,或者你想要一个字典列表,或者更高级别的类型的通用变体等等。