在了解你的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
我无法仅使用Num
和Ord
类型类来解决错误。为什么我需要使用RealFloat
类型类来使这段代码工作? RealFloat
未涵盖的Num
有什么特别之处?
答案 0 :(得分:10)
虽然Num
还不够,但RealFloat
对于此示例确实过多。 Fractional
这对于(/)
来说是必要的,这已经足够了:
GHCi> :t (/)
(/) :: Fractional a => a -> a -> a
然后,适当的签名将是:
bmiTell :: (Fractional a, Ord a) => a -> a -> String
RealFloat
是浮点类型的类,而Fractional
涵盖支持实际除法的所有内容。 Double
是RealFloat
(也是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
和/
),因此只需编写一个约束。多个约束可能使示例看起来更复杂和令人生畏,其功能并不是练习的重点。