Luciana
Luciana

Reputation: 33

Haskell: confused about uses of Num typeclass

I'm confused about why this works:

f :: Num a => a -> a
f x = x * 42

But this doesn't:

g :: Num a => a -> a
g x = x * 4.2

I had understood that Num enclosed all types that implemented operators (+), (-), (*). So, if 42 is an Int and 4.2 is a Fractional, and both types implement the operator (*), why am I getting this error for g?:

Could not deduce (Fractional a) arising from the literal ‘4.2’ 
from the context (Num a)
bound by the type signature for g :: Num a => a -> a
at file.hs:9:6-20
Possible fix:
add (Fractional a) to the context of
the type signature for g :: Num a => a -> a

Upvotes: 2

Views: 278

Answers (3)

Carl
Carl

Reputation: 27023

Haskell types work differently than it appears you are expecting.

Num a => a -> a does not mean "there is some type a that is an instance of Num for which this takes a value of type a and returns a value of type a."

It means "For every single type a that is an instance of Num, this takes a value of type a and returns a value of type a."

This means that it has to work for any type the caller chooses. If the caller chooses a ~ Int (~ is the syntax for type equality, which some optional GHC features enable as a constraint) and then calls g 1, what Int value should that be?

Well, that doesn't work out very well. 4.2 isn't a valid Int. So the * operator fails to type-check, because it can't make the types of its arguments match.

This is why it has a more restrictive type - the types it accepts must be types that the literal 4.2 is a valid representation of. That ends up requiring a Fractional constraint.

Type variables in Haskell are universally quantified. They always make statements about all types which satisfy their constraints. If there is ever a counter-example possible, things won't type-check.

Upvotes: 4

Nicola Gigante
Nicola Gigante

Reputation: 3276

The problem is that the Num a constraint is too broad. Using the interpreter might help:

Prelude> :type 42
42 :: Num t => t
Prelude> :type 4.2
4.2 :: Fractional t => t

Numeric literals in Haskell are polymorphic, so as the types are saying, the literals are instantiated as any type as long as the type is a Num or, respectively, a Fractional.

Now look at the type signature of the (*) function from the Num class:

Prelude> :type (*)
(*) :: Num a => a -> a -> a

The (*) operator works on any Num value, but the two operands have the same type. So if you are multiplying by a value of Fractional type (whatever type the literal 4.2 will be instantiated into), the other operand must be Fractional as well, and the same type in particular. This is why the Num constraint on your function is too broad: your function cannot work on any Num type, but only on Fractionals, hence the compiler is saying that you are missing a Fractional a => constraint in the type signature.

If you let the compiler inference the type of your function, it will agree:

Prelude> let g x = x * 4.2
Prelude> :type g
g :: Fractional a => a -> a

Upvotes: 5

Davislor
Davislor

Reputation: 15164

The numeric constant 42 is defined to be the same as fromIntegral (42::Integer), and any instance of Num has an implementation of fromIntegral. It occurs in the expression x * 42, so its inferred type is the same as x, which is an instance of Num. So the compiler calls the proper version of fromIntegral for that Num instance (or rather, does the calculation at compile time) and everything is hunky-dorey.

But a constant with a decimal point tries to convert from a decimal to the type of x. All we know about that type is that it’s an instance of Num, so it can’t do the conversion in the general case. There’s no way to represent 4.2 as, say, an Integer.

As the error message says, the compiler can convert a decimal constant to any Fractional type. So change the type constraint from Num to Fractional, and the decimal will work.

Upvotes: 2

Related Questions