user8638529
user8638529

Reputation:

Unresolved top level overloading

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

Answers (1)

Daniel Wagner
Daniel Wagner

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

Related Questions