Reputation: 1447
I have the following code in a .hs file
module TypeInference1 where
f :: Num a => a -> a -> a
f x y = x + y + 3
Then if I check the type of f, I obtain the following result, which is ok:
*TypeInference1> :t f
f :: Num a => a -> a -> a
If I pass to f one parameter of type fractional and check its type I obtain:
*TypeInference1> :t f 1.0
f 1.0 :: Fractional a => a -> a
But on the other hand if I change the f by setting a division operation on one of its arguments, as follows:
f x y = x/2 + y + 3
I obtain the following error:
5-typeInference1.hs:4:9: error:
• Could not deduce (Fractional a) arising from a use of ‘/’
from the context: Num a
bound by the type signature for:
f :: forall a. Num a => a -> a -> a
at 5-typeInference1.hs:3:1-25
Possible fix:
add (Fractional a) to the context of
the type signature for:
f :: forall a. Num a => a -> a -> a
• In the first argument of ‘(+)’, namely ‘x / 2’
In the first argument of ‘(+)’, namely ‘x / 2 + y’
In the expression: x / 2 + y + 3
Why does this happen and why the type can not be deduced when I change the function f as above?
Upvotes: 5
Views: 2475
Reputation: 16224
Let's have in mind this kind of typeclass hierarchy:
Num a
/ \
/ \
/ \
(Fractional a) (Integral a)
/ \ / \
/ \ / \
/ \ / \
Double Float Integer Int
Now if you say:
f :: Num a => a -> a -> a
You are telling that all the functions that will be used by a
will be from the Num
typeclass, these here:
(+), (*), abs, signum, fromInteger, (negate | (-))
If you check the body of your function:
f :: Num a => a -> a -> a
f x y = x/2 + y + 3
you are using (/)
function, and that function belongs to the "sub" typeclass of Num
, Fractional
and Fractional
has these functions allowed:
fromRational, (recip | (/))
Conclusion:
If you use one function that belongs from one o the bottom in the hierarchy, you are restricting the type to that level.
Fix
The good thing is, if you read carefully the stack error, in some part of it says:
Possible fix:
add (Fractional a) to the context of
the type signature for:
And let's try it:
f :: Fractional a => a -> a -> a
f x y = x/2 + y + 3
Upvotes: 4
Reputation: 477160
Short answer: by specifying Num a => a -> a -> a
you claim that f
can deal with all a
s that are members of the Num
typeclass, but the (/)
can only work with types that are members of the Fractional
typeclass.
Fractional
is a "sub" typeclass of Num
. That means that all types that are a member of Fractional
are a member of Num
, but this does not hold vice versa.
By specifying as type for f:
f :: Num a => a -> a -> a
You say that f
can deal with all types a
that are members of the Num
typeclass. But that is incorrect if you define f
as f x y = x/2 + y + 3
, since x/2
means that x
should be a type that is a member of Fractional
. Indeed (/)
has type (/) :: Fractional a => a -> a -> a
. You thus should make f
more restrictive, such that you can only pass values that are of type a
with a
a member of the Fractional
typeclass:
f :: Fractional a => a -> a -> a
f x y = x/2 + y + 3
Upvotes: 10