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 typeInt
, because(*)
has the signatureNum 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.
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:
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 thefromIntegral
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
:Because
Int
has an instance of the typeclassIntegral
, we know our declared signature will not conflict with our modified function definitions.Our final code: