如何使类函数的约束取决于类实例?

时间:2015-03-16 19:31:46

标签: haskell types

我一直在玩实现可以不同索引的列表的包装器。

这种包装的基本类:

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] }

如果IdxByIntListList的实例,那么iIntegralIx

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

但它无法编译,因为编译器无法推断 类型变量iEnumNum的实例。

编辑:

indexed的{​​{1}}中,IdxByIntList的约束[0..]会转到(Num a, Enum a),依此类推,然后转到zip [0..]。因此,indexed应该有约束indexed,但它不是。

(Ix i, Enum i, Num i, List (l i))i的实例,因此它是IntegralNum的实例。

我希望Enum对其所有实例都有一般约束 有额外的约束,取决于具体的实例。在此,我希望IndexedIdxByIntList上具有约束(Ix i, List (l i), Integral i)。我怎么能这样做?

1 个答案:

答案 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扩展名,当涉及多个类约束时,表达族实例会很尴尬。 (我不知道它在实践中是否能令人满意。)