Why are these two haskell functions not equivalent?
I looked 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
numberDivider
does what you think. 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 can't see how to do something more specific leads to ambiguity? I feel like I'm so stupid. (I also don't understand why the function has to come from (a,a) -> a
, as I would have thought it would be (a,a) -> b
where a is Integral
and b is a float or something.
I tried to insert type annotations to force it to use Float for the result.
Can anyone point out what I am missing here?
source to share
This most likely comes from the definition digits
, which I assume it takes Integrala => a
as one of its arguments. This place then adds an additional constraint Integral
to the argument numberDivider2
. As it turns out, there is not a type that is an instance of how Fractional
, and so Integral
. When entering numeric literals, however, it tries to convert from Num a => a
to (Integral a, Fractional a) => a
, and there are special rules in GHCi 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 come from a misunderstanding of the Haskell numbering system. You cannot use /
for all numbers, for example Int
s, because it is /
not defined for these types. You can only use it /
for fractional types, hence the class Fractional
. If you want to convert Int
or Integer
to Float
, or Double
for the performance of the division with a floating point, you can use fromIntegral
to convert them into any type Num
, the eg
a = head $ digits 10 $ fromIntegral num
b = head . tail $ digits 10 $ fromIntegral denom
This should remove the constraint Integral
from the function.
Looking at the type digits
, I can see that it doesn'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
Pay attention to the location of the fromIntegral
s, they will convert each value Integral
where you want to perform the operation Fractional
.
source to share
Now I can't see how to do something more specific leads to ambiguity?
This is ambiguous in both cases, but in case of numberDivider
ambiguity can be resolved using Haskell's default rules. In this particular case, these rules basically say that if multiple numeric types are possible, and one of them Integer
, choose Integer
. If Integer
not possible, but available Double
, select Double
. If none of these are possible, uncertainty remains.
In case numberDivider
Integer
it is impossible because it is Integer
not an instance Fractional
, but Double
is. Therefore it is chosen Double
.
In case numberDivider
it is impossible, because there is simply no type that and Integral
, and Fractional
. Therefore, ambiguity remains.
You could argue that the set of exactly 0 possible types is not ambiguous, but simply impossible, so the error message should be different, but it should also account for instances that might be defined elsewhere. That is, although there are no types in the standard library Fractional
and Integral
they can be defined elsewhere, so we cannot rule out this possibility (although it does not make sense).
source to share