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)