Reputation: 378
As a beginner to Haskell I'm having a hard time understanding why this is failing
-- this works fine of course
f :: Float
f = 1.0
f :: Num a => a
f = 1.0
-- Could not deduce (Fractional a) arising from the literal ‘1.0’
-- from the context: Num a
-- bound by the type signature for:
-- f :: forall a. Num a => a
My confusion stems from them both having instances of Num. So since Int's, Integers, Doubles, etc all have an instance of the Num typeclass, why can't I stuff any numerical value in f
?
For example, negate
which has a signature
negate :: Num a => a -> a
will work with Int's Float's Doubles, etc.
Any insight would be greatly appreciated.
Upvotes: 3
Views: 201
Reputation: 71119
If you simply write
f = 10.5
you get back f :: Fractional a => a
without a problem.
It is when you explicitly claim its type to be Num a => a
, Haskell must unify the proclaimed and the actual types, and it can't, since Fractional
is Num
's subclass:
>> :i Fractional
class Num a => Fractional a where
-- ^^^ -- Fractional is Num's subclass
...........
fromRational :: Rational -> a
...........
Every Fractional
is a Num
, but not every Num
is a Fractional
.
The type of fromRational
is fromRational :: Fractional a => Rational -> a
.
This suggests that floating point literals like 10.5
are actually read as fromRational (10.5 :: Rational)
, just like whole number literals like 10
are read as fromInteger (10 :: Integer)
.
Indeed, section 10.3 in the Haskell tutorial reads:
An integer numeral (without a decimal point) is actually equivalent to an application of
fromInteger
to the value of the numeral as anInteger
. Similarly, a floating numeral (with a decimal point) is regarded as an application offromRational
to the value of the numeral as aRational
. Thus,7
has the type(Num a) => a
, and7.3
has the type(Fractional a) => a
.
Upvotes: 1
Reputation: 116174
why can't I stuff any numerical value in
f
?For example, negate which has a signature
negate :: Num a => a -> a
will work withInt
'sFloat
'sDouble
s, etc.
Precisely because of that!
You can't stuff any numerical value in the definition of f
, as you can't stuff any numerical value in the definition of negate
.
Suppose we tried to define negate
as follows
negate :: Num a => a -> a
negate x = 10.5 - 10.5 - x
Would that work on an Int
? That is, on negate 42 :: Int
? No, since 10.5
is not an Int
.
Since the type of negate
promises that it works on any numeric type, including Int
, but it does not actually work on Int
, then the promise is broken. Static type checking rejects that code because of this.
Similarly, if type checking accepted
f :: Num a => a
f = 10.5
then all of these should work: f + 8 :: Int
, f / 2 :: Double
, f - 4 :: Integer
.
But 10.5
does not fit into Int
(nor into Integer
).
The issue here is that f :: Num a => a
allows the caller to choose any numeric type a
. Since the type allows the caller to choose, f
can not choose itself, but must adapt to any choice made by the caller. So, f
can not use code which only works at some numeric types, but not others.
If f
only works at Fractional
types, a subset of Num
types, then the type of f
must advertise to the caller that its choice is limited to fractional types. This is done by using f :: Fractional a => a
instead.
Upvotes: 2
Reputation: 1590
The issue here is that when you write f :: Num a => a
, this means that f
must work for all possible instantiations of a
such that a Num a
instance exists. In particular, this means that writing (f :: Int)
somewhere else should work fine, since instance Num Int
certainly exists. However, the value that you wrote for f
to return is 1.0
, which is not an integer: 1.0 :: Fractional p => p
. The error message is basically saying that "Knowing that a
is a Num
doesn't tell us that a
is a Fractional
, so there's no way for the fractional literal 1.0 to have type a
".
One way to think about it is that the caller gets to choose what a
should be: this is why type signatures of this form are called universally quantified.
You may be thinking of existential quantification: f
returns some kind of Num
, but the "caller" doesn't know what Num
was returned. This is often not as useful as universal quantification, and is a little clunky in Haskell, but can be done like this:
{-# LANGUAGE ExistentialQuantification #-} -- This is a GHC extension
-- A SomeNum value contains some kind of Num. Can't see what kind from the "outside".
data SomeNum = forall a. Num a => SomeNum a
f :: SomeNum
f = SomeNum 5.0
In Haskell, a signature like f :: a
is syntax for the (conceptually clearer) f :: forall a. a
, where the forall
is a lot like a "type-level lambda" (and not the f :: exists a. a
which you may have been thinking of). In fact, if you look at GHC Core, you will see that all these type applications are explicit: whenever you use a universally quantified function, the "caller" explicitly passes in the types to use for each of the type variables.
However, I would advise that you not try to use existential quantification at this stage: there are often better/easier alternatives. I just wanted to explain it to help show the difference between existentials and universals.
Upvotes: 5
Reputation: 15887
When you give the compiler a type declaration like f :: Num a => a
, you're not just saying f
has the typeclass Num
but that that's all you know about it here. Since you've entered 1.0
, not 1
, the compiler concludes that you also need Fractional
. Your declaration said that you don't, and so this can't compile. It does compile if you say it's Fractional a => a
, and Fractional
implies Num
.
Upvotes: 0