views:

128

answers:

2

Can 2 or more equations defining a function in Haskell share the same where / let block?

Let me present a contrived example to illustrate the question.

First, consider the following code as a starting point:

someFunction v1 v2 v3 = difference ^ v3
   where
      difference = v1 - v2

So far, so good. But then, imagine I need to deal with an "alternative case", where I need to return zero if v3 == 99 and difference < 4 (completely arbitrary, but let's say those are my requirements).

My first thought would be to do this:

someFunction v1 v2 99 | difference < 4 = 0
someFunction v1 v2 v3 = difference ^ v3
   where
      difference = v1 - v2

However, that won't work because the first equation for someFunction and the second equation for someFunction are not both sharing the same where block. This is not a big deal in this contrived example because there is only one variable in the where block ("difference"). But in a real world situation, there could be a large number of variables, and it would be unacceptable to repeat them.

I already know how to solve this by using guards and having only one equation. The question is, is there a way for multiple equations to share the same where / let clause? Because it seems desirable to have multiple equations with different patterns instead of being forced to have just one equation with many guards.

+7  A: 

One option would be to lift the function into the where block itself:

someFunction v1 v2 = f
    where
        f 99 | difference < 4 = 0
        f v3 = difference ^ v3
        difference = v1 - v2
bdonlan
Oh man, that is so simple, clean and "functionalish"! Now that you mention it it makes perfect sense! Thanks, that's exactly the kind of insight I was looking for!
Charlie Flowers
It's interesting to note that all bindings in a where clause (or a let expression) are mutually recursive in Haskell. In this example that means `f` can use `difference` but `difference could also use `f` (if it wanted to). So whenever you have this sort of a problem, it can usually be fixed by putting things in the same where clause, like @bdonian says.
Tom Lokhorst
Excellent, thanks for the additional info Tom.
Charlie Flowers
Oh, wait. I just realized this code gives me "not in scope" errors for v1 and v2, on the line where `difference` is defined. Makes sense ... v1 and v2 are pattern match bindings. Is there a way around that?
Charlie Flowers
Oops. How's this then? :)
bdonlan
Ahh, right. The same principle that I didn't see the first time around, that your response showed me, simply needed to be applied again! Fascinating, thanks.
Charlie Flowers
A: 

I think you can't. Probably your best solution is something like:

someFunction v1 v2 v3 | v3==99 && difference<4 = 0
                      | otherwise = difference ^ v3
                      where difference = v1 - v2
hiena