Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: where clause referencing bound variables in lambda

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?

like image 483
Bylextor Avatar asked Jan 24 '12 10:01

Bylextor


2 Answers

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
like image 56
Rotsor Avatar answered Sep 23 '22 13:09

Rotsor


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)
like image 36
Matthew Walton Avatar answered Sep 22 '22 13:09

Matthew Walton