Haskell DeriveAnyClass与空实例
假设我有一个类型族,如果传递给它的类型不是记录,它会在编译时抛出一个自定义类型错误:Haskell DeriveAnyClass与空实例,haskell,ghc,typeclass,type-families,deriving,Haskell,Ghc,Typeclass,Type Families,Deriving,假设我有一个类型族,如果传递给它的类型不是记录,它会在编译时抛出一个自定义类型错误: type系列IsRecord(a::type)其中 ... 现在,我有了这个类型类,它有默认实现的方法,但通过添加IsRecord约束,要求该类型是记录: class IsRecord a=>Foo a其中 foo::Text foo=“foo” 当试图错误地使用它时,如果我们将其作为非记录类型的常规实例使用,它将成功编译失败: data Bar=Bar 实例Foo Bar--错误:Bar不是记录 但是如
type系列IsRecord(a::type)其中
...
现在,我有了这个类型类,它有默认实现的方法,但通过添加IsRecord
约束,要求该类型是记录:
class IsRecord a=>Foo a其中
foo::Text
foo=“foo”
当试图错误地使用它时,如果我们将其作为非记录类型的常规实例使用,它将成功编译失败:
data Bar=Bar
实例Foo Bar--错误:Bar不是记录
但是如果我启用-xderivanyclass
并将其添加到派生子句中,这不会导致编译失败,完全忽略约束:
data Bar=Bar
派生(Foo)
我知道,DeriveAnyClass
生成一个空实例声明,这就是我在第一个示例中所做的,但它仍然不会抛出错误。发生什么事了
我正在使用GHC 8.6.4哇!我本打算将此标记为的副本,但似乎GHC的行为自该问题被问到和回答后发生了变化 无论如何,如果您在启动ghci之前用ghci内部的
:i
或用-ddump deriv
询问编译器做了什么,那么很明显,在您的情况下有什么不同:
> :i Bar
data Bar = Bar -- Defined at test.hs:15:1
instance IsRecord Bar => Foo Bar -- Defined at test.hs:16:13
实际上,如果您更改代码的非DeriveAnyClass版本以匹配
instance IsRecord Bar => Foo Bar
而不是
instance Foo Bar
一切正常。如何选择该实例上下文的细节似乎有点复杂;您可以阅读GHC手册中关于它的内容,尽管我怀疑其中的描述不是很精确就是不完整,因为如果我严格遵守文档中规定的规则,我不会得到编译器在这里给出的相同答案。(我怀疑真正的答案是,它首先编写实例,然后执行通常的类型推断操作,并将以这种方式发现的任何约束复制到实例上下文中。)它将“继承”约束,如
instance IsRecord Bar=>Foo Bar
。是的,但这不应该编译,对吗?为什么不?约束是错误的,但这不是问题。如果您稍后在使用该实例的模块中添加该实例,则该实例将变为“可用”。我知道实际上这不是问题,但我的问题是这两件事之间的区别。您只需说如果IsRecord Bar
保持不变,然后我们可以定义一个实例Foo Bar
,如该实例中指定的那样。因为现在该约束不成立,如果稍后添加实例(例如在另一个模块中),则该约束为true,因此您将获得一个实例。因此,它增加了额外的灵活性。