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

为什么要编译这个Haskell表达式?,haskell,typechecking,phantom-types,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 34美元+3美元

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

{-# 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
  • 34美元+3美元
  • 33美元+33欧元
    是一个预期的编译错误
  • 我很惊讶,但是根据编译器的说法,
    33美元+3
    还可以。我想避免的事,我不明白。我怀疑这是因为
    Num
    instance,但是第二种情况有什么不同呢

你能解释一下为什么
USD33+3
会编译,以及是否有可能让类型检查器拒绝这个表达式。

Haskell中的数字有很多隐含性。从精神上讲,你应该用整数3的
替换像
3
这样的每个数字。由于
Amount
使用
GeneralizedNewtypeDeriving
作为
Num
typeclass的一部分,因此它继承了
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)

当GHC派生
Num
类时,它为
fromInteger
函数。像
3
这样的整数文本实际上具有类型
Num a=>a

ghci> :t (34)
(34) :: Num a => a
当类型检查器看到您正在尝试添加类型为的值时
Amount USD
3
,它确定
3::Amount USD
,自
它是
Num
typeclass的成员。

那是什么
∀ 十,。x⊦ ...比特?@tel,这是我的GHCI提示。我可以看出哪里会令人困惑。
Num
type类不是。它适用于可能包含整数的数据类型,并且以与整数兼容的方式支持原始数字运算。所以你真正想要的是一个或多个“数学符号”类(加、减、乘、除等)。许多EDSL都定义了自己的中缀运算符,例如
+
-
*
,以避免使用笨拙的
Num
类。不过,我不确定这是否真的是一个致命的问题。实际上,事实证明,拥有自定义类型的文本很少有关系,因为除非声明为多态,否则它们仍然不是多态的。