Haskell类型系列,了解错误信息

Cli*_*ton 5 haskell type-families

在尝试使用时Data.Has,我一直在编写如下代码:

data Name = Name; type instance TypeOf Name = Text
type NameRecord = FieldOf Name;
Run Code Online (Sandbox Code Playgroud)

我发现:

instance I NameRecord where
  ...
Run Code Online (Sandbox Code Playgroud)

引发编译错误,即:

实例中的非法类型同义词系列应用程序

鉴于:

instance (NameRecord ~ a) => I a where
  ...
Run Code Online (Sandbox Code Playgroud)

编译好.

我认为该错误与GHC中的票证有关,标记为无效.

对机票的回应说:

我不确定你在暗示什么.我们无法自动转换

instance C (Fam Int) -- (1)
Run Code Online (Sandbox Code Playgroud)

instance (Fam Int ~ famint) => C famint -- (2)
Run Code Online (Sandbox Code Playgroud)

如果只有一个实例,则此方法有效,但只要有两个这样的实例,它们就会重叠.

也许你建议我们应该这样做,程序员应该只考虑隐式转换.我认为这不是一个好主意.令人困惑的是很少的好处(因为你总是可以轻松地自己编写转换后的实例).

有人可以详细说明这个解释,可能有一些示例代码,其中(1)失败,但(2)没有,为什么?

dfl*_*str 4

(1) 失败但 (2) 没有失败的情况是微不足道的;因为type ExampleOfATypeSynonym = ...实例声明中不允许使用类型同义词 ( ),但在约束中允许使用类型同义词 ( ),因此在只有一个实例的任何情况下,如下所示:

-- (1)
class Foo a
type Bla = ()
instance Foo Bla
Run Code Online (Sandbox Code Playgroud)

...可以转化为:

-- (2)
class Foo a
type Bla = ()
instance (a ~ Bla) => Foo a
Run Code Online (Sandbox Code Playgroud)

(1) 失败的唯一原因是实例声明中不允许使用类型同义词,这是因为类型同义词就像类型函数:它们提供从类型名称到类型名称的单向映射,因此如果您有type B = A和 an ,创建instance Foo B的实例是不明显的。Foo A该规则的存在使得您必须编写instance Foo A以明确是实际获取实例的类型。

在这种情况下,类型族的使用是无关紧要的,因为问题在于您正在使用类型同义词,即类型NameRecord。你还必须记住,如果类型同义词被删除并FieldOf Name直接替换,编译仍然会失败;这是因为“类型族”只是类型同义词的增强版本,因此在这种情况FieldOf Name下也是“类型同义词” Name :> Text。您必须使用数据族和数据实例来获得“双向”关联。

有关数据系列的更多信息可以在GHC 文档中找到。


我认为你的意思是“......其中(2)失败但(1)没有......”

假设我们有一个像这样的类型类:

class Foo a where
  foo :: a
Run Code Online (Sandbox Code Playgroud)

现在,您可以像这样编写实例:

 instance Foo Int where
   foo = 0

 instance Foo Float where
   foo = 0

 main :: IO ()
 main = print (foo :: Float)
Run Code Online (Sandbox Code Playgroud)

这正如人们所期望的那样工作。但是,如果将代码转换为:

{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
class Foo a where
  foo :: a

instance (a ~ Int) => Foo a where
  foo = 0

instance (a ~ Float) => Foo a where
  foo = 0

main :: IO ()
main = print (foo :: Float)
Run Code Online (Sandbox Code Playgroud)

它无法编译;它显示错误:

test.hs:5:10:
    Duplicate instance declarations:
      instance a ~ Int => Foo a -- Defined at test.hs:5:10-27
      instance a ~ Float => Foo a -- Defined at test.hs:8:10-29
Run Code Online (Sandbox Code Playgroud)

所以,这就是您希望寻找的示例。现在,只有当有多个实例Foo使用这一技巧时才会发生这种情况。这是为什么?

GHC解析类型类时,只查看实例声明头;即它忽略 之前的所有内容=>。当它选择了一个实例时,它“提交”给它,并检查之前的约束以=>查看它们是否为真。因此,首先它看到两个实例:

instance Foo a where ...
instance Foo a where ...
Run Code Online (Sandbox Code Playgroud)

显然不可能仅根据这些信息来决定使用哪个实例。

  • 原因是 Haskell 报告要求它这样做。**这样做**的原因是,如果它不这样做,就必须有一种算法来给出“类型与约束的拟合程度”的启发式算法;你必须争论“是的,这种类型适合这个类实例,但其他实例更适合,因为{更少的约束,更短的减少距离,......}”。开发这样的启发式方法也许是可能的,但它会打破开放世界假设,而开放世界假设是类型类的一个关键概念。 (3认同)