Tes Engineering Blog

Musings of the Tes Engineering Team

BlogHow we workMeet the TeamOpen rolesWork with us

June 11, 2020

What is currying and why use it?

by Rabea Gleissner

Here at Tes we sometimes use functional programming concepts when they help make our code easier to understand. With around 50 engineers working on approximately 200 microservices it's important that we make our code as maintainable as possible. One of the concepts that can help with this is currying.

The theory

Currying means that you turn a function with several parameters into several functions with one parameter. This works in JavaScript because it allows us to create functions inside other functions. When using currying we create a wrapper function to the original function. This wrapper function calls the original function with a fixed parameter. The calling code can now call one of the more specific wrapper functions rather than the original function.

An example

Before we get into the code, let me explain a bit about the domain we're working with at Tes. One of our products is a job board where schools can post job ads for teachers or other school staff. Candidates can apply for these jobs using an application form which we provide. When the school posts the job ad, they can select which type of application form they want the candidate to fill in.

Let's imagine we had a function that saves the details of a new job ad and the application form that a school selected.

const saveJobAdWithForm = (formType, jobAdDetails) => {
  const validatedFormType = findAndValidateForm(formType);
  const transformedJobDetails = transform(jobAdDetails);
  jobAdStore.save(transformedJobDetails, validatedFormType)

We can call the function like this:

 saveJobAdWithForm('internationalApplicationForm', jobAdDetails);
 saveJobAdWithForm('britishApplicationForm', jobAdDetails);

The job ad details will always be different because each school will write a unique job description, will have different salaries, different expectations for successful candidates etc. But we have a limited number of pre-made application forms.

Let's do some currying! First we create a function inside our existing function that takes the job ad details as an argument. Then we return this function.

  const saveJobAdWithForm = (formType) => {
    const validatedFormType = findAndValidateForm(formType);
    return (jobAdDetails) => {
      const transformedJobDetails = transform(jobAdDetails);
      jobAdStore.save(transformedJobDetails, validatedFormType)

Then we create separate functions with more specific names like this:

const saveAdWithInternationalForm = saveJobAdWithForm('internationalApplicationForm')
const saveAdWithBritishForm = saveJobAdWithForm('britishApplicationForm')

And now we can call these new functions with just the job ad details.


No need to pass the string for the application form type anymore!

But why would we use currying here?

Better naming

The new function names are more specific and easier to understand.

Faster execution

Let's imagine that findAndValidateForm could be a computationally expensive function. In our first example we'd have to run this function every time we call the 'save' function. But after currying it would only run once, on creation of the saveAdWithInternationalForm and saveAdWithBritishForm functions.

Less margin for error

In the first version of the code we had to manually pass in the form type each time we called the saveJobAdWithForm function. What if we introduced some typos and hence bugs? Now the form type strings are locked in and we can be sure that the saveJobAdWithForm function is always called with the correct string because we use the wrapper functions instead.

Hopefully this explains the currying concept and the reasons for using it. And you got an insight into what we do as a business! Although I have to say that sadly our code that saves new job ads is a lot more complex than this...

© Tes Engineering Team2021| All rights reserved
Follow @tes_engineering