我写这个的时候有什么不同?
data Book = Book Int Int
与
newtype Book = Book (Int, Int) -- "Book Int Int" is syntactically invalid
我写这个的时候有什么不同?
data Book = Book Int Int
与
newtype Book = Book (Int, Int) -- "Book Int Int" is syntactically invalid
好问题!
有几个关键的区别。
表示
newtype保证您的数据在运行时具有与所包装的类型完全相同的表示形式。 While data在运行时声明了一个全新的数据结构。
因此,这里的关键点是保证在编译时擦除newtype的构造。
例子:
data Book = Book Int Int
newtype Book = Book (Int, Int)
注意它与a (Int,Int)的表示完全相同,因为Book构造函数是擦除的。
data Book = Book (Int, Int)
具有新类型中没有的附加Book构造函数。
data Book = Book {-# UNPACK #-}!Int {-# UNPACK #-}!Int
没有指针!这两个Int字段是Book构造函数中未装箱的单词大小的字段。
代数数据类型
由于需要删除构造函数,newtype仅在使用单个构造函数包装数据类型时有效。没有“代数”新类型的概念。也就是说,你不能写一个新类型的等价物,
data Maybe a = Nothing
| Just a
因为它有多个构造函数。你也不会写字
newtype Book = Book Int Int
严格
构造函数被擦除的事实导致data和newtype之间在严格程度上有一些非常细微的差异。特别是,数据引入了一种“提升”的类型,这意味着,从本质上讲,它有一种额外的方法来计算底部值。因为在运行时没有附加的newtype构造函数,所以这个属性不成立。
Book to(,)构造函数中的额外指针允许我们放入一个底值。
因此,newtype和data具有稍微不同的严格性属性,正如Haskell wiki文章中所解释的那样。
Unboxing
对newtype的组件进行拆箱是没有意义的,因为没有构造函数。尽管这样写是完全合理的:
data T = T {-# UNPACK #-}!Int
生成一个带有T构造函数和Int#组件的运行时对象。你只是用newtype得到一个纯整型。
引用:
Haskell wiki上的“Newtype” Norman Ramsey关于严格性的回答
它们在语义上有所不同。
data定义了一个GADT(产品类型,和类型等) Newtype定义了一个同构。
当你不关心它是否同构时,你应该使用数据,即使它只有一个字段。
例如,
data Student = Student {
age :: Int
}
如果在这个问题域中,年龄是您必须处理的关于学生的唯一信息,那么您应该使用data而不是newtype,因为您从来没有说过学生应该与年龄同构。