1

If I define a curried function like this:

const gt = x => y => y > x

I would expect gt(5) returns y => y > 5

but gt(5) (or gt(5).toString()) returns y => y > x

How do I get the captured parameter?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Nan Li
  • 603
  • 1
  • 7
  • 18

3 Answers3

1

I would expect gt(5) returns y => y > 5

That isn't how JavaScript works. The code of the function returned by gt(5) is y => y > x, it's just that that code executes in an environment where the identifer x resolves to a binding that has the value 5 because it's a closure over that environment. toString on functions returns a string version of the source code of the function (or a function declaration around the [ native code ]) (spec), so you still see y => y > x. The string returned by toString doesn't include the environment in which that code runs.

See also:


As @VLAZ points out in a comment, you can override the default toString on a function to give it the behavior you want. If you don't mind duplication (which almost inevitably leads to maintenance issues):

const gt = x => {
    const fn = y => y > x;
    fn.toString = () => `y => y > ${x}`;
    return fn;
};
console.log(String(gt(5)));

...or if you want to define toString with the same enumerability, etc., as the version on the prototype:

Object.defineProperty(fn, "toString", {
    value: () => `y => y > ${x}`,
    writable: true,
    configurable: true,
});

Doing that in the general case requires a proper JavaScript parser to do the substitution.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

It's not possible to just print a variable captured in a closure, however there is a workaround for curry. I will be referring to Brian Lonsdorf's article on Medium Debugging Functional which goes into more details about curry and compose. I heartily recommend reading the article itself, as I will only use the essentials.

Brian Lonsdorf proposes a custom implementation of a general curry function. The original version is taken from an article by Erin Swenson-Healy.

Here is the modified version:

function fToString(f) {
  return f.name ? f.name : `(${f.toString()})`;
}

function curry(fx) {
  var arity = fx.length;

  function f1() {
    var args = Array.prototype.slice.call(arguments, 0);
    if (args.length >= arity) return fx.apply(null, args);

    function f2() {
      return f1.apply(null, args.concat(Array.prototype.slice.call(arguments, 0))); 
    }

    f2.toString = function() {
      return fToString(fx)+'('+args.join(', ')+')';
    }
    return f2;
  };

  f1.toString = function() { return fToString(fx); }
  return f1;
}

const gt = curry((x, y) => y > x);
const sum3 = curry((x, y, z) => x + y + z);
const ltUncurried = (x, y) => y < x;

const lt = curry(ltUncurried);

console.log(gt(1));      // ((x, y) => y > x)(1)
console.log(sum3(1));    // ((x, y, z) => x + y + z)(1)
console.log(sum3(1, 2)); // ((x, y, z) => x + y + z)(1, 2)
console.log(sum3(1)(2)); // ((x, y, z) => x + y + z)(1, 2)

console.log(lt(1));      // ltUncurried(1)

(I have modified fToString() to include brackets around f.toString() for better readability)

This is quite flexible as it allows currying any function but also provides better logging of said function. The toString implementation can be modified if required for whatever you deem are the most important details.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
0
const gt = x => y => y > x
gt(1);

If you had pass only 1 argument like gt(1) and if you Check in console, x = 1, y is not available at this time. So the execution context (call stack) will show only the x argument and for the gt() method it will only show the code (y > x).

And here below gives us a hint that gt(1) will first bind x to 1, and return a new function f(y) = y > x = y > 1

You didn't second pass second argument, It means now the function f(y) = 1 + y is executed, and this time, it will check for the y where y is not available at that time. And the argument x’s value 1 is still preserved in the context via Closure.

It's look like same as below closure function :-

function add(x) {
    return function (y) {
        return y > x
    }
}

var d = add(1)(2); ===>>>> true