Does Haskell allow a let expression for multiple pattern matchings?

teh internets is made of catz picture teh internets is made of catz · Mar 14, 2013 · Viewed 7k times · Source

Let's say I have a function which does some computation, with several patterns; implemented in the form of pattern matching.

Most of these patterns do (along with other things different from one to another) a treatment on a parameter, for which I use an intermediary variable in a let expression. But I find it really redundant to have the same let on many patterns, and I wonder if there is a way to define a let for several patterns?

Here is an example of my duplicated let :

data MyType a = Something a | Another Int [a]

myFunc (Something x) = -- return something, this isn't the point here
myFunc (Another 0 xs) =
    let intermediary = some $ treatment xs
    in doSthg intermediary 1 
myFunc (Another 1 (x:xs)) =
    let intermediary = some $ treatment xs
    in doSthg1 intermediary 1 x
myFunc (Another 2 (x:x':xs)) =
    let intermediary = some $ treatment xs
    in doSthg2 intermediary 2 x x'

You can see that the parameter xs is always present when I use it for intermediary, and this could be factorised. It could easily be achieved by using a helper function but I was wondering if what I am asking is possible without one. Please try to keep it simple for a beginner, and I hope my example is clear enough.

Answer

yatima2975 picture yatima2975 · Mar 14, 2013

This particular problem can be worked around as follows:

myFunc2 (Something x) = returnSomething x
myFunc2 (Another n ys) = 
    let xs = drop n ys
        x = head ys 
        x' = head (tail ys)
        intermediate = some $ treatment xs 
    in case n of
        0 -> doSomething intermediate n
        1 -> doSomething1 intermediate n x
        2 -> doSomething2 intermediate n x x'

Thanks to lazy evaluation x and x' will be only evaluated if their value is needed.

However - and this is a big however! - your code will give a runtime error when you try to call myFunc2 (Another 2 []) (and if doSomething2 actually uses x!) because to find out what x is, we need to evaluate head ys - and that'll crash for an empty list. The code you gave as an example also won't work (another runtime error) for Another 2 [] since there's no matching pattern, but there it's easier to supply a fall-back case.

This might not be a problem if you control the input and always make sure that the list in Another is long enough, but it's important to be aware of this issue!