我一直在玩实现可以不同索引的列表的包装器。
这种包装的基本类:
class List l where
toList :: l a -> [a]
支持索引的包装器:
class Indexed l where
indexed :: (Ix i, List (l i)) => l i a -> [(i, a)]
-- i - type of index
-- l - type of wrapper
可以按Integral
类型索引的列表包装:
data IdxByIntList i a = (Ix i, Integral i) => IdxByIntList { getList :: [a] }
如果IdxByIntList
是List
的实例,那么i
是Integral
和Ix
:
instance (Integral i, Ix i) => List (IdxByIntList i) where
toList = getList
看起来(Ix i, Integral i)
函数中的约束indexed
已得到满足,并且可以使IdxByIntList
成为Indexed
的实例:
instance Indexed IdxByIntList where
indexed = zip [0..] . getList
但它无法编译,因为编译器无法推断
类型变量i
是Enum
和Num
的实例。
在indexed
的{{1}}中,IdxByIntList
的约束[0..]
会转到(Num a, Enum a)
,依此类推,然后转到zip [0..]
。因此,indexed
应该有约束indexed
,但它不是。
(Ix i, Enum i, Num i, List (l i))
是i
的实例,因此它是Integral
和Num
的实例。
我希望Enum
对其所有实例都有一般约束
有额外的约束,取决于具体的实例。在此,我希望Indexed
在IdxByIntList
上具有约束(Ix i, List (l i), Integral i)
。我怎么能这样做?
答案 0 :(得分:3)
如何将课程改为
{-# LANGUAGE TypeFamilies #-}
import Data.Ix
class Indexed li where
indexed :: li ~ l i => li a -> [(i, a)]
-- i - type of index
-- l - type of wrapper
data IdxByIntList i a = IdxByIntList { getList :: [a] }
instance (Integral i, Ix i) => Indexed (IdxByIntList i) where
indexed = zip [0..] . getList
这使得class参数应用于l i
,而不仅仅是l
,这可能足以供您使用。
如果你真的做需要在l
上对参数进行参数化,事情会变得更加棘手;我认为您需要一个类型* -> Constraint
的关联类型系列,它需要ConstraintKinds
扩展名,当涉及多个类约束时,表达族实例会很尴尬。 (我不知道它在实践中是否能令人满意。)