Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do Haskell currying and pattern matching work together?

I'm new to Haskell. I understand that functions are curried to become functions that take one parameter. What I don't understand is how pattern matching against multiple values can be achieved when this is the case. For example:

Suppose we have the following completely arbitrary function definition:

myFunc :: Int -> Int -> Int
myFunc 0 0 = 0
myFunc 1 1 = 1
myFunc x y = x `someoperation` y

Is the partially applied function returned by myFunc 0 essentially:

partiallyAppliedMyFunc :: Int -> Int
partiallyAppliedMyFunc 0 = 0
partiallyAppliedMyFunc y = 0 `someoperation` y

Thus removing the extraneous pattern that can't possibly match? Or.... what's going on here?

like image 508
JamesSwift Avatar asked Sep 30 '12 05:09

JamesSwift


1 Answers

Actually, this question is more subtle than it may appear on the surface, and involves learning a little bit about compiler internals to really answer properly. The reason is that we sort of take for granted that we can have nested patterns and patterns over more than one term, when in reality for the purposes of a compiler the only thing you can do is branch on the top-level constructor of a single value. So the first stage of the compiler is to turn nested patterns (and patterns over more than one value) into simpler patterns. For example, a naive algorithm might transform your function into something like this:

myFunc = \x y -> case x of
    0 -> case y of
        0 -> 0
        _ -> x `someoperation` y
    1 -> case y of
        1 -> 1
        _ -> x `someoperation` y
    _ -> x `someoperation` y

You can already see there's lots of suboptimal things here: the someoperation term is repeated a lot, and the function expects both arguments before it will even start to do a case at all; see A Term Pattern-Match Compiler Inspired by Finite Automata Theory for a discussion of how you might improve on this.

Anyway, in this form, it should actually be a bit more clear how the currying step happens. We can directly substitute for x in this expression to look at what myFunc 0 does:

myFunc 0 = \y -> case 0 of
    0 -> case y of
        0 -> 0
        _ -> 0 `someoperation` y
    1 -> case y of
        1 -> 1
        _ -> 0 `someoperation` y
    _ -> 0 `someoperation` y

Since this is still a lambda, no further reduction is done. You might hope that a sufficiently smart compiler could do a bit more, but GHC explicitly does not do more; if you want more computation to be done after supplying only one argument, you have to change your definition. (There's a time/space tradeoff here, and choosing correctly is too hard to do reliably. So GHC leaves it in the programmer's hands to make this choice.) For example, you could explicitly write

myFunc 0 = \y -> case y of
    0 -> 0
    _ -> 0 `someoperation` y
myFunc 1 = \y -> case y of
    1 -> 1
    _ -> 1 `someoperation` y
myFunc x = \y -> x `someoperation` y

and then myFunc 0 would reduce to a much smaller expression.

like image 87
Daniel Wagner Avatar answered Sep 18 '22 15:09

Daniel Wagner