为什么在引用newtype包装数字类型时我可以省略构造函数?

时间:2013-04-16 04:17:26

标签: haskell typeclass newtype

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 }

有人请告诉我这两个之间的区别是什么?

2 个答案:

答案 0 :(得分:14)

这里的诀窍是GeneralizedNewtypeDeriving扩展名。特别是,只要基础类型是实例,这就允许我们为newtype派生任何类。所有这一切都是将实例从旧类型复制到新类型。

在这种特殊情况下,AInt派生Num。这意味着AIntNum的实例,使用与Int相同的代码(所有内容都包含在A构造函数中)。这包括Int的{​​{1}}功能。

fromInteger函数是根据fromInteger的{​​{1}}定义的,如下所示:

Int

由于fromInteger是多态的 - 它的类型为fromInteger i = A (fromInteger i) - 它是0任何类型的有效常量。感谢newtype派生,这包括0 :: Num a => a,使用上面的Num函数。这意味着AIntfromInteger之间确实没有区别。

答案 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。它只是执行必要的强制转换以使用现有的强制转换。