DEV Community

Aled Wassell
Aled Wassell

Posted on

Function Currying, What's the use?

I've been working as a dev for a little over a year now,
gathering experience using functional programming in JavaScript and busting out all those neat FP tricks you learn about early on, but having no clue how or where to use them.
Exploring function Currying at the moment, having read examples online but still figuring out how I can apply it in daily use.
Any discussion on function Currying uses will be fab!
Aled

Top comments (4)

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

I believe the end goal of currying is partial application. Essentially, currying is how the function is structured, and partial application is what you can do with that structure.

The main reason you want to use partial application is with functions which take other functions as arguments. For example, Array.map.

const sayHello = offset => (name, index) =>
    return "Hello, " + name + ". Your number is " + (offset + index) + ".";

const names = [ "Deqwan", "Samir" ];
const offset = 5;

names
    .map(sayHello(offset))
    .forEach(console.log);

In the above, offset is curried. A non-curried equivalent would be.

const sayHello = (offset, name, index) =>
    return "Hello, " + name + ". Your number is " + (offset + index) + ".";

const names = [ "Deqwan", "Samir" ];
const offset = 5;

names
    .map((name, index) => sayHello(offset, name, index))
    .forEach(console.log);

In the latter example, it was necessary to make an adapter function for Array.map in order to use sayHello. Whereas in the previous example, all that was necessary was to partially apply an argument, and that resulted in a function that matched the signature of map.

In Javascript, currying is somewhat less compelling because most built-in functions are not curried, so using them may require extra adapter functions anyway. Plus, applying arguments requires parenthesis which is a bit of extra noise compared to commas. But it is still useful sometimes.

Sometimes devs use partial application in place of classes... it's equivalent to passing in a value to a constructor and keeping it as a private variable for subsequent method calls. But I don't care for this use case as much. I mainly use it for so-called "higher order" functions such as Array.map which take functions are arguments.

To be fair, I don't use Javascript much. I am speaking of when I use partial application in functional languages like Elm and F#. These languages are curried by default.

Collapse
 
aledwassell profile image
Aled Wassell

Thanks for all the replies, so I finally found an application for currying, your advice was really useful!
This week I have been working on getting pagination working on a list of lists represented as tables that might have thousands and thousands of results.
So querying the server with page number and page size was key to getting it all working.
Users will select a list in a table to display the results within that list as another table and should be able to paginate.

get_list_curry = function (list_id) {
  return (search_params) => {
    return new Promise(
      function (resolve, reject) {
        numbersProvider.get({id: list_id}, {}, (data) => {
          list = data;
          resolve();
        });
      }
    );
  };
};
 //...
let paginate = get_list_curry(list_id);

paginate({pageNumber: 1, pageSize: 25});
paginate({pageNumber: 2, pageSize: 50});
paginate({pageNumber: 3, pageSize: 75});

Thanks for the replies.

Aled

Collapse
 
avalander profile image
Avalander • Edited

Basically, curried and partially applied functions allow you to inject data to the function at different stages, which is convenient to reuse functionality without having to inject the same data all over the place.

One example is a function that has two or more parameters and one of them will always be the same while the other can change.

For instance, imagine that we are writing a function to read an entry from a database by id. That function would need a connection to the database and an id to query. We could write it like this.

const getPony = (connection, id) =>
    connection.select('ponies')
        .where({ id })

getPony(connection, 1)

But now you need to pass the connection object around every time you want to query the database. Even worse, any code that reads ponies from the database will have to know both, about the getPony function and the connection object.

Let's consider what happens if we curry that function.

const makeGetPony = connection => id =>
    connection.select('ponies')
        .where({ id })

// When we initialise our app.
const getPony = makeGetPony(connection)

// Anywhere else
const twilight = getPony(1)

Now we can create the function getPony with the connection object and any code needing to read ponies only needs to know about that function.

What if our database stores other kinds of data besides ponies? –no clue why anybody would store anything other than ponies, but for the sake of the example, let's imagine it has a real use case. We might want to pass the name of the table we are querying to the function, so that it can query different tables. However, passing the same string over an over would be inconvenient, especially if we need to change the name of the table in the future. Better have it only in one place. We could add a new parameter to our curried function.

const makeGetTable = connection => table => id =>
    connection.select(table)
        .where({ id })

// When we initialise our app
const makeGetById = makeGetTable(connection)

const getPony = makeGetById('ponies')
const getDragon = makeGetById('dragons')
const getCat = makeGetById('cats')

// Anywhere else in our application
const twilight = getPony(1)
const fluffykins = getDragon(1)
const garfield = getCat(1)

Voilà, we only need to pass our connection object once, and we can use the intermediate function to create functions to query a different table each. Then we can pass these functions around and the rest of the code doesn't need to know about the connection object or table names that might change in the future or may be hard to remember or we will type incorrectly.

Collapse
 
peter profile image
Peter Kim Frank