Haskell float calculation error

362 Views Asked by At

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:

  • In type declaration a is declared as [Int]
  • therefore Haskell inferred that eval also has type Int, because (*) has the signature Num a => a -> a -> a (so that it only takes parameters of the same type)

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.

1

There are 1 best solutions below

0
On BEST ANSWER

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]