类型同义词对类型类实例的影响是什么? GHC中的TypeSynonymInstances pragma有什么作用?

时间:2010-01-24 02:32:16

标签: typeclass haskell

我正在阅读真实世界Haskell 第151页,我已经盯着以下段落一个多小时了:

  

回想一下,String是一个同义词   [Char],反过来又是[a]的类型   其中Char代替了类型   参数a。根据Haskell 98的说法   规则,我们不允许提供   键入时代替类型参数   我们写一个实例。换一种说法,   写一篇文章对我们来说是合法的   [a]的实例,但不是[Char]的实例。   16条评论5335

它只是没有下沉。盯着the (free not pirated) copy of RWH chapter 6我看到很多的其他人真的很痛苦。我仍然不理解评论......

首先,关于这一切的一切都让我感到困惑,所以如果您觉得可以解释有关此段落的任何内容,请{I} TypeSynonymInstances

这是我的问题:

  • Int数据构造函数
  • String数据构造函数 AND 类型同义词

现在我无法回答这些问题:

  1. 为什么类型同义词会排除使类型成为类型类的成员(我正在寻找可能与类型同义词的编译或实现有关的某些原因)?
  2. 为什么语言的设计者不想要这种语法(我要求推理不是广泛的理论或unicode数学符号)。
  3. 我看到这行“类型[a],其中Char替换了类型参数a”,我想知道为什么我不能用它代替“键入一个其中Int替换类型参数a“
  4. 谢谢!

4 个答案:

答案 0 :(得分:27)

我认为问题的一部分是两个很大程度上不相关的限制:

  • 没有类型同义词实例意味着实例只能是使用datanewtype声明的内容,而不是type。这禁止String,但不禁止[Char]
  • 没有灵活的实例意味着实例只能提到一个不是变量的类型,只有该类型可以用作类型构造函数。这禁止Maybe Intf Int,但不禁止Maybe a

以下是GHCi对IntCharString所说的内容:

data Char = GHC.Types.C# GHC.Prim.Char#
data Int = GHC.Types.I# GHC.Prim.Int#
type String = [Char]

IntChar都是没有类型变量参数的简单类型;没有涉及类型构造函数,因此您可以非常自由地使用它们创建实例。

然而,

字符串在两个计数上失败。它是一个类型的同义词,这是不允许的,它也是应用于非变量的类型构造函数,即应用于Char的列表类型构造函数。

为了进行比较,请注意[a]Maybe aEither a b在实例中均有效,但[Int]Maybe [a]和{{1}被禁止;希望你现在能明白为什么。

关于你的直接问题,我不知道用这种方式设计语言的原始动机是什么,而且我没有资格做出关于“最佳实践”的权威性陈述,而是我自己的个人编码我真的不犹豫地使用这些pragma:

Either String a

你总是可以去看packages that use pragmas。看起来,灵活的实例确实得到了相当多的使用,并且来自“可敬的”软件包(例如,在Parsec的源代码中有几个点击)。

答案 1 :(得分:10)

实际上,IntString都不是数据构造函数。也就是说,您不能使用它们来创建

的值
> (Int 42, String "bob")
<interactive>:1:1: Not in scope: data constructor `Int'
<interactive>:1:9: Not in scope: data constructor `String'

Int命名一种新的,不同的代数数据类型。 String是已存在类型的“类型同义词”或别名:[Char]。问题是Haskell 98说你不能在实例声明中使用类型同义词。

我不能说为什么Haskell 98报告的作者在这种情况下选择限制类型同义词。它们有很多限制。例如,它们不能被部分应用(如果它们采用类型参数)。我认为§4.2.2结尾处有一条线索:

  

类型同义词很方便,但是   严格的句法,制作机制   类型签名更具可读性。一个   同义词及其定义是   完全可以互换,除了   实例的实例类型   声明(第4.3.2节)。

据推测,有一种程序编译方法,这种语法可互换性会导致实例出现问题。也许它与它们从包中泄漏的实例的显着方面有关...

至于你的上一个问题,我认为这个解释会混淆两件事:1)String[Char]的类型同义词,而[a]又是一般类型{{1}的特化2)即使没有同义词,[Char]也不能在实例的头部使用。

第二个问题与类型同义词无关,但是实例头必须将类型构造函数的所有类型参数都设置为变量,而不是具体类型。也就是说,您无法为某些类定义[Int][Char]的单独实例,您只能定义实例[a]。 (请记住,尽管语法很方便,[]是一个类型构造函数,里面的东西是类型参数。)

同样,我不知道为什么报告会限制这些,但我怀疑它也与编译策略有关。由于GHC的实例编译策略可以处理这个问题,因此您可以通过-XFlexibleInstances在GHC中放宽此约束。

最后,我看到两个扩展都在相当多的代码中启用,但也许具有更多Haskell经验的人可以权衡它们是否是“最佳实践”或不是。

答案 2 :(得分:1)

Haskell 98

  1. 实例头必须具有C(T u1 ... uk)形式,其中T是由数据或newtype声明定义的类型构造函数(参见TypeSynonymInstances),ui是不同的类型变量,并且
  2. 上下文中的每个断言必须具有C'v形式,其中v是ui之一。
  3. 因此使用instance ClassName TypeConstructor where和TypeConstructor 必须IntDouble[a]是有效的,请确保只有[]涉及类型构造函数!!

    BTW,[TypeConstructor]是类型构造函数,因此无法使用[NewType],但允许使用[TypeVariable]和{{1}}。

    这是Haskell的限制,我们可以通过启用FlexibleInstances来避免它。

答案 3 :(得分:0)

Int和String是类型,而不是数据构造者。字符串恰好是[Char]的别名,也可以写为List Char。数据构造函数类似于Just,因此Just 3是Maybe Int类型的值。这里解释了类型同义词实例:

http://hackage.haskell.org/trac/haskell-prime/wiki/TypeSynonymInstances