Haskell中type和newtype的区别

4

从《Haskell编程》(http://www.cs.nott.ac.uk/~gmh/book.html)一书中定义了一个解析器:

> type Parser a = String -> [(a,String)]

然而,从示例代码(http://www.cs.nott.ac.uk/~gmh/Parsing.lhs)来看,定义略有不同。

> newtype Parser a              =  P (String -> [(a,String)])

我从这个页面https://wiki.haskell.org/Type#Type_and_newtype找到了以下不同:

type引入了类型的同义词并使用相同的数据构造函数。newtype引入了类型的重命名,需要您提供新的构造函数。

以下是我的问题:

  1. 为什么在new type中要使用 P(...) 来括起内容?
  2. 使用newtype需要提供新的构造函数,但是我在示例代码中找不到一个。如何定义newtype的构造函数?不提供构造函数可以吗?

3
P 是新的构造函数。 - Daniel Wagner
1个回答

10
对于新类型,为什么要使用P(...)来封装内容?
任何声明的newtype类型必须只有一个构造函数。在这种情况下,构造函数名为P,其类型为:
P :: (String -> [(a, String)]) -> Parser a

您也可以使用记录语法,这样就相当于:
newtype Parser a = P { runParser :: String -> [(a, String)] }

如果您使用 data 类型(在这种情况下,包装器不会像运行时那样轻易地被优化掉),则非常相似:

data Parser a = P { runParser :: String -> [(a, String)] }

需要为newtype提供新的构造函数,但我在示例代码中找不到一个。如何为newtype定义构造函数?不提供构造函数可以吗?
如上所述,newtype Parser的构造函数名为P,并且newtype必须有一个且仅有一个构造函数。
更进一步的解释:关键字type构建了一个简单的别名。例如,String被定义为
type String = [Char]

您可以在任何需要[Char]的函数中使用String,也可以在任何需要String的函数中使用[Char]。由于FilePath被定义为

type FilePath = String

然后,您可以在任何使用`String`和`[Char]`的地方使用`FilePath`,反之亦然。这些只是为了缩小更复杂类型的名称。实际上,在实践中,除非在非常简单的情况下,否则更常见使用`newtype`而不是`type`,因为它允许类型系统更好地强制执行您软件中的内容。使用`type`存在一些缺点,例如需要语言扩展才能将它们用于`instance`声明,并且它们必须始终完全应用于其他表达式中。而`newtype`的缺点是您需要频繁进行包装/解包,但在处理真正复杂的程序时,这可能更有益。

1
由于它必须被充分应用,因此将type版本实例化为更高级别的类(例如Monad)也根本不可能。 - Ørjan Johansen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接