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

Zna*_*atz 10 haskell typeclass newtype

真实世界Haskell的第321页

有这些代码,

...

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype AInt = A { unA::Int }
    deriving (Show, Eq, Num)

instance Monoid AInt where
    mempty = 0
Run Code Online (Sandbox Code Playgroud)

我的困惑是为什么

mempty = 0
Run Code Online (Sandbox Code Playgroud)

但不是

mempty = A 0
Run Code Online (Sandbox Code Playgroud)


我也注意到了这两点

ghci> 0 :: AInt
Run Code Online (Sandbox Code Playgroud)

ghci> A 0 :: AInt
Run Code Online (Sandbox Code Playgroud)

给我同样的回应

A { unA = 0 }
Run Code Online (Sandbox Code Playgroud)

有人请告诉我这两个有什么区别?

Tik*_*vis 14

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

在这种特殊情况下,AInt派生出来Num.这意味着这AInt是一个Num使用相同代码的实例Int(包含在A构造函数中的所有内容).这包括IntfromInteger功能.

fromInteger功能在来定义IntfromInteger,看起来像这样:

fromInteger i = A (fromInteger i)
Run Code Online (Sandbox Code Playgroud)

因为0是多态的 - 它具有类型0 :: Num a => a--it是任何类型的有效常量Num.感谢newtype派生,这包括AInt使用fromInteger上面的函数.这意味着0 :: AInt一个人之间确实没有区别A 0 :: AInt.

  • 所以我们可以同时执行`1 :: Float`和`1 :: Int`的原因相同? (4认同)
  • @Znatz:是的.由于`AInt`在`Num`中,它可以使用文字,就像`Float`,`Int`和许多其他类型. (2认同)

ham*_*mar 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)
  ...
Run Code Online (Sandbox Code Playgroud)

因此,当你写作时0 :: AInt,这会扩展到fromInteger 0 :: AInt(通过上面的定义)等于A (fromInteger 0)哪个与你写的相同A 0.

1 GeneralizedNewtypeDeriving实际上并没有写一个新的intance.它只是执行必要的强制转换以使用现有的强制转换.