为什么这个Haskell表达式编译?

时间:2013-07-24 21:18:58

标签: haskell typechecking phantom-types

以下是我写的一些定义,以避免混合货币

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

data EUR
data USD 

newtype Amount a = Amount Double deriving (Fractional, Num, Show)

eur :: Double -> Amount EUR
eur = Amount

usd :: Double -> Amount USD
usd = Amount
  • usd 34 + usd 3按预期进行类型检查
  • usd 33 + eur 33是预期的编译错误
  • 我很惊讶,但根据编译器usd 33 + 3是可以的。我想避免的东西,并且不明白。我怀疑这是因为Num实例,但那么第二种情况有什么不同?

你能解释为什么usd 33 + 3编译,以及是否有可能使类型检查器拒绝这个表达式。

2 个答案:

答案 0 :(得分:12)

Haskell中的数字有很多隐含性。在心理上,您应该用3替换fromInteger 3之类的每个数字。由于Amount使用GeneralizedNewtypeDeriving作为Num类型类的一部分,因此它会继承fromInteger个实例。所以编译器正在这样做

usd 33 + 3
===                                      [implicit fromInteger & expand usd]
(Amount 33 :: Amount USD) + 
  fromInteger 3
===                                      [fromInteger :: Num a => a -> Amount a]
(Amount 33 :: Amount USD) + 
  (Amount 3 :: Amount a)
===                                      [unify a]
(Amount 33 :: Amount USD) + 
  (Amount 3 :: Amount USD)

答案 1 :(得分:2)

当GHC派生Num类时,它提供了一个实现 fromInteger功能。像3这样的整数文字实际上具有Num a => a类型。

ghci> :t (34)
(34) :: Num a => a

当类型检查器发现您正在尝试添加类型值时 Amount USD3,它确定3 :: Amount USD,这是有效的 它是Num类型类的成员。