Reputation: 7815
I've been looking at these functions:
import Data.Digits (digits)
numberDivider (a,b) = a / b
numberDivider2 (num,denom) = num / denom
where
a = head $ digits 10 num
b = head . tail $ digits 10 denom
We can look at the types of these functions:
λ> :t numberDivider2
numberDivider2 :: (Integral a, Fractional a) => (a, a) -> a
λ> :t numberDivider
numberDivider :: Fractional a => (a, a) -> a
The numberDivider
does what you would think it does. numberDivider2
gives:
No instance for (Show a0) arising from a use of ‘print’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance Show Double -- Defined in ‘GHC.Float’
instance Show Float -- Defined in ‘GHC.Float’
instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
-- Defined in ‘GHC.Real’
...plus 34 others
In a stmt of an interactive GHCi command: print it
when called with correct arguments (e.g. (48,98)). Now I don't see how making something more specific leads to ambiguity? I feel that I am being stupid in some way. (I also don't see why the function needs to go from (a,a) -> a
as I would have thought it would be (a,a) -> b
where a is Integral
and b is float or something.
I have tried putting in type annotations to force it to use Float for the result.
Can someone point out what I'm missing here?
Upvotes: 3
Views: 174
Reputation: 370142
Now I don't see how making something more specific leads to ambiguity?
It's ambiguous in both cases, but in case of numberDivider
the ambiguity can be resolved using Haskell's defaulting rules. In this particular instance those rules basically say that if multiple numeric types are possible and one of them is Integer
, pick Integer
. If Integer
is not possible, but Double
is, pick Double
. If neither are possible, the ambiguity remains.
In case of numberDivider
Integer
is not possible because Integer
is not an instance of Fractional
, but Double
is. Therefore Double
is picked.
In case of numberDivider
neither is possible because there simply is no type that's both Integral
and Fractional
. Therefore the ambiguity remains.
You might argue that a set of exactly 0 possible types isn't ambiguous, but simply impossible, so the error message should be different, but it also has to take into account instances that might be defined elsewhere. That is, while there aren't any types that are both Fractional
and Integral
in the standard library, they might be defined somewhere else, so we can't exclude that possibility (even though it would make no sense).
Upvotes: 4
Reputation: 54058
This is likely coming from the definition of digits
, which I'm guessing takes an Integrala => a
as one of its arguments. This then places the additional constraint of Integral
on the argument to numberDivider2
. As it turns out, there is not a type that is an instance of both Fractional
and Integral
. When you input numeric literals, though, it tries to convert from Num a => a
to (Integral a, Fractional a) => a
, and in GHCi there are special rules to try to find an instance that also uses Show
so that you can print it to the screen. Since no such type exists, you get an error.
Now, the real problem seems to be arising from a misunderstanding of Haskell's number system. You can't use /
on all numbers, such as Int
s, because /
is not defined for those types. You can only use /
on fractional types, hence the Fractional
typeclass. If you want to convert Int
s or Integer
s to Float
or Double
to perform a floating point division, you can use fromIntegral
to convert them to any Num
type, e.g.
a = head $ digits 10 $ fromIntegral num
b = head . tail $ digits 10 $ fromIntegral denom
This should remove the Integral
constraint from the function.
After looking at the type of digits
, I see that this won't work. Instead, you probably want something like
numberDivider2 :: (Integral a, Fractional b) => (a, a) -> b
numberDivider2 (num, denom) = fromIntegral num / fromIntegral denom
where
a = head $ digits 10 num
b = head . tail $ digits 10 denom
Note the location of the fromIntegral
s, they convert each Integral
value at the point where you want to perform a Fractional
operation.
Upvotes: 6