14

I am trying to numerically integrate a function in Haskell using the trapezoidal rule, returning an anti-derivative which takes arguments a, b, for the endpoints of the interval to be integrated.

integrate :: (Float -> Float) -> (Float -> Float -> Float)

integrate f
  = \ a b -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where
      d = (b - a) / n
      n = 1000

In the above, I use

n - for the number of subintervals
d - for the width of each subinterval

This almost works, except for the bound arguments a,b in the lambda. I get the error message:

Not in scope: `b'
Not in scope: `a'

I can understand that the scope of a,b is restricted to just that lambda expression, but is there a workaround in Haskell so that I don't have to write (b-a)/n for each occurrence of d in the above?

Bylextor
  • 213
  • 3
  • 7
  • 7
    TIL: You can't use `where` with lambdas [let vs where](http://www.haskell.org/haskellwiki/Let_vs._Where). See also [where does the `where` clause come in handy in Haskell](http://stackoverflow.com/questions/6032183/where-does-the-where-clause-come-in-handy-in-haskell) – rampion Jan 24 '12 at 16:21
  • Thanks to everyone who replied. I did not realize that this problem leads directly to a well-known let vs where discussion. I also want to thank those who suggested that I write the function as: integrate f a b = ... That's a nice and succinct solution as well. – Bylextor Jan 25 '12 at 19:19

5 Answers5

19

You're thinking you need to return a function which takes two Floats and returns a Float, but actually that's no different to taking two extra Float arguments in your integrate function and using currying (i.e. just don't provide them and the return type will be Float -> Float -> Float).

So you can rewrite your function like this

integrate :: (Float -> Float) -> Float -> Float -> Float

integrate f a b
  = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where
      d = (b - a) / n
      n = 1000

Or you could use let ... in instead of where:

integrate f
  = \a b ->
      let d = (b - a / n)
          n = 1000
      in d * sum [ f (a + d * k) | k <- [0..n] ] - d/2.0 * (f a + f b)
Will Ness
  • 70,110
  • 9
  • 98
  • 181
Matthew Walton
  • 9,809
  • 3
  • 27
  • 36
  • 2
    FWIW, Matthew omitted the parentheses in the type signature, but that is not necessary. You can keep the type signature the same and write the function body in this new way. The two signatures are *exactly* equivalent. – luqui Jan 24 '12 at 17:42
  • Well spotted. I was intending to mention that when I was writing the answer but I must've forgotten. – Matthew Walton Jan 25 '12 at 09:13
  • Thanks. Rewriting the function as integrate f a b = ... is a great solution. – Bylextor Jan 25 '12 at 19:21
4

Sure.

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where 
      d = (b - a) / n
      n = 1000
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
4

You have a lot of work-arounds.

If you don't know any binding syntax except lambda expressions you can do this (which I love the most because of its theoretical beauty, but never use because of its syntactic ugliness):

integrate f
  = \a b -> (\d -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)) 
             ((b - a) / n)
    where
      n = 1000

If you like definitions and only know where-syntax you can do this:

integrate f = go
  where
    n = 1000
    go a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
      where
        d = (b - a) / n

If you also know let-syntax, you can do this:

integrate f = 
  \a b -> let d = (b - a) / n 
          in d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
  where
    n = 1000

Finally, if you remember that a -> (b -> c -> d) is the same as a -> b -> c -> d, you can do the obvious:

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
  where
    n = 1000
    d = (b - a) / n
Rotsor
  • 13,655
  • 6
  • 43
  • 57
  • 1
    There is another reason not to use the first option: lambdas do not have let polymorphism. – luqui Jan 24 '12 at 17:43
  • Thank you. The let option is probably the simplest workaround for my where bound-variable problem. – Bylextor Jan 25 '12 at 19:11
  • Can you tell me a bit more about "go"? I've tried to look it up, but it's very difficult to find any information about "go" in Haskell, because search engines confuse it with the usual English word "go", and "go" does not appear in most book indices. – Bylextor Jan 25 '12 at 19:29
  • @Bylextor, `go` is just an arbitrary name, like `foo`. You use that for a helper function when you run out of imagination. :) – Rotsor Jan 25 '12 at 19:57
2

If you insist on where:

integrate f = \a b -> case () of
    () ->  d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
           where 
               d = (b - a) / n
               n = 1000

Looks pretty nice, does it not? To make the case look a bit more motivated:

integrate f = \a b -> case (f a + f b) of
    fs ->  d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * fs
           where 
               d = (b - a) / n
               n = 1000
Ingo
  • 36,037
  • 5
  • 53
  • 100
1

try:

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where
      d = (b - a) / n
      n = 1000
vivian
  • 1,434
  • 1
  • 10
  • 13