Flowryn
Flowryn

Reputation: 1447

Why I get: Could not deduce (Fractional a) arising from a use of ‘/’?

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

Answers (2)

developer_hatch
developer_hatch

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

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477160

Short answer: by specifying Num a => a -> a -> a you claim that f can deal with all as 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

Related Questions