什么是Haskell中的RealFloat类型用于?

时间:2016-12-13 17:14:33

标签: haskell typeclass

了解你的Haskell 一书中,有一个关于计算BMI的例子。

bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
    | bmi <= skinny = "You're underweight, you emo, you!"
    | bmi <= normal = "You're supposedly normal. Pffft, I bet you're ugly!"
    | bmi <= fat    = "You're fat! Lose some weight, fatty!"
    | otherwise     = "You're a whale, congratulations!"
    where bmi = weight / height ^ 2
          (skinny, normal, fat) = (18.5, 25.0, 30.0)

当我尝试自己做这个例子时,我使用(Num a) => a -> a -> String作为方法的类型签名。但是,这引发了以下错误:

Could not deduce (Ord a) arising from a use of ‘<=’
    from the context (Num a)
      bound by the type signature for
                 bmiTell :: Num a => a -> a -> String
      at scratch.hs:96:12-38
    Possible fix:
      add (Ord a) to the context of
        the type signature for bmiTell :: Num a => a -> a -> String

我无法仅使用NumOrd类型类来解决错误。为什么我需要使用RealFloat类型类来使这段代码工作? RealFloat未涵盖的Num有什么特别之处?

2 个答案:

答案 0 :(得分:10)

虽然Num还不够,但RealFloat对于此示例确实过多。 Fractional这对于(/)来说是必要的,这已经足够了:

GHCi> :t (/)
(/) :: Fractional a => a -> a -> a

然后,适当的签名将是:

bmiTell :: (Fractional a, Ord a) => a -> a -> String

RealFloat是浮点类型的类,而Fractional涵盖支持实际除法的所有内容。 DoubleRealFloat(也是Fractional,因为它是RealFloat的超类。 Data.Ratio提供的有理数的Ratio类型是Fractional的一个例子,不是RealFloat

另请参阅:Ben's answer,其中考虑了本书可能使用RealFloat的原因而不是此处显示的可论证的更简单的替代方法。

答案 1 :(得分:5)

除了在duplode的答案中提到的/之外,您还使用非整数文字,例如18.5。那些不能用作每个Num类型(例如18.5 :: Integer会是什么类型?),因此您无法保证您的函数可以处理任何Num RealFloat输入来电者。

我怀疑这本书使用的是Ord,因为它还暗示RealFrac(通过Real/),因此只需编写一个约束。多个约束可能使示例看起来更复杂和令人生畏,其功能并不是练习的重点。