DeriveAnyClass 与空实例

DeriveAnyClass vs Empty Instance

假设我有这个类型族,如果传递给它的类型不是记录,它会在编译时抛出自定义类型错误:

type family IsRecord (a :: Type) where
  ...

现在我有了这种类型 class,它具有默认实现的方法,但通过添加 IsRecord 约束要求该类型是记录:

class IsRecord a => Foo a where
  foo :: Text
  foo = "foo"

当尝试错误使用它时,如果我们将其用作类型不是记录的常规实例,它会成功编译失败:

data Bar = Bar

instance Foo Bar   -- error: Bar is not a record

但是如果我启用 -XDeriveAnyClass 并将其添加到派生子句中,这不会编译失败,完全忽略约束:

data Bar = Bar
  deriving (Foo)

我知道 DeriveAnyClass 生成一个空的实例声明,这是我在第一个示例中所做的,但它仍然没有抛出错误。怎么回事?

我正在使用 GHC 8.6.4

哇!我打算将其标记为 的副本,但自从提出并回答了该问题后,GHC 的行为似乎发生了变化!

无论如何,如果你问——无论是在 ghci 中使用 :i 还是在启动 ghci 之前使用 -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 手册对它的描述 here,尽管我怀疑那里的描述不是很准确就是不完整,因为如果我严格遵守,我不会得到编译器在这里所​​做的相同答案文件中规定的规则。 (我怀疑 true 的答案是它首先写入实例,然后只进行通常的类型推断,并将以这种方式发现的任何约束复制到实例上下文中。)