Mike H-R
Mike H-R

Reputation: 7815

Why are these two haskell functions not equivalent?

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

Answers (2)

sepp2k
sepp2k

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

bheklilr
bheklilr

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 Ints, because / is not defined for those types. You can only use / on fractional types, hence the Fractional typeclass. If you want to convert Ints or Integers 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 fromIntegrals, they convert each Integral value at the point where you want to perform a Fractional operation.

Upvotes: 6

Related Questions