我写这个的时候有什么不同?

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,因为您从来没有说过学生应该与年龄同构。