How many times have you heard about making your code 'functional'? You know it makes things more composable, more efficient, and easy to reason with, but what's a practical example of this in action? Something you could use at work?
One technique I like to use in my daily coding is partial application. Wikipedia defines this as:
In computer science, partial application (or partial function application) refers to the process of fixing a >number of arguments to a function, producing another function of smaller arity.
...what?
Put simply, we take a function which could accept multiple inputs, but make it more flexible by splitting it into smaller functions to allow for better reusability. But, it's going to be easier to explain this with some code.
Imagine we have an API call we want to make. We could do something like this:
const getData = async (url: string) => {
try {
const response = await fetch(URL);
const result = await response.json();
} catch (error) {
console.error(error);
}
};
So it works, but if we wanted to then amend our getData
function to use another endpoint on the API, we'd need to pass in a whole new URL. It's also not very descriptive. We can do better!
This is where partial application comes in. We can split our function into its constituent parts:
const getAPIData = (baseUrl: string) => (endpoint: string) => async (callback) => {
try {
const response = await fetch(`${baseUrl}/${endpoint}`);
const result = await response.json();
callback(result);
} catch (error) {
console.error(error);
}
};
So what's different? Here we are using currying to allow us to reuse certain elements of the function, making it more efficient. Check out the usage:
const fetchInternalData = getAPIData("https://internal.myco.io");
// Now we want to fetch employees and clients
const fetchEmployees = fetchInternalData("employees");
const fetchClients = fetchInternalData("clients");
const callback = (data) => console.log(data); // This could do anything you want. It's just a callback.
// So putting it all together
fetchEmployees(callback);
fetchClients(callback);
// We can then use the same function for an external API
const fetchExternalData = getAPIData("https://api.github.com");
const fetchUsers = fetchExternalData("/users");
// We want to get the login names here
fetchUsers((data) => {
console.log(data.map((user) => user.login));
});
// And the avatars here
fetchUsers((data) => {
console.log(data.map((user) => user.avatar_url));
});
And, that's it! Just a simple way of splitting up a rigid function and making it more composable, saving you and your team from needing to reinvent the wheel. It also is easier to reason with which makes code reviews a more pleasant experience for everyone involved!
P.S. Want to sound clever when you talk about this stuff? You could mention that it reduces the arity of your functions. Put simply, arity is just a fancy way of talking about the number of parameters your function takes. If you've coded for a while, then you'll actually have used this already:
- A function that takes one parameter is unary
- A function which takes two parameters is binary
- A function which takes three parameters is ternary
...and so on.
Thanks to Kyle Shevin who I learned this from on Egghead, and Mark Barry for a neat async refactoring which saved me some time when putting this quick post together.
Top comments (1)
Awesome, great use of currying π will definitely try to shift my head around to follow this pattern