在Learn You A 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)
Run Code Online (Sandbox Code Playgroud)
当我试图自己做这个例子时,我用作(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
Run Code Online (Sandbox Code Playgroud)
我只能使用Num和Ord类型类来解决错误.为什么我需要使用RealFloat类型类来使这段代码工作?有什么特别之处RealFloat不在于Num?
dup*_*ode 10
虽然Num还不够,但RealFloat这个例子确实过分了.Fractional,这是必要的(/),足够好:
GHCi> :t (/)
(/) :: Fractional a => a -> a -> a
Run Code Online (Sandbox Code Playgroud)
那么,适当的签名将是:
bmiTell :: (Fractional a, Ord a) => a -> a -> String
Run Code Online (Sandbox Code Playgroud)
RealFloat是一个浮点类型的类,同时Fractional涵盖了支持实际划分的所有内容.Double是一个RealFloat(也是一个Fractional,因为它是一个超类RealFloat).Ratio有理数的类型,可从中获得Data.Ratio,是一个Fractional不是a 的例子RealFloat.
另请参阅:Ben的答案,该答案考虑了本书可能使用的原因,RealFloat而不是此处所示的可论证的更简单的替代方案.
除了/在duplode的答案中提到的,你还使用非整数文字18.5.那些不能用作每种Num类型(18.5 :: Integer例如,什么?),所以你不能保证你的函数可以处理Num调用者喜欢的任何类型.
我怀疑这本书的使用RealFloat仅仅是因为它也暗示了Ord(通过RealFrac和Real),所以只需编写一个约束.多个约束可能使示例看起来更复杂和令人生畏,其功能不是练习的重点.