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?

+3


source to share


2 answers


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

.

+6


source


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).

+4


source







All Articles