DEV Community

Abdullah Al Numan
Abdullah Al Numan

Posted on • Edited on

Curry Functions in JavaScript

This is a series of articles on currying functions in JavaScript. It consists of an introductory post (this one) and several follow ups that delve into basic and advanced currying with JavaScript.

In the next post, named Basic Curried Functions in JavaScript, we'll see an example of a basic curried function. And in the follow up posts, we'll gradually cover more advanced techniques for currying existing functions.

Before that, first, we'll consider a few underlying terminologies.

Arity

The arity of a function is the number of arguments a function expects at invocation.

Unary Functions

Unary functions are functions that take only one argument, i.e. their arity is one. They are very common in Haskell, where a function can only be unary.

Curried Functions

The simplest form of a curried function is a unary function that returns another unary function. The returned function can also return another unary function. Returning nested unary functions continues in sequence to achieve the results of a multi-argument function. This gives us the ability to call the function with one argument at a time, sequentially.

// Regular multi-argument function
const createMessage = (greeting, name, message) => {
  return `${greeting}, ${name}! ${message}`);
};

// A curried function helping achieve the same thing
const createMessage = greeting => name => message => `${greeting}, ${name}! ${message}`);

// So now we can do this:
createMessage('Hello')('World')('Whadup?');
Enter fullscreen mode Exit fullscreen mode

In Haskell, this is the technique used to deliver functions that effectively accept multiple arguments, but one at a time. The word "curry" comes from Haskell Curry.

Higher Order Functions

Curried functions are also Higher Order Functions, or HOFs -- as they always return other functions, and also commonly take in the to-be-curried function.

Variadic Curried Functions

A variadic curried function is a more sophisticated curried function that is able to take variable number of arguments per call. Each returned function may take a single argument, or more than one argument. Basically, we can also do this:

createMessage('Hello', 'World')('Whadup?');
// or
createMessage('Hello')('World', 'Whadup?');
Enter fullscreen mode Exit fullscreen mode

Top comments (17)

Collapse
 
peerreynders profile image
peerreynders • Edited

A variadic Curry function is a more sophisticated Curry function that is able to take variable number of arguments per call.

In my mind your example is simply the more general concept of partial application.

"The ability to apply only some of a function’s arguments is called partial application."
[Haskell Programming from First Principles, Chapter 5: Types - Partial Application; p.129]

A variadic curry typically requires a superpositioned result, i.e.:

  • a function capable of accepting an additional argument AND
  • access to the intermediate result.

λvc: Variadic Auto-Curried Lambda Calculus

Example:

const sum = (n0 = 0) => {
  const fn = (n1) => sum(n0 + n1);
  fn.valueOf = () => n0;
  fn.toString = () => String(n0);
  return fn;
};

console.log(sum(1)(2)(3)(4)); // 10
Enter fullscreen mode Exit fullscreen mode

Currying solves a problem that JavaScript doesn't have:

Each lambda can only bind one parameter and can only accept one argument. Functions that require multiple arguments have multiple, nested heads. When you apply it once and eliminate the first (leftmost) head, the next one is applied and so on. This formulation was originally discovered by Moses Schönfinkel in the 1920s but was later rediscovered and named after Haskell Curry and is commonly called currying.

Haskell Programming from First Principles

Currying versus partial application (with JavaScript code):

  • Currying always produces nested unary (1-ary) functions. The transformed function is still largely the same as the original.
  • Partial application produces functions of arbitrary arity. The transformed function is different from the original – it needs less arguments.
Collapse
 
anewman15 profile image
Abdullah Al Numan

How exactly does Haskell implement the said superposition? Does Function.prototype.bind do superposition in JS?

I think I just got another reason to hate Physics.

Collapse
 
peerreynders profile image
peerreynders • Edited

How exactly does Haskell implement the said superposition?

I don't know. If we are talking about the same thing, Haskell will automatically perform a partial application if you provide more than one but less than all of the required arguments.

In principle superposition just implies that two results occupy the same space, i.e. a function and a primitive value. In reality that is smoke and mirrors.

sum simply returns an object that is callable (i.e. a function object) but also makes its primitive value accessible via Object.prototype.valueOf().

Does Function.prototype.bind do superposition in JS?

No, it simply creates a bound function (which is not possible with arrow functions).

Just be careful because the first this binding wins:

function sum(a, b, c) {
  return this.value + a + b + c;
}

const one = {
  value: 1,
};

const ten = {
  value: 10,
};

sum2 = sum.bind(one, 2);

console.log(sum2(3, 4)); // 10

sum23 = sum2.bind(ten, 3);

console.log(sum23(4)); // 10 not 19!
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
anewman15 profile image
Abdullah Al Numan • Edited

Nice, thanks!

To make this work, variadic functions must be declared within the context of another non-nullary function. When a variadic function is applied, we evaluate its body with the proper substitution as though it were a regular unary function. However, we also construct a new version of the variadic function with an updated environment. In this environment, the value bound during application of the parent function is replaced with the result of the variadic application. This is kept around in the variadic closure for subsequent applications (if any).

This is wealth of info. It talks about an enclosing context, updated environment.

I suppose in your sum, you're creating the said "equally probable environment" with recursion ?

const sum = (n0 = 0) => {
  const fn = (n1) => sum(n0 + n1);
  fn.valueOf = () => n0;
  fn.toString = () => String(n0);
  return fn;
};
Enter fullscreen mode Exit fullscreen mode

I've seen examples using recursion both with and without Function.prototype.bind, but kind of wondering how superposition is being dealt with ?

Thread Thread
 
peerreynders profile image
peerreynders • Edited

I suppose in your sum, you're creating the said "equally probable environment" with recursion ?

Not sure what that means.

It's not really recursion in the sense of "body recursion" but more in the sense of trampolining.

but kind of wondering how superposition is being dealt with ?

It's all just conceptual.

Basically we are breaking the expected general contract of a function object by monkey patching the instance to shadow the expected implementations of Object.prototype.valueOf() and Function.object.toString() available on prototype chain.

A more honest representation would be:

const sum = (n0 = 0) => {
  const fn = (n1: number) => sum(n0 + n1);
  fn.valueOf = () => n0;
  fn.toString = () => String(n0);
  return fn;
};

const result1 = sum(1)(2)(3)(4); // const result1: { (n1: number):... , valueOf(): number, toString(): string }
const result2 = result1.valueOf() // const result2: number

console.log(result1, result2); // 10, 10
Enter fullscreen mode Exit fullscreen mode
 
peerreynders profile image
peerreynders • Edited

That can be read as

Stated plainly:

  • JavaScript functions can already accept more than one argument at a time. There is no need to resort to the "accept a single argument and return a function that accepts the next argument" hack (unless there is a need to) - regardless of what fascinating emergent properties that hack may have.
  • If only a subset of those arguments happen to be available at some point in time a closure is always within reach.

From my personal taste/preference your approach is way more verbose.

Overly verbose code is hard to read, so is overly terse code. Closures can make code more difficult to understand, as well as multiple levels of higher order functions and lots of people have trouble with recursion. It's practically a tautology that code that is written in a style you are unfamiliar with is harder to read.

If universal readability was the aim we'd probably end up with something as uninspired as Go.

use whatever makes the team more comfortable

For me it has less to do with my own comfort level but more with Andrea Giammarchi yelling at me in the back of my head to stop creating all this unnecessary garbage for the garbage collector to collect 😉.

In Haskell currying is built in; in JavaScript it could quickly devolve into "premature pessimization".


Perhaps replaceablity should be more important than reusability.

Collapse
 
darleneri profile image
Darlene Rivera

Nice... please tell me more about functions

Collapse
 
fparedlo profile image
fparedlo

Ok, another way to create unnecessary overhead :(

BTW the correct ES6 syntax should be :
const createMessage = (greeting, name, message) => {}

and not :
const createMessage(greeting, name, message) => {}

I believe.

 
peerreynders profile image
peerreynders

It's always been my contention that currying is hogging all the glory when in fact partial application is doing most of the heavy lifting (unless you are actually constrained to lambda calculus).

That said, in most cases plain functions (expressions, really) compose just fine ("Look ma, no closures").

const add = (a, b) => a + b;
const rreduce = (reducer, values, index, result) =>
  index < values.length
    ? rreduce(reducer, values, index + 1, reducer(result, values[index]))
    : result;
const reduce = (reducer, initial, values) =>
  rreduce(reducer, values, 0, initial);
const sum = (values) => reduce(add, 0, values);

console.log(sum([1, 2, 3, 4])); // 10
Enter fullscreen mode Exit fullscreen mode

As I said, JavaScript doesn't have the problem that currying solves, as fascinating as currying may be.

And (mutable) closures are great when you need them, just don't go looking for excuses to use them.

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

*Curried functions

Collapse
 
anewman15 profile image
Abdullah Al Numan

I'm at odds with calling this

const createMessage = greeting => name => message => `${greeting}, ${name}! ${message}`);
Enter fullscreen mode Exit fullscreen mode

a curried function. There is a currying pattern implemented in the function, but it has not been curried from any passed in function. No?

Collapse
 
peerreynders profile image
peerreynders

Haskell wiki Currying:

Currying is the process of transforming a function that takes multiple arguments in a tuple as its argument, into a function that takes just a single argument and returns another function which accepts further arguments, one by one, that the original function would receive in the rest of that tuple.

f :: a -> (b -> c) -- or simply f :: a -> b -> c
Enter fullscreen mode Exit fullscreen mode

is the curried form of

g :: (a, b) -> c
Enter fullscreen mode Exit fullscreen mode

… In Haskell, all functions are considered curried: That is, all functions in Haskell take just one argument.

Thread Thread
 
anewman15 profile image
Abdullah Al Numan

In Haskell, all functions are considered curried: That is, all functions in Haskell take just one argument.

Yes, I agree.

Currying is the process of transforming a function that takes multiple arguments in a tuple as its argument, into a function that takes just a single argument and returns another function which accepts further arguments, one by one, that the original function would receive in the rest of that tuple.

Given this notion (or definition, if that is so) of currying, are we able to say the below function is a curried function ?

const createMessage = greeting => name => message => `${greeting}, ${name}! ${message}`);
Enter fullscreen mode Exit fullscreen mode

I think it does not implement currying as a technique - as there is no transformation involved. It very well does implement currying as a pattern, if we can call it a pattern. It's just a JS function declared to return a sequence of unary functions that currying an existing function would do.

I'm talking about semantics, obviously.

I have not been able to find usage of Curry (Haskell's last name, capitalized) as a noun. I think the verb "curry" is more prevalent, takes us to the kitchen. But, for me, it makes sense to attribute the technique to the person Curry, and then derive the verb from Curry, like Curry-ing.

We could call currying a pattern, if that is accepted as so. I'd love to read if there is any historical context.

Thread Thread
 
peerreynders profile image
peerreynders • Edited

Given this notion (or definition, if that is so) of currying, are we able to say the below function is a curried function ?

According to the wiki's usage

const createMessage = greeting => name => message => `${greeting}, ${name}! ${message}`;
Enter fullscreen mode Exit fullscreen mode

is the curried form of the otherwise equivalent

const createMessage = (greeting, name, message) => `${greeting}, ${name}! ${message}`;
Enter fullscreen mode Exit fullscreen mode

I think it does not implement currying as a technique.

It isn't the result of currying, the transformation by a compiler, runtime or a library function like Rambda's curry. But it is deliberately written in a fashion where only a single argument is accepted and another single argument function is returned until all parameters are bound and the final value is returned.

So while "In JavaScript, all functions are considered curried" is clearly false, it could be argued that createMessage() has been crafted to behave in a curried fashion which is close enough for many people to refer to it as a "curried function".

The issue is that nobody uses the term "curry function". If you want to be careful you could state that "createMessage satisfies the curried form`".

Also keep in mind that in lambda calculus the author is forced to write functions in the curried form due to the constraints of lambda calculus, i.e. the author is doing the "currying" manually. So it's not too far of a stretch to state that in lambda calculus all functions have to be "curried" or that it only supports "curried functions".

In JavaScript things are a bit less clear as it doesn't support "currying" as such but it has sufficient capabilities to allow functions to be written in the curried form.

Thread Thread
 
anewman15 profile image
Abdullah Al Numan • Edited

This is really the point I am at.

Currying transformation is not happening at runtime in this example, but while we write our code. As you said earlier, it's conceptual. It's about our design thinking. Then, is currying a design pattern ? Anything concrete about that so far ?

I am aware that nobody uses "curry functions". But, I think it will make a lot of sense if currying is a accepted design pattern, rather than merely as a technique.

Thread Thread
 
peerreynders profile image
peerreynders • Edited

Software, Faster • Dan North • GOTO 2016:

"Linda Rising has my favourite description of a pattern, she says if you go up to someone to say 'Hey I've got this pattern and it's a really good idea, let me show you it' and they show you it and you go that's a really good idea then it's not a pattern, it's a really good idea.

OK, if you go up to someone you say 'Hey I've got this pattern and it's like this' and they say I thought everyone did that—then it might be a pattern.

You're naming things that you see."


Dave Thomas:

"They are treated by most developers as recipes: “I need a to implement a xxx pattern”.

But they aren’t. Instead, they are a naming system. Once you’ve written code, you can say “oh, look, that’s like a strategy pattern.” You could then refer to it as such as a short cut when talking with other developers."

[ref]


"Consequently, you'll find recurring patterns of classes and communicating objects in many object-oriented systems. These patterns solve specific design problems and make object-oriented designs more flexible, elegant, and ultimately reusable."

[Design Patterns. Elements of Reusable Object-Oriented Software, 1995; p.1]


"Idioms are low-level patterns specific to a programming language. An idiom describes how to implement particular aspects of components ot the relationships between them with the features of the given language.

In this chapter we provide an overview of the use of idioms, show they can define a programming style, and show where you can find idioms."

[Pattern-Oriented Software Architecture, A System of Patterns, Vol.1 1996; p.345]


Then, is currying a design pattern ?

In my mind no; at best it's an idiomatic practice within the context of lambda calculus. That said, it's an idiomatic practice with a name.

In terms of JavaScript I'm even reluctant to use the term "curry". Rambda gets away with it because curry() "transforms" the passed function by wrapping it inside another function which mimics curried behaviour.

Given the capabilities inherent in function expressions (including arrow function expressions), Function.prototype.apply(), Function.prototype.bind(), and Function.prototype.call() I think it makes a lot more sense to talk about the practical applications of partial application in JavaScript—but that isn't as "cool" as currying.

I am aware that nobody uses "curry functions".

The issue is in the phrasing of the title.

The segment of the audience who knows what's going on expects to see "Curried Functions in JavaScript", so the title is jarring.

The uninitiated will wonder, "What's a 'curry function'" failing to read it as "Lets curry functions in JavaScript". So the title doesn't really connect with anybody in a meaningful way.

Thread Thread
 
anewman15 profile image
Abdullah Al Numan

It's good to know that it is idiomatic after all. Related to Haskell Curry, but ironically still idiomatic.

I think I will leave the title as is, because of the depth of the discussion around it.