I am attempting to create a list of pythagorean triplets, that is, a list of tuples that each contain (x, y, z) such that x^2 y^2 = z^2 and x, y, z are in range [1..n].
I've run into what seems to be a type error inside my list comprehension guard. Here is my code:
-- integer square root
isqrt :: Int -> Float
isqrt = sqrt . fromIntegral
-- hypotenuse
hyp :: Int -> Int -> Float
hyp x y = isqrt (x^2 y^2)
-- pythagorean triplets in range [1..n]
pyths :: Int -> [(Int, Int, Int)]
pyths n = [(x, y, truncate (hyp x y)) | x <- [1..n], y <- [x..n], elem (hyp x y) [1..n]]
Here is the error:
• Couldn't match expected type ‘Float’ with actual type ‘Int’
• In the expression: n
In the second argument of ‘elem’, namely ‘[1 .. n]’
In the expression: elem (hyp x y) [1 .. n]
|
11 | pyths n = [(x, y, truncate (hyp x y)) | x <- [1..n], y <- [x..n], elem (hyp x y) [1..n]]
|
I can resolve the error by editing my guard from elem (hyp x y) [1..n]
to elem (hyp x y) [1..fromIntegral n]
, which indicates to me that the variable n
is somehow transformed from an Int to a Float at some point during the list comprehension.
The [1..fromIntegral n]
doesn't look particularly concise or elegant compared to [1..5]
, is there another method that I should be using to either resolve or avoid this problem?
CodePudding user response:
which indicates to me that the variable n is somehow transformed from an Int to a Float at some point during the list comprehension.
Quite the opposite. You've declared n
to be an Int
in the type signature, and that fixes it forever. The Float
here is hyp x y
. You're asking whether hyp x y
is an elem
ent of [1..n]
. elem :: a -> [a] -> Bool
, so it only makes sense to ask that question when you have an object that's the same type as the list's elements. That's the mismatch: to use elem
with a Float
and a list, you need that list to be a [Float]
. But since the list is [1..n]
, it's clearly a [Int]
.
As for what you should do differently, I would suggest not using any floating-point arithmetic at all for this function. Probably it's fine for the scales you're working at, but I'd be too worried about rounding errors giving me wrong answers. Instead of searching for pairs such that sqrt (a^2 b^2)
is an integer, which requires a sqrt, you can search for triples such that a^2 b^2 == c^2
.