Hacker News new | past | comments | ask | show | jobs | submit login

Currying makes sense if haskell because it’s a curried langage, so you may need the odd uncurry, then curry it back to normal.

In uncurried langages, generic curring has few use cases. Partial application does, and some libraries may want to provide both curried and uncurried versions of some things, but generally “curry” itself is unhelpful.




What's the difference between currying and partial application/closures?


Currying is the conversion of a n-ary function to a chain of n 1-ary functions. So if you have a function `(a, b, c) => d` (a 3-function which returns a result) and you curry it it becomes a => b => c => d (a 1-fn which returns a 1-fn which return a 1-fn which returns a result).

Partial application is the application of an n-ary function to k arguments (k <= n, often but not necessarily l < n) without actually evaluating (calling) the function, so given the same `f = (a, b, c) => d`, `partial(f, 1, 2)` returns a function `c => d`.

A closure is a function which "closes over" its lexical initialisation context and carries it along.

A curried function is trivially partially applicable, just call it with less than `n` parameters and it'll yield a (also curried) function taking `n - k` parameters. It's useful to build a language on that principle, it's not really useful to provide that as a generic utility, because in most scenarios you'd rather just partially apply a function.

A closure is a completely freeform tool so it can do more or less anything you want.


The distinction sounds almost completely syntactic. I.e. foo(a, b) vs foo(a)(b). Is the former not just sugar for the latter?


> Is the former not just sugar for the latter?

Theoretically they're equivalent, but practically not really especially when you introduce more complicated "imperative" features e.g. default parameters or overloading.

They also have API impacts e.g. because Haskell is curried, its HoFs are generally of the form `fn param`, that way you can trivially partially apply the callback and use the same partially applied function to different parameters.

In method-based languages, there is no way to partially apply the callback, and in uncurried languages in general since partial application is a secondary concern you'll often see the callback last regardless so the non-callback parameters don't "get lost" at the tail end of a long callback.


Ah that makes sense about default parameters and overloading. Hmm.


Not quite. In curried languages functions often only take in ONE argument in and return ONE value out. That simplification means the language can only accept the latter form (e.g. foo(a)(b)). Surprisingly this makes the language more powerful not less by unifying all functions to one shape.

For example this allows you to write methods that only take 1 input and 1 output type arg and handle all function cases with any amount of arguments. e.g. in Java/C# you typically have lots of types/anonymous class shapes to define different function shapes to represent lambdas/functions.

e.g. C# delegate syntax (Java has a more verbose way to do this last time I checked)

delegate T Action<T>(); delegate T Func<T>(); delegate R Func<T, R>(T firstArg); delegate R Func<T1, T2, R>(T1 firstArg, T2 secondArg);

Java has things like Consumer<>, Supplier<>, Function<> etc. If you want more than a https://docs.oracle.com/javase/8/docs/api/java/util/function... you typically have to define your own template that's not standard.

However in curried languages all use cases typically are handled by one form and the compiler does the mapping for you (e.g Haskell, F#, etc).

type Func<T, R> = T -> R

The compiler translates a method like foo(a, b) to foo(a)(b) and has certain compile tricks to avoid intermediate allocations if you're specifying all the args at once.

In practice it means writing a LOT less overloads to deal with all the different argument length cases defined and a lot less "Function" types to consider. Instead of an overload in Java for example with Function, BiFunction, etc or C# with Func<>, Func<_, _>, etc you just write the one or two and chain it.


So partial takes an N-ary function and gives an M-ary function, while currying always turns an N-ary function into N unary functions, that is the main distinction?




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: