Partial application fixes some of a function's arguments and returns a new function that takes the remaining ones: partial(volume, 2) is a function of (w, h). The built-in way is fn.bind(thisArg, ...preset), which also lets you fix this.
Currying transforms a function of several arguments into a sequence of functions that each take some arguments, running the original once enough have been supplied: curry(f)(a)(b)(c) is equivalent to f(a, b, c). Strictly, a curried function takes one argument at a time; the JavaScript version below also lets you pass several at once.
Difference: partial application is a one-step operation that you apply to an existing function with specific values. Currying is a reshaping of the function itself, so every partial application is available afterwards.
How the implementation works: each call collects its arguments in a closure. If args.length >= fn.length, call the original with them; otherwise return a function that continues collecting. fn.length is the arity.
Caveat: length counts only the parameters before the first default or rest parameter. A curried function with defaults or ...rest fires too early, or never waits. Accept an explicit arity for such functions.
Use cases: configuring a general function into specialised ones (const getUserById = fetchById(api)), building point-free pipelines with map/filter, and composing (see compose and pipe).