What is the difference between currying and partia

2019-01-01 07:40发布

I quite often see on the Internet various complaints that other peoples examples of currying are not currying, but are actually just partial application.

I've not found a decent explanation of what partial application is, or how it differs from currying. There seems to be a general confusion, with equivalent examples being described as currying in some places, and partial application in others.

Could someone provide me with a definition of both terms, and details of how they differ?

13条回答
何处买醉
2楼-- · 2019-01-01 08:05

I could be very wrong here, as I don't have a strong background in theoretical mathematics or functional programming, but from my brief foray into FP, it seems that currying tends to turn a function of N arguments into N functions of one argument, whereas partial application [in practice] works better with variadic functions with an indeterminate number of arguments. I know some of the examples in previous answers defy this explanation, but it has helped me the most to separate the concepts. Consider this example (written in CoffeeScript for succinctness, my apologies if it confuses further, but please ask for clarification, if needed):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

This is obviously a contrived example, but notice that partially applying a function that accepts any number of arguments allows us to execute a function but with some preliminary data. Currying a function is similar but allows us to execute an N-parameter function in pieces until, but only until, all N parameters are accounted for.

Again, this is my take from things I've read. If anyone disagrees, I would appreciate a comment as to why rather than an immediate downvote. Also, if the CoffeeScript is difficult to read, please visit coffeescript.org, click "try coffeescript" and paste in my code to see the compiled version, which may (hopefully) make more sense. Thanks!

查看更多
唯独是你
3楼-- · 2019-01-01 08:05

There are other great answers here but I believe this example (as per my understanding) in Java might be of benefit to some people:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

So currying gives you a one-argument function to create functions, where partial-application creates a wrapper function that hard codes one or more arguments.

If you want to copy&paste, the following is noisier but friendlier to work with since the types are more lenient:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}
查看更多
大哥的爱人
4楼-- · 2019-01-01 08:11

Simple answer

Curry: lets you call a function, splitting it in multiple calls, providing one argument per-call.

Partial: lets you call a function, splitting it in multiple calls, providing multiple arguments per-call.


Simple hints

Both allow you to call a function providing less arguments (or, better, providing them cumulatively). Actually both of them bind (at each call) a specific value to specific arguments of the function.

The real difference can be seen when the function has more than 2 arguments.


Simple e(c)(sample)

(in Javascript)

function process(context, success_callback, error_callback, subject) {...}

why always passing the arguments, like context and the callbacks, if they will be always the same? Just bind some values for the the function

processSubject = _.partial(process, my_context, my_success, my_error)

and call it on subject1 and foobar with

processSubject('subject1');
processSubject('foobar');

Comfy, isn't it?

查看更多
心情的温度
5楼-- · 2019-01-01 08:12

I had this question a lot while learning and have since been asked it many times. The simplest way I can describe the difference is that both are the same :) Let me explain...there are obviously differences.

Both partial application and currying involve supplying arguments to a function, perhaps not all at once. A fairly canonical example is adding two numbers. In pseudocode (actually JS without keywords), the base function may be the following:

add = (x, y) => x + y

If I wanted an "addOne" function, I could partially apply it or curry it:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

Now using them is clear:

addOneC(2) #=> 3
addOneP(2) #=> 3

So what's the difference? Well, it's subtle, but partial application involves supplying some arguments and the returned function will then execute the main function upon next invocation whereas currying will keep waiting till it has all the arguments necessary:

curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want

partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error

In short, use partial application to prefill some values, knowing that the next time you call the method, it will execute, leaving undefined all unprovided arguments; use currying when you want to continually return a partially-applied function as many times as necessary to fulfill the function signature. One final contrived example:

curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works

partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

Hope this helps!

UPDATE: Some languages or lib implementations will allow you to pass an arity (total number of arguments in final evaluation) to the partial application implementation which may conflate my two descriptions into a confusing mess...but at that point, the two techniques are largely interchangeable.

查看更多
皆成旧梦
6楼-- · 2019-01-01 08:13

I have answered this in another thread https://stackoverflow.com/a/12846865/1685865 . In short, partial function application is about fixing some arguments of a given multivariable function to yield another function with fewer arguments, while Currying is about turning a function of N arguments into a unary function which returns a unary function...[An example of Currying is shown at the end of this post.]

Currying is mostly of theoretical interest: one can express computations using only unary functions (i.e. every function is unary). In practice and as a byproduct, it is a technique which can make many useful (but not all) partial functional applications trivial, if the language has curried functions. Again, it is not the only means to implement partial applications. So you could encounter scenarios where partial application is done in other way, but people are mistaking it as Currying.

(Example of Currying)

In practice one would not just write

lambda x: lambda y: lambda z: x + y + z

or the equivalent javascript

function (x) { return function (y){ return function (z){ return x + y + z }}}

instead of

lambda x, y, z: x + y + z

for the sake of Currying.

查看更多
与风俱净
7楼-- · 2019-01-01 08:16

The easiest way to see how they differ is to consider a real example. Let's assume that we have a function Add which takes 2 numbers as input and returns a number as output, e.g. Add(7, 5) returns 12. In this case:

  • Partial applying the function Add with a value 7 will give us a new function as output. That function itself takes 1 number as input and outputs a number. As such:

    Partial(Add, 7); // returns a function f2 as output
    
                     // f2 takes 1 number as input and returns a number as output
    

    So we can do this:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Currying the function Add will give us a new function as output. That function itself takes 1 number as input and outputs yet another new function. That third function then takes 1 number as input and returns a number as output. As such:

    Curry(Add); // returns a function f2 as output
    
                // f2 takes 1 number as input and returns a function f3 as output
                // i.e. f2(number) = f3
    
                // f3 takes 1 number as input and returns a number as output
                // i.e. f3(number) = number
    

    So we can do this:

    f2 = Curry(Add);
    f3 = f2(7);
    f3(5); // returns 12
    

In other words, "currying" and "partial application" are two totally different functions. Currying takes exactly 1 input, whereas partial application takes 2 (or more) inputs.

Even though they both return a function as output, the returned functions are of totally different forms as demonstrated above.

查看更多
登录 后发表回答