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

时间:2012-05-07 07:33:14

标签: haskell type-families

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

data Name = Name; type instance TypeOf Name = Text
type NameRecord = FieldOf Name;

我找到了:

instance I NameRecord where
  ...

引发编译错误,即:

  

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

鉴于:

instance (NameRecord ~ a) => I a where
  ...

编译好。

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

对机票的回复说:

  

我不确定你在暗示什么。我们不能自动   变换

instance C (Fam Int) -- (1)
     

instance (Fam Int ~ famint) => C famint -- (2)
     

如果只有一个实例,则会有效,但只要有两个实例   在这种情况下,它们总是重叠。

     

也许你建议我们无论如何都应该这样做和程序员   应该只考虑隐式转换。我不   认为这是个好主意。令人困惑的是,收益甚微   (因为你总是可以自己编写变换后的实例   努力)。

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

1 个答案:

答案 0 :(得分:4)

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

-- (1)
class Foo a
type Bla = ()
instance Foo Bla

......可以转化为:

-- (2)
class Foo a
type Bla = ()
instance (a ~ Bla) => Foo a

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

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

有关数据系列的更多信息,请参阅GHC documentation


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

让我们假设我们有一个类似的类:

class Foo a where
  foo :: a

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

 instance Foo Int where
   foo = 0

 instance Foo Float where
   foo = 0

 main :: IO ()
 main = print (foo :: Float)

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

{-# 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)

它不编译;它显示错误:

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

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

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

instance Foo a where ...
instance Foo a where ...

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