因此,C a Bool
形式的类型类有许多优点。主要是因为当正常C a
隐式地对所有内容进行AND时,它们允许您在两个约束之间进行任何逻辑运算。
如果我们考虑~
类约束,可以这样做
class Equal x y b | x y -> b
instance Equal x x True
instance False ~ b => Equal x y b
但是,这个案例的特殊之处在于,将x x
放在实例的头部相当于头部中的x ~ y =>
然后x y
。任何其他类型类都不是这种情况。
因此,如果我们尝试为类C
执行类似操作,我们会得到类似
class C' x b | x -> b
instance C x => C' x True
instance False ~ Bool => C' x b
不幸的是,这不起作用,因为只会选择其中一个实例,因为它们不会区分类型x
,所以任何类型都匹配两个头。
我还阅读了https://www.haskell.org/haskellwiki/GHC/AdvancedOverlap,它再次不适用于任何类C
,因为它要求您重写原始类的所有实例。理想情况下,我希望我的代码可以与GHC.Exts.Constraint
和KindSignatures
一起使用,以便C
可以参数化。
所以,对于像这样的课程
class Match (c :: * -> Constraint) x b | c x -> b
我如何编写实例,以便Match c x True
当且仅当c x
,Match c x False
时才会这样做?
答案 0 :(得分:1)
由于所谓的开放世界假设,这在Haskell中是不可能的。它声明类型类的实例集是打开的,这意味着您可以随时创建新实例(而不是封闭的世界,其中必须有一组固定的实例)。例如,虽然在Prelude中定义了Functor
类型类,但我仍然可以在我自己的代码中为它创建实例,而不是在Prelude中。
为了实现您提出的建议,编译器需要一种方法来检查类型T
是否是类C
的实例。但是,这需要编译器知道该类的所有可能实例,并且由于开放世界的假设而无法实现(在编译Prelude时,编译器仍然知道您以后会生成{{1}也是YourOwnFunctor
的一个实例。
使其工作的唯一方法是仅考虑当前可见的实例(因为它们是在当前模块或其任何导入中定义的)。但这会导致非常不可预测的行为:可见实例集不仅取决于模块的导入,还取决于导入的导入,因为您无法隐藏实例。因此,代码的行为将取决于依赖项的实现细节。
如果你想要一个封闭的世界,你可以使用GHC 7.8中引入的closed type families。使用它们,你可以写:
Functor