haskell中一般使用data关键字来自定义type,像这样:
代码语言:javascript复制data BookInfo = Book Int String [String] deriving (Show)
但有些情况下要使用newtype来定义, 举个例子,对于数字来说,它有两种选择可以表现为一个monoid,一个是 *
作为二元函数,1
作为identity, 另外一种是
作为二元函数,0
作为identity。那么问题来了怎么把这两种选择都实现 (这里所说的实现是指把一个数字实现为Monoid这个typeclass的instance) 呢?
Data.Monoid
这个模块导出了两个类型:Product
和 Sum
。Product的定义如下:
Prelude Data.Monoid> :i Product
newtype Product a = Product {getProduct :: a}
Sum的定义如下:
代码语言:javascript复制Prelude Data.Monoid> :i Sum
newtype Sum a = Sum {getSum :: a}
Product的Monoid的instance实现:
代码语言:javascript复制instance Num a => Monoid (Product a) where
mempty = Product 1
Product x `mappend` Product y = Product (x * y)
很显然它将第一种选择即乘法实现了。它代表 Product a
对于所有属于 Num
的 a
是一个 Monoid
为什么要用newtype呢?
因为newtype比较快。 如果用data的话在执行的时候会有包起来和解开来的成本,但使用newtype的话,Haskell会知道你只是要将一个type包成一个新的type,你想要内部运作完全一样只是要一个新type而已。有了这个概念,Haskell可以将包裹和解开的成本省掉。
为什么不能所有地方都用newtype呢,是因为当使用newtype来制作一个新type的时候,只能有一个值构造器,而且这个值构造器只能有一个字段。