Derekzs
Derekzs

Reputation: 117

Haskell float calculation error

I'm trying to write a short function that calculate the area under a curve, but I kept getting type mismatch error.

Parameters in the following function: l, r are the range for evaluation, a and b are parameters of the curve.

solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
    where eval a b p = fromIntegral . sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
          area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
          range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]

I'm getting quite frustrated as the type system in Haskell is really not that intuitive. Can someone suggest the best practice when dealing with Floating numbers in numeric calculation?


In summary, the above code doesn't work because:

If we want to evaluate the algebraic curve in this question to Floating point value without changing the input type, we could just cast a to [Double]. Here's the code:

solve :: Int -> Int -> [Int] -> [Int] -> [Double]
solve l r a b = [area]
    where eval p = sum . map (\(ai, bi) -> ai * p ^^ bi) $ zip af b
          area = foldl (\acc p -> acc + 0.001 * eval p) 0 range
          range = map (\x -> (fromIntegral x :: Double ) / 1000) [l*1000..r*1000]
          af = map fromIntegral a :: [Double]

I've also change ^ to ^^ to deal with negative exponent.

Upvotes: 1

Views: 357

Answers (1)

mnoronha
mnoronha

Reputation: 2625

To understand type errors, you should walk through some types :). A good place to start is by commenting out the type declaration to see what GHCi infers (in this case, our error doesn't change, but it's a good general practice to make sure our type declaration isn't the problem). Anyways, when we do this, we encounter the error:

 floatingError.hs:4:47: error:
    • No instance for (Integral Double) arising from a use of ‘eval’
    • In the second argument of ‘(*)’, namely ‘eval a b p’
      In the second argument of ‘(+)’, namely ‘0.001 * eval a b p’
      In the expression: acc + 0.001 * eval a b p

We know from the type signature of the relevant numeric operators (*) and (+) that they accept arguments of the same type. This is the cause of our error; eval is expected to be an integral type. However, we've applied the fromIntegral function to it. Thus, if we remove this, our function application type-checks and our program compiles.

Next, we can check what type signature GHCi infers for solve:

solve :: (Integral b, Integral t) =>
 t -> t -> [Double] -> [b] -> [Double]

Because Int has an instance of the typeclass Integral, we know our declared signature will not conflict with our modified function definitions.


Our final code:

solve :: Int -> Int -> [Double] -> [Int] -> [Double]
solve l r a b = [area]
  where eval a b p = sum . map (\(ai, bi) -> ai * p ^ bi) $ zip a b
        area = foldl (\acc p -> acc + 0.001 * eval a b p) 0 range
        range = map (\a -> (fromIntegral a :: Double ) / 1000) [l*1000..r*1000]

Upvotes: 4

Related Questions