haskell中一般使用data关键字来自定义type,像这样:

data BookInfo = Book Int String [String] deriving (Show)

但有些情况下要使用newtype来定义, 举个例子,对于数字来说,它有两种选择可以表现为一个monoid,一个是 * 作为二元函数,1 作为identity, 另外一种是 + 作为二元函数,0 作为identity。那么问题来了怎么把这两种选择都实现 (这里所说的实现是指把一个数字实现为Monoid这个typeclass的instance) 呢?

Data.Monoid 这个模块导出了两个类型:ProductSum 。Product的定义如下:

Prelude Data.Monoid> :i Product
newtype Product a = Product {getProduct :: a}

Sum的定义如下:

Prelude Data.Monoid> :i Sum
newtype Sum a = Sum {getSum :: a}

Product的Monoid的instance实现:

instance Num a => Monoid (Product a) where  
    mempty = Product 1  
    Product x `mappend` Product y = Product (x * y)

很显然它将第一种选择即乘法实现了。它代表 Product a 对于所有属于 Numa 是一个 Monoid

为什么要用newtype呢?

因为newtype比较快。 如果用data的话在执行的时候会有包起来和解开来的成本,但使用newtype的话,Haskell会知道你只是要将一个type包成一个新的type,你想要内部运作完全一样只是要一个新type而已。有了这个概念,Haskell可以将包裹和解开的成本省掉。

为什么不能所有地方都用newtype呢,是因为当使用newtype来制作一个新type的时候,只能有一个值构造器,而且这个值构造器只能有一个字段。