2

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

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

现在我有了这个类型类,它具有默认实现的方法,但通过添加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

4

1 回答 1

1

哇!我打算将此标记为与空实例有什么区别?DeriveAnyClass,但是自从提出并回答了这个问题以来,GHC 的行为似乎发生了变化!

无论如何,如果你问 - 无论是:i在 ghci 内部还是-ddump-deriv在启动 ghci 之前 - 编译器做了什么,很明显你的情况有什么区别:

> :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 手册对此的说明,尽管我怀疑那里的描述不是很精确或不完整,因为如果我严格遵守在文档。(我怀疑真正的答案是它首先编写实例,然后只是进行通常的类型推断并将它以这种方式发现的任何约束复制到实例上下文中。)

于 2019-06-14T14:44:06.240 回答