Reputation:
Task is to find all two-valued numbers representable as the sum of the sqrt's of two natural numbers. I try this:
func = [sqrt (x) + sqrt (y) | x <- [10..99], y <- [10..99], sqrt (x) `mod` 1 == 0, sqrt (y) `mod` 1 == 0]
Result:
Unresolved top-level overloading Binding : func Outstanding context : (Integral b, Floating b)
How can I fix this?
Upvotes: 3
Views: 292
Reputation: 152867
This happens because of a conflict between these two types:
sqrt :: Floating a => a -> a
mod :: Integral a => a -> a -> a
Because you write mod (sqrt x) 1
, and sqrt
is constrained to return the same type as it takes, the compiler is left trying to find a type for x
that simultaneously satisfies the Floating
constraint of sqrt
and the Integral
constraint of mod
. There are no types in the base
library that satisfy both constraints.
A quick fix is to use mod' :: Real a => a -> a -> a
:
import Data.Fixed
func = [sqrt (x) + sqrt (y) | x <- [10..99], y <- [10..99], sqrt (x) `mod'` 1 == 0, sqrt (y) `mod'` 1 == 0]
However, from the error you posted, it looks like you may not be using GHC, and mod'
is probably a GHC-ism. In that case you could copy the definition (and the definition of the helper function div'
) from here.
But I recommend a more involved fix. The key observation is that if x = sqrt y
, then x*x = y
, so we can avoid calling sqrt
at all. Instead of iterating over numbers and checking if they have a clean sqrt
, we can iterate over square roots; their squares will definitely have clean square roots. A straightforward application of this refactoring might look like this:
sqrts = takeWhile (\n -> n*n <= 99)
. dropWhile (\n -> n*n < 10)
$ [0..]
func = [x + y | x <- sqrts, y <- sqrts]
Of course, func
is a terrible name (it's not even a function!), and sqrts
is a constant we could compute ourselves, and is so short we should probably just inline it. So we might then simplify to:
numberSums = [x + y | x <- [4..9], y <- [4..9]]
At this point, I would be wondering whether I really wanted to write this at all, preferring just
numberSums = [8..18]
which, unlike the previous iteration, doesn't have any duplicates. It has lost all of the explanatory power of why this is an interesting constant, though, so you would definitely want a comment.
-- sums of pairs of numbers, each of whose squares lies in the range [10..99]
numberSums = [8..18]
This would be my final version.
Also, although the above definitions were not parameterized by the range to search for perfect squares in, all the proposed refactorings can be applied when that is a parameter; I leave this as a good exercise for the reader to check that they have understood each change.
Upvotes: 4