In the Learn You A Haskell book, there is an example about calculating your 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)
When I tried to do this example by myself, I used (Num a) => a -> a -> String
as the type signature for the method. However that threw the following error:
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
I was not able to resolve the error just using the Num
and Ord
typeclasses. Why do I need to use RealFloat
typeclass to make this piece of code work? What is so special about RealFloat
that is not covered by Num
?
While Num
is not enough, RealFloat
is indeed excessive for this example. Fractional
, which is necessary for (/)
, is good enough:
GHCi> :t (/)
(/) :: Fractional a => a -> a -> a
An appropriate signature, then, would be:
bmiTell :: (Fractional a, Ord a) => a -> a -> String
RealFloat
is a class for floating point types, while Fractional
covers everything that supports real division. Double
is a RealFloat
(and also a Fractional
, as that is a superclass of RealFloat
). The Ratio
type for rational numbers, available from Data.Ratio
, is an example of a Fractional
that isn't a RealFloat
.
See also: Ben's answer, which considers why the book might have used RealFloat
rather than the arguably simpler alternative shown here.
In addition to /
as noted in duplode's answer, you use non-integer literals like 18.5
. Those can't be used as every Num
type (what would 18.5 :: Integer
be, for example?), so you can't promise that your function can handle any Num
type the caller likes.
I suspect the book uses RealFloat
simply because it also implies Ord
(via RealFrac
and Real
), so that it was only necessary to write a single constraint. Multiple constraints might have made the example look more complex and intimidating, with a feature that isn't the point of the exercise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With