了解您的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 typeclass来使这段代码起作用? RealFloat未涵盖的Num有何特别之处?

最佳答案

尽管Num不够,但在此示例中RealFloat确实过多。 Fractional必需的(/)足够好:

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

那么,适当的签名将是:
bmiTell :: (Fractional a, Ord a) => a -> a -> String

RealFloat 是浮点类型的类,而 Fractional 涵盖支持实数除法的所有内容。 DoubleRealFloat(也是Fractional,因为它是RealFloat的 super class )。可以从 Ratio 获得的有理数的Data.Ratio类型是不是FractionalRealFloat的示例。

另请参阅:Ben's answer,它考虑了为什么本书可能使用RealFloat而不是此处显示的可能更简单的替代方法。

07-27 13:43