Home > front end >  Haskell Fractional could not deduce (RealFrac a) arising from a use of `round'
Haskell Fractional could not deduce (RealFrac a) arising from a use of `round'

Time:10-10

I do not want to perform integer divison. I want to take in a fractional input type, do a division with it, round the result to an integer and return that.

piCalc :: (Fractional a, Ord a, Integral b) => a -> (a, b)
piCalc z = (piCalc' steps 1 0, steps)
    where steps = round $ (4-z)/(2*z)   1

piCalc' :: (Integral a, Ord a, Fractional b) => a -> a -> a -> b
piCalc' 0 s k = 0
piCalc' steps s k = (fromInteger s*4)/(fromInteger 2*k   1)   piCalc' (steps-1) ((-1)*s) (k 1)

main = do
    print (piCalc 0.001)

I get the following error:

Recursion.hs:59:19: error:
    * Could not deduce (RealFrac a) arising from a use of `round'
      from the context: (Fractional a, Ord a, Integral b)
        bound by the type signature for:
                   piCalc :: forall a b.
                             (Fractional a, Ord a, Integral b) =>
                             a -> (a, b)
        at Recursion.hs:57:1-58
      Possible fix:
        add (RealFrac a) to the context of
          the type signature for:
            piCalc :: forall a b.
                      (Fractional a, Ord a, Integral b) =>
                      a -> (a, b)
    * In the first argument of `($)', namely `round'
      In the expression: round $ (4 - z) / (2 * z)   1
      In an equation for `steps': steps = round $ (4 - z) / (2 * z)   1
   |
59 |     where steps = round $ (4-z)/(2*z)   1
   |                   ^^^^^

Recursion.hs:63:34: error:
    * Couldn't match expected type `Integer' with actual type `a'
      `a' is a rigid type variable bound by
        the type signature for:
          piCalc' :: forall a b.
                     (Integral a, Ord a, Fractional b) =>
                     a -> a -> a -> b
        at Recursion.hs:61:1-64
    * In the first argument of `fromInteger', namely `s'
      In the first argument of `(*)', namely `fromInteger s'
      In the first argument of `(/)', namely `(fromInteger s * 4)'
    * Relevant bindings include
        k :: a (bound at Recursion.hs:63:17)
        s :: a (bound at Recursion.hs:63:15)
        steps :: a (bound at Recursion.hs:63:9)
        piCalc' :: a -> a -> a -> b (bound at Recursion.hs:62:1)
   |
63 | piCalc' steps s k = (fromInteger s*4)/(fromInteger 2*k   1)   piCalc' (steps-1) ((-1)*s) (k 1)
   |                                  ^^^

Changing Fractional to RealFrac in piCalc (the top function) fixed the first error for me but I don't understand why or know if this is a "good" solution. I could barely find any usage examples of RealFrac when searching google. I do not know what is causing the second error.

CodePudding user response:

The RealFrac constraint implies both Real and Fractional. Real means that the numbers must be 1-dimensional (in contrast to, say, 2-dimensional complex numbers, or higher dimensional vectors). round requires Real, because it must map its input on the 1-dimensional set of integers. And round obviously requires Fractional, otherwise it doesn't make much sense to round. So, round requires a RealFrac as input. Changing Fractional to RealFrac is the right solution to the first error.

The second error is because you specify that your types are Integral, but then you apply the fromInteger function which expects a concrete Integer. You should change fromInteger to fromIntegral.

Then you will still get an error, because you write fromInteger 2*k, which means (fromInteger 2) * k in Haskell, but you probably meant fromInteger (2 * k) or 2 * fromInteger k. And this should also be changed to fromIntegral.

This code compiles without errors:

piCalc :: (RealFrac a, Ord a, Integral b) => a -> (a, b)
piCalc z = (piCalc' steps 1 0, steps)
    where steps = round $ (4-z)/(2*z)   1

piCalc' :: (Integral a, Ord a, Fractional b) => a -> a -> a -> b
piCalc' 0 s k = 0
piCalc' steps s k = (fromIntegral s*4)/(2 * fromIntegral k   1)   piCalc' (steps-1) ((-1)*s) (k 1)

main = do
    print (piCalc 0.001)

If you're just interested in calculating pi then it might make more sense to choose more concrete types, for example Double or Rational instead of RealFrac a => a and Int or Integer instead of Integral a => a. You can keep the implementation the same and only change the signatures:

piCalc :: Double -> (Double, Int)
piCalc z = (piCalc' steps 1 0, steps)
    where steps = round $ (4-z)/(2*z)   1

piCalc' :: Int -> Int -> Int -> Double
piCalc' 0 s k = 0
piCalc' steps s k = (fromIntegral s*4)/(2 * fromIntegral k   1)   piCalc' (steps-1) ((-1)*s) (k 1)

main = do
    print (piCalc 0.001)
  • Related