I have a function (exercise 10.11 in Thompson’s The Craft of Functional Programming) which computes an approximation to the value of the definite integral of a function over a domain (a, b). It might not be the most elegant function, but I’m still a a beginner:
import Data.Ratio (Rational, (%), denominator, numerator)
type R = Rational
integrate :: (R -> R) -> R -> (R, R) -> R
integrate f d (a, b) = foldr (+) 0 $ zipWith (*) (map f [a, a + d..b]) (widths d)
where widths :: R -> [R]
widths = \n -> n : widths n
eval :: R -> Double
eval = \r -> (/) (fromIntegral $ numerator r) (fromIntegral $ denominator r)
For instance,
eval $ integrate (\x -> 20 + x^2) (1%10000) (-3%1, 3%1) =
~> 138.00290001
Now, widths d should be equivalent to the expression [d..]. However, if I replace widths by [d..] in integrate, my function outputs incorrect values. For instance:
integrate' :: (R -> R) -> R -> (R, R) -> R
integrate' f d (a, b) = foldr (+) 0 $ zipWith (*) (map f [a, a+d..b]) [d..]
eval $ integrate' (\x -> 20 + x^2) (1%10000) (-3%1, 3%1)
~> 41400870141.0029
Why is this?
Because the two statements aren’t equivalent. Consider what happens when I call
widths d:In other words, you get an infinite list of
ds. However,[d..]returns the list[d, d+1, d+2, ...]. To get an infinite list ofds, you can write[d,d..]; in general,[d,d+n..]creates the infinite list[d, d+n, d+2*n, ...]. More idiomatically, one would generally writerepeat d; repeat has the signaturea -> [a], and just repeats its argument infinitely.Edit: Also, some style, etc., points:
function = \x -> ...is the same asfunction x = ...in all cases. And there’s no particular reason to write yourevalfunction with a prefix/; I’d write iteval r = (fromIntegral $ numerator r) / (fromIntegral $ denominator r); in actual fact, however, I’d just use thefromRational :: Fractional a => Rational -> afunction instead ofeval. You can also replacefoldr (+) 0withsum. And you don’t need to create an infinite list ofds and then multiply everything; more simply, you could just havesum . map (* d) $ map f [a, a + d..b]. Of course, you can then distribute this out, and haveAnd then we have