Search code examples
javascriptramda.jscurrying

Ramda: how curry pass the arguments to placeholders


I have a snippet code as below to play with curry and placeholder

const calFourNumbers = (a, b, c, d) => a - b - c + d;

const curriedCalFourNumbers = R.curry(calFourNumbers );
const f = curriedCalFourNumbers(5, __);
const g = f(__,4);

console.log(g(1,2)) //=> result is 2

As I understand, 5 -1 -2 + 4 = 6.

Can anyone help me to explain how it returns the result is 2.

I am newbie with Ramda

Edit* As Scott Sauyet explained, the placeholder at the end is omitted

const curriedCalFourNumbers = R.curry(calFourNumbers );
const f = curriedCalFourNumbers(5);
const g = f(__,4);

console.log(g(1,2)) // => 2
console.log (g (1,6)) //=> 6
console.log (g (1,10)) //=> 10

I know it's not the way to write code, but just try to understand how placeholders worked on the above cases. Any explanation is appreciated.


Solution

  • The placeholder at the end of the definition of f doesn't do anything. It's not really sensible there. So that is really just equivalent to curriedCalFourNumbers (5).

    If, however you used it twice in the beginning of g, const g = f (__, __, 4), then you would get the equivalent of (b, c) => calFourNumbers (5, b, c, 4):

    const calFourNumbers = (a, b, c, d) => a - b - c + d;
    
    const curriedCalFourNumbers = curry (calFourNumbers)
    const f = curriedCalFourNumbers (5)
    const g = f (__, __,4)
    
    console .log (g (1, 2)) //=> result is 6
    <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
    <script> const {__, curry} = R </script>

    Update : further explanation

    A comment wanted to know more about why this is the way it is. First off, technically, this happens in lines 32 - 34 of _curryN. (NB: The variable left in that code really should be called remaining.) We reduce the count of parameters that need to be received only when the current value is not a placeholder.

    Logically, every placeholder can be thought of as a non-replacement for one of our formal parameters. So if we start with this:

    const calFourNumbers = (a, b, c, d) => a - b - c + d)
    

    We could write a function applyArgN that works like this:

    const f = applyArgN (calFourNumbers, 0, 5)
    

    which would make f approximately (b, c, d) => calFourNumbers (5, b, c, d) and then

    const g = applyArgN (f, 2, 4)
    

    which would make g approximately (b, c) => f (b, c, 4), which of course is also approximately (b, c) => calFourNumbers (5, b, c, 4).

    so that

    g (1, 2) ~= calFourNumbers (5, 1, 2, 4)
    

    But rather than making the user call it that way, we use Ramda's extended notion of currying so that when you call

    const curriedCalFourNumbers = curry (calFourNumbers)
    

    you can simply partially apply the values on the left by calling with fewer than are needed. Ramda tries to work, and encourages you work as well, in a manner where the parameters more likely to change come after those less likely to change. Thus the simple partial application, like const f = curriedCalFourNumbers (5) will be less common. But there are times when you really do want to partially apply a different value. All the placeholder does is literally that: it hold the place in the line of arguments for one that will be filled in later. Thus curriedCalFourNumbers (__, __, 2) needs to partially apply the 2 to the formal parameter c. That of course leaves the function (a, b, d) => curriedCalFourNumbers (a, b, 2, d).

    This is the reason the placeholder makes little sense at the end of the list of arguments. While Ramda doesn't consider that an error, logically, you're not holding the place in line for anything else. We needed two placeholder above because we needed to keep the a and b formal parameters while applying 2 to c. At the end of the list, we won't be applying subsequent values to anything, so they do nothing.

    I hope this clears things up. (While I was typing this update, you said you now understood it, but this may explain more, or be helpful to the next person who comes along with a related question.)