3

I'm having trouble understanding curried and uncurried functions. All the sites I Google'd to try to provide me a definition were unclear to me.

In one example I found them saying that

max 4 5 is the same as (max 4) 5

But I don't understand what they're doing. How can you have a function (max 4) when max requires 2 parameters? I'm completely lost.

dtgee
  • 1,272
  • 2
  • 15
  • 30
  • 1
    See also [What is the advantage of currying?](http://programmers.stackexchange.com/q/185585/61231). – Petr Mar 21 '13 at 05:48
  • I think this question and my answer there might help: http://stackoverflow.com/questions/8148253/how-are-functions-curried/8148957 – Ben Mar 21 '13 at 05:54

4 Answers4

14

The trick with Haskell is that functions only take one argument. This seems totally crazy but it actually works.

A haskell function:

foo :: Int -> Int -> Int
foo a b = a + b

Really means: A function which takes in 1 argument, and then returns another function which takes one argument. This is called currying.

So using this, we can really write this function definition like this:

foo :: Int -> (Int -> Int) --In math speak: right associative

and mean exactly the same thing.

This is actually super useful because we can now write concise code like:

foo1 :: Int -> Int
foo1 = foo 1

Since function application in haskell is just whitespace, most of the time you can just pretend that curried functions are uncurried (taking more than one argument and just returning a result).

If you really really realllly need uncurried functions: Use tuples.

uncFoo :: (Int, Int) -> Int
uncFoo (a, b) = a + b

Edit

OK so to understand whats going on with partial application consider bar

bar a b c = [a, b, c]

The thing is, the compiler will desugar what you just typed into lambdas like this

bar = \a ->
      \b ->
      \c ->
           [a, b, c]

This takes advantage of closures (each inner function can 'remember' the arguments to the previous ones.

so when we say bar 1, the compiler goes and looks at bar and sees the outermost lambda, and applies it giving

bar 1 = \b ->
        \c ->
             [1, b, c]

If we say bar 1 2

bar 1 2 = \c ->
                [1, 2, c]

If what I mean when I say "apply" is hazy, then it may help to know that I really mean beta reduction from lambda calculus.

phimuemue
  • 34,669
  • 9
  • 84
  • 115
daniel gratzer
  • 52,833
  • 11
  • 94
  • 134
  • I understand that it takes one argument and returns another function which takes another argument, but how does it calculate anything? So in your case if I had the function `foo a` how is it supposed to do anything with just one argument? I think I need a more detailed explanation as to the process of it working. – dtgee Mar 21 '13 at 04:52
  • Look at the lambdas. That's what's actually happening. Hang on, im editing cause this is too small – daniel gratzer Mar 21 '13 at 04:52
  • @user1831442 There you are – daniel gratzer Mar 21 '13 at 05:03
  • 1
    Hmmm, so are you saying that for `foo a b = a + b` `(foo 4) 5` just plugs in `4` so we get `4 + b`? What I'm getting from your example is that `bar` is just plugging stuff in. Gah, currying seems like such a simple concept to understand, why am I having so much trouble understanding it?! – dtgee Mar 21 '13 at 05:09
  • Yeah thats it! It's basically boils down to substitution. And don't worry everyone who learns Haskell hits stuff like this :) – daniel gratzer Mar 21 '13 at 05:10
  • Okay, so what if I had three parameters then? `foo a b c = a + b + c`. Would this be equal to `((foo a) b) c`? And thanks four your help, I really appreciate it! – dtgee Mar 21 '13 at 05:13
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/26597/discussion-between-jozefg-and-user1831442) – daniel gratzer Mar 21 '13 at 05:15
4

Depending on your background, you may find this paper enlightening: How to Make a Fast Curry: Push/Enter vs Eval Apply. While it's true that multi-argument functions can be understood as functions which bind a single parameter and return another function: max = (\a -> (\b -> if a > b then a else b)), the actual implementation is quite a bit more efficient.

If the compiler knows statically that max requires two arguments, the compiler will always translate max 4 5 by pushing the two arguments on the stack (or in registers) and then invoking max. This is essentially the same as how a C compiler would translate max(4, 5).

On the other hand, if for instance max is an argument to a higher-order function, the compiler may not know statically how many arguments max takes. Perhaps in one instance it takes three, so max 4 5 is a partial application, or perhaps in another it takes only one and max 4 generates a new function to which 5 is applied. The paper discusses the two common approaches to handling the case where the arity isn't known statically.

kputnam
  • 1,061
  • 7
  • 8
1

You probably got your answer already, but just to reiterate:

If we have

add x y = x + y

then we can say the following:

add = \ x y -> x + y
add 3 = \ y -> 3 + y
add 3 5 = 3 + 5 = 8

You ask "how can max 3 calculate anything?", and the answer is "it can't". It just gives you another function. This function can do something when you call it, but you don't "get an answer" as such until all the arguments have been supplied. Until then, you just get functions.

Most of the time, this is just a useful syntactic shortcut. For example, you can write

uppercase :: String -> String
uppercase = map toUpper

instead of having to say

uppercase xs = map toUpper xs

Notice that if map had its arguments the other way around, we wouldn't be able to do this (you can only curry away the last argument, not the _first), so it can be important to think about which order you define your functions' arguments.


I say "most of the time" because this is more than syntax sugar. There are several places in the language where you can handle functions with different numbers of arguments polymorphically because of currying. Every function either returns an answer, or another function. If you think of it like a linked list (which either contains the next item of data or the end-of-list marker), you can see how this lets you recursively process functions.

So what the heck do I mean by that? Well, for example, QuickCheck can test functions with any number of arguments (provided there's a way to auto-generate test data for each argument). This is possible because function types are curried. Every function either returns another function, or a result. If you think about it like a linked list, you can imagine QuickCheck recursively iterating over the function until no more arguments are left.

The following code snippet may or may not explain the idea:

class Arbitrary a where
  autogenerate :: RandomGenerator -> a

instance Arbitrary Int
instance Arbitrary Char
...

class Testable t where
  test t :: RandomGenerator -> Bool

instance Testable Bool where
  test rnd b = b

instance (Arbitrary a, Testable t) => Testable (a -> t) where
  test rnd f = test $ f (autogenerate rnd)

If we have a function foo :: Int -> Int -> Bool, then this is Testable. Why? Well, Bool is testable, therefore so is Int -> Bool, and therefore so is Int -> (Int -> Bool).

By contrast, each size of tuple is a different size, so you have to write separate functions (or instances) for each and every size of tuple. You can't recursively process tuples, because they don't have a recursive structure.

MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
0

To relate to your own example...

Suppose that you want a function that gives the maximum of 4 and the functions argument. You could implement it like this:

max4 :: Integer -> Integer
max4 x = max 4 x

What max 4 does is just to return the function max4 created on the fly.

md2perpe
  • 3,372
  • 2
  • 18
  • 22