DEV Community

Discussion on: Curry Functions in JavaScript

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