Currying vs. partial application

When I first came across the terms “currying” and “partial application” I was a bit confused about the difference. Here is my attempt at an explanation1. I’m not 100% confident of my understanding, so please point out any inconsistencies – I’m happy to be corrected :).

Consider a call that takes 2 arguments and returns some value2:

f : (String, Int) -> Widget
// Example call:
f("a", 1)

Currying is the process of converting this to a function that takes a single argument, and returns another function that takes a single argument.

f' : String -> (Int -> Widget)
// or just:
f' : String -> Int -> Widget

// Example call:

For functions with more than 2 arguments, we can use currying to convert it to a series of functions that each take a single argument:

g : (a,b,c,d) -> e
g' : a -> (b -> (c -> (d -> e)))
// or just:
g' : a -> b -> c -> d -> e

Partial application is when we can have a function that takes multiple arguments, give it a subset of those arguments, and get back a function that will take the remaining arguments. With curried functions we get this ability for free, but you could imagine a language feature that implements this for uncurried functions:

// With curried function:
g' : a -> b -> c -> d -> e
let partialApplyG' = g'(1)(2)
// partialApplyG' : c -> d -> e
partialApplyG'(3)(4) // <- providing the rest of the arguments

// With uncurried function (via our imagined language feature):
g : (a,b,c,d) -> e
let partialApplyG = g (1, 2)
// partialApplyG : (c, d) -> e
partialApplyG (3, 4) // <- providing the rest of the arguments

I think it is correct to say that all curried functions support partial application, but not all partial application implementations require currying.

  1. Also left as a comment to this post, modified slightly here

  2. See Reading type annotations if this style of writing out types is unfamiliar.