1

I'm have been using Haskell for a while and I'm having some trouble with Numeric Types. Most of the time I can solve the after trying around a bit but this time i have been stumped for over two hours by this trivial piece of code.

I have these Functions:

takeLessEqual x = takeWhile (<=x)

leftHalf x = takeLessEqual x $ (map (\x -> ((x+0.5)*(x+0.5) + 0.75))) [1..]
-- Produces the list [3.0,7.0,13.0,21.0,31.0,43.0 ... (some number < x)]

rightHalf x = takeLessEqual x $ (map (\x -> if even x then x*x + 1 else x*x)) [1..]
-- Produces the list [1,5,9,17,25,37,49 ... (some number < x)]   

total x = (sum $ rightHalf x) + (sum $ leftHalf x)
-- total 10 Should produce some number 25 or 25.0

It loads without error in to ghci, but when i try to evaluate:

*> leftHalf 10 
[3.0,7.0]
it :: (Ord a, Fractional a, Enum a) => [a]

*> rightHalf 10
[1,5,9]
it :: Integral a => [a]

*> total 10

<interactive>:150:1: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘it’
  prevents the constraint ‘(Fractional a0)’ from being solved.
  Probable fix: use a type annotation to specify what ‘a0’ should be.
  These potential instances exist:
    instance Fractional Double -- Defined in ‘GHC.Float’
    instance Fractional Float -- Defined in ‘GHC.Float’
    ...plus one instance involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the first argument of ‘print’, namely ‘it’
  In a stmt of an interactive GHCi command: print it

And I have tried adding type annotations at various points and converting types with toInteger and fromIntegral with no success.

What am I doing wrong and how do I fix it?

4
  • Try: total 10 :: Double Commented Nov 1, 2016 at 17:05
  • I already tried this ofc --> • No instance for (Integral Double) arising from a use of ‘total’ • In the expression: total 10 :: Double In an equation for ‘it’: it = total 10 :: Double Commented Nov 1, 2016 at 17:14
  • 2
    Yes, the problem is that Haskell does not have any type that is both Integral and Fractional at the same time, but your total functions needs such a type! Commented Nov 1, 2016 at 17:17
  • fwiw, you can rewrite leftHalf as leftHalf x = takeLessEqual x $ map (\x -> (x * x + x + 1) [1..] and it'll work for all numeric types, not just Integral or Fractional types. Commented Nov 2, 2016 at 0:48

1 Answer 1

6

Firstly, I would like to recommend that you definitely follow GHC's suggestion of adding type signatures. I recommend always giving type signatures to top-level definitions. This will make the error messages significantly more clear.

If we go ahead and do that, assuming that the Fractional type you want is Double and the Integral type is Int, we have

takeLessEqual :: Ord a => a -> [a] -> [a]
takeLessEqual x = takeWhile (<=x)

leftHalf :: Double -> [Double] -- This must be some `Fractional` type since we are doing things like adding by 0.5
leftHalf x = takeLessEqual x $ (map (\x -> ((x+0.5)*(x+0.5) + 0.75))) [1..]

rightHalf :: Int -> [Int]      -- This must be some `Integral` type since we are using `even`
rightHalf x = takeLessEqual x $ (map (\x -> if even x then x*x + 1 else x*x)) [1..]

-- total :: ?
total x = (sum $ rightHalf x) + (sum $ leftHalf x)

Note that there is no type that is both Integral and Fractional, so we cannot use the same number type everywhere here.

The problem now is that the two sides of + in total are not the same type, even though they must be ((+) :: Num a => a -> a -> a).

Now, you need to decide if you want to end up using Doubles or Ints (or a combination. I will assume you want Double for both the input and the output):

total :: Double -> Double

Now, we see that rightHalf needs an Int, so we must use either ceiling, floor or round to convert the argument from a Double. The result we get back is a [Int] fed into sum, which gives us an Int (since sum :: Num a => [a] -> a).

Assuming ceiling gives the rounding we want, we end up with:

total x = (fromIntegral . sum . rightHalf $ ceiling x) + (sum $ leftHalf x)

On a stylistic note, I would also suggest writing ... + sum (leftHalf x) instead of ... + (sum $ leftHalf x).

In general, if you want to go from a fractional type to an integral type, you will want to use floor, ceiling or round. If you want to go from an integral type to anything else (usually a fractional type, but possible some different integral type), you will want fromIntegral.

You could do the conversions in a different spot (in leftHalf and/or rightHalf). It seems like those two functions should stay floating and integral respectively though, since you would have to choose a rounding method (when the function that calls those two should probably be able to decide on a rounding method).

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.