Reputation: 117
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:
a
is declared as [Int]
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.
Upvotes: 1
Views: 357
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