Reputation: 33
Hi I'm trying to write some basic code to implement the Bresenham Algorithm but I'm stuck trying to use round
and /
. My code is:
bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $ (fromIntegral ((y2 -
y1) * (x' - x1)) / fromIntegral (x2 - x1)) + y1
I keep getting the No instance for (Fractional Int) arising from a use of ‘/’
and No instance for (RealFrac Int) arising from a use of ‘round’
errors occuring. I don't understand as I believed that fromIntegral would convert the numerator and denominator to fractionals which would allow for the use of the /
operation?
Upvotes: 3
Views: 240
Reputation: 15683
I don't understand as I believed that fromIntegral would convert the numerator and denominator to fractionals which would allow for the use of the / operation?
Yes, your understanding is basically correct but you made a small oversight.
Here is your code formatted slightly differently:
bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $
(
fromIntegral ((y2 - y1) * (x' - x1))
/ fromIntegral (x2 - x1)
) + y1
this is (because +
binds stronger than $
) equivalent to:
bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round (
(
fromIntegral ((y2 - y1) * (x' - x1))
/ fromIntegral (x2 - x1)
) + y1)
The problem here is actually neither round
nor /
, it's your + y1
at the end. Because y1
is already known to be Int
, the type checker tries to unify
fromIntegral ((y2 - y1) * (x' - x1)) / fromIntegral (x2 - x1)
with Int
, which of course fails because /
isn't allowed with Int
.
The solution is to add another fromIntegral
:
bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $
(
fromIntegral ((y2 - y1) * (x' - x1))
/ fromIntegral (x2 - x1)
) + fromIntegral y1 -- we need y1 as a fractional
Alternatively you could also wrap the everything before + y1
- including the round
- in parentheses, that would work too.
Now the reason this wasn't caught earlier is because fromIntegral
can actually convert integer types to any number type... including themselves. So in this case, because of how the expression was written the typechecker inferred that you meant fromIntegral
to convert Int
to... Int
again. Which is not what you meant, but the compiler doesn't know that.
If we add a small helper function:
toDouble :: Int -> Double
toDouble = fromIntegral
and use it in your definition instead of fromIntegral
bresenhamAlgorithm :: Coord -> Coord -> Int -> Int
bresenhamAlgorithm (x1, y1) (x2, y2) x'= round $
(
toDouble ((y2 - y1) * (x' - x1))
/ toDouble (x2 - x1)
) + y1
to force the conversion, we actually get a more helpful error message:
• Couldn't match expected type ‘Double’ with actual type ‘Int’
• In the second argument of ‘(+)’, namely ‘y1’
In the second argument of ‘($)’, namely
‘(toDouble ((y2 - y1) * (x' - x1)) / toDouble (x2 - x1)) + y1’
In the expression:
round
$ (toDouble ((y2 - y1) * (x' - x1)) / toDouble (x2 - x1)) + y1
) + y1
^^
Upvotes: 5