Real World Haskell
的第321页有这些代码,
...
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype AInt = A { unA::Int }
deriving (Show, Eq, Num)
instance Monoid AInt where
mempty = 0
我的困惑是为什么
mempty = 0
但不是
mempty = A 0
我也注意到了两者
ghci> 0 :: AInt
和
ghci> A 0 :: AInt
给我相同的回复
A { unA = 0 }
有人请告诉我这两个之间的区别是什么?
答案 0 :(得分:14)
这里的诀窍是GeneralizedNewtypeDeriving
扩展名。特别是,只要基础类型是实例,这就允许我们为newtype
派生任何类。所有这一切都是将实例从旧类型复制到新类型。
在这种特殊情况下,AInt
派生Num
。这意味着AInt
是Num
的实例,使用与Int
相同的代码(所有内容都包含在A
构造函数中)。这包括Int
的{{1}}功能。
fromInteger
函数是根据fromInteger
的{{1}}定义的,如下所示:
Int
由于fromInteger
是多态的 - 它的类型为fromInteger i = A (fromInteger i)
- 它是0
中任何类型的有效常量。感谢newtype派生,这包括0 :: Num a => a
,使用上面的Num
函数。这意味着AInt
和fromInteger
之间确实没有区别。
答案 1 :(得分:12)
像0
这样的数字文字被重载并具有类型0 :: Num a => a
,这意味着它们可以是具有Num
实例的任何类型,具体取决于上下文。这是通过fromInteger
类型类中的Num
函数实现的,因此当您键入0
时,它会被视为您编写fromInteger 0
。
通过使用GeneralizedNewtypeDeriving
,GHC(实际上 1 )为您的班级写了Num
个实例,如下所示:
instance Num AInt where
fromInteger n = A (fromInteger n)
...
因此,当您撰写0 :: AInt
时,这会扩展为fromInteger 0 :: AInt
,这是(按照上面的定义)等于A (fromInteger 0)
,这与您编写的A 0
相同
1 GeneralizedNewtypeDeriving
实际上并没有写一个新的intance。它只是执行必要的强制转换以使用现有的强制转换。