Summary
Partial application and currying often get muddled; they are related but not the same. They’re both techniques that transform functions that accept multiple arguments, but the difference is in the transformation.
-
Partial application simplifies a function by prefilling arguments, so the newly-returned function accepts less arguments. E.g.
add(2, 3)
becomesaddTwo(3)
, which callsadd()
and prefills the first argument as2
.1 -
Currying simplifies a function by breaking it down into multiple one-argument functions. E.g.
add(3, 1, 4)
becomesadd(3)(1)(4)
. Also called “chaining”, used by libraries such as D3 and jQuery.2 -
bind()
is a native Javascript function that helps us achieve both partial application and currying. It makes sure the function is called from a specificthis
context, with n number of arguments prefilled.
Introduction / Why should I care?
Why should you even care about these functional programming concepts? After all, you can build applications perfectly well without utilizing these. This is true, but getting your code to run is only the first step. Making your code cleaner and more readable by coworkers (or your future self) can make it much easier to maintain.
The major benefit is that these can reduce clutter and make your code more readable, which are great things to help code maintenance in the long run.
Partial application
Partial application is a fancy term for prefilling arguments to a function, nothing more. Why the “partial” in “partial application”? Because only some of the arguments to the function are prefilled. Some arguments are first “partially applied” (passed in), then later the rest of the arguments are passed in.
This is best illustrated by some examples.
Simple addition example
Say we have a basic function to add two numbers together:
|
|
Great, simple enough. But during the course of writing our code, we find that we are often adding the same number over and over, leading to a lot of code repetition:
|
|
This sort of code should especially stand out during refactoring, when we’re often trying to keep things DRY.
If you previously knew nothing about using bind()
, you might try to fix this manually with something like this:
|
|
addTwo()
3 simplifies the code we need to write:
|
|
But if we know about bind()
, this can be simplified even further. This does the same thing as the code above:
|
|
In this case, bind()
returns a new function (which is not immediately called) which makes sure 2
“sticks” as the first argument to add()
.
Here we’re using bind()
to:
- always call
add()
with thethis
context (null
in this case, since we don’t need to care about it in this example) - always prefills
2
as the first argument toadd()
. The value passed toaddTwo()
will be the second argument sent toadd()
.
That’s it!
This is the essence of partial application: prefilling data to simplify our function calls. In this case, bind()
lets us “stick” or “bind” both the this
context and any number of arguments to the function.
Custom console.log()
example
We can even use bind()
to prefill arguments to native functions, such as console.log()
:
|
|
Form input example
Here’s one case where I’ve used partial application in a real app, in this case a React app.
A certain HTML form has a number of text input fields with different input names/keys. Instead of repeating ourselves and writing a separate onChange
for each individual text input (onFirstNameChange
, onLastNameChange
, etc), we can simply write a generic handleChange()
that can update the internal state of the form for any text input.
We’ll partially apply the input name, so it always gets passed as the first argument. Because we’ve prefilled the first argument, the actual event
will arrive as the second argument here:
|
|
Currying
To be honest, in my own code I’ve mostly found partial application to be a much more useful concept than currying. But it’s good to understand, because the concept is used in some popular JavaScript libraries, including D3 and jQuery. If you are a library author, you should at least be aware of this pattern, which can simplify your function interfaces.
Currying often gets mixed up with partial application, but it’s different. Like partial application, currying is a way of simplifying functions with multiple arguments. But unlike partial application, in currying a function with multiple arguments is broken down into several functions, each accepting exactly one argument.
Simple addition example
Let’s reuse the addition example for clarity. We’ll start with the same simple add()
function:
|
|
Currying means breaking this into several functions which accept only one argument, meaning our interface will look like this:
|
|
If you’ve never seen that sort of thing before, you may be taken aback. But don’t be scared, it’s not magic! We can break it down.
Anytime you see a pair of parentheses ()
, you can assume that a function is being executed. From looking at the above code, it’s clear that two functions are being executed, left-to-right, with 2
being passed into the first function (the function named add()
) and 3
being passed into the second function.
The first function seems simple enough since it’s named, but the second function is more mysterious. Where did it come from? Because of the position of the second function, it must be something that was returned from the add()
function.4 So we know that add()
is what’s called a higher-order function, which returns another function. We can start to guess at a skeleton implementation of the curried add()
function:
|
|
In the implementation example above (add(2)(3)
), we know:
2
is passed toadd(x)
, so we knowx
must be2
.- The second function (the nested one) must accept the
3
as a second variable (let’s call ity
) and add it tox
.
With these two pieces of information, we can fill in the gaps in the skeleton function:
|
|
It works! That wasn’t too bad, right?
But this is a pretty naive implementation that only lets you chain exactly two functions together. No more, no less. What if you are adding three or more numbers? You could modify the above to nest even more functions, but things get out of control pretty fast with the level of nesting:
|
|
There’s also the bigger problem that this implementation is inflexible. The user must pass in exactly three numbers to three functions, or the reference to the last function is mistakenly returned:
|
|
Oops! Obviously we wanted a number returned, not a reference to the deepest nested function…
This is where things get a little more tricky. We want the add()
function to be flexible and accept n number of arguments, then keep returning the nested function until it’s done, when it should return the total. But that’s the trick - how does it know when it’s done? There must be some sort of “signal” at the end of the chain so the code knows when to return the total:
|
|
Here’s a clean recursive solution that returns an object with two methods: an add()
that partially applies the ongoing total to add() as the first argument:
|
|
This is pretty clean implementation and the interface is pretty explicit - I like it.
Alternatively, if we really want an implementation where there’s less to type, we can achieve it, but with a more unintuitive implementation. For this, we want the user to only need to type add(2)(3)()
. And note the necessary empty parentheses on the end - that’s our “stop” signal in this case (it was explicitly called done()
in the prior example).
|
|
This implementation is a little messier, because it’s harder to detect the implicit “stop” signal.
Because we’re partially-applying the first argument to add()
, the stop signal actually has the first argument rawX
present, and the second argument rawY
undefined. But the problem is that looks exactly like the first function call add(2)
, which is definitely not a stop signal! So we need some way of distinguishing the stop signal from that first function call. In this implementation I differentiated it by prefilling (partially-applying) rawX as an object that contains the running total. Now the code can see the difference:
add(2)
first call:rawX
is2
,rawY
is undefined()
stop condition:rawX
is a prefilled object,rawY
is undefined
Another currying example
This is an even easier way of implementing currying in your application.
In this example we simply return this
, which refers to the instance of the object, with all its setter methods intact. Pretty straightforward, and we can keep chaining just like in the previous example:
|
|
Footnotes
-
A more formal definition of partial application from Wikipedia: refers to the process of fixing a number of arguments to a function, producing another function of smaller arity (fewer arguments). For instance, starting with function
add(x, y)
with an arity of 2 and producing a new functionaddTwo(y)
with an arity of 1. ↩︎ -
A more formal definition of currying from Wikipedia: “currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument”. For instance, translating
add(x, y, z)
to three functionsadd(x)(y)(z)
. ↩︎ -
This fat arrow syntax is equivalent to
function addTwo(y){ return add(2, y); }
. I prefer fat arrow syntax in this case since it’s simple enough, but I do believe that oftentimes more complex examples need to be written out the long, old way for clarity. Priority should be given to clarity, over trying to fit everything onto one line. ↩︎ -
JavaScript is a functional language, which means that functions are first-class citizens. They can be passed as arguments to other functions, and can be returned from other functions (check out higher-order functions)! ↩︎
Comments