Reading JS ... currying for newbie
My brain is exploding again ... this time it's the concept of currying
Where do I start to read this code? From
add-one
or from let add-one
?15 Replies
Code gets executed from top to bottom so that's the right order here as well. The function
curried_add
accepts a single argument and immediately returns another function, defined inline. So add_one
in this case is is a function itself.
However the concept of currying is actually different:
The idea is that you can invoke a function that itself returns another function, and you can go deeper into this concept, and provide the arguments to each individual function as they are being invoked inline.codecademy presents it also as a safeguard for not forgetting to enter a value for the parameter ...
Why is yours so much easier to understand than theirs?
I'v never seen the double parenthesis syntax before ...
Well I wouldn't say much easier but thanks!
And that's actually a very valid use case of currying. The thing with JavaScript is that you are not actually forced to provide all arguments to a function, and so you may forget or switch the order of parameters to functions that have multiple. Of course when you run the code you may get an error, but sometimes falsy values and numbers can work together and won't fail, but silently introduce a bug in the code program.
Another thing from the example in codecademy is that the nested function is named whereas I didn't use one, since we won't really be referencing it at all. But I think they did it this way just to make it clear that the parent function has inner functions defined within it.
https://javascript.info/currying-partials
But still, you need to explain the double parenthesis. It's the first time I see it.
Could the codecademy example do the same on
let add_one
?Well, the
add_one
is a variable that stores the returned value of curried_add
. If we apply currying it would instead store the value of whatever the inner function (aka nest_add) returns.
add_one
is not a function anymore, but a number: 1 + 10
So, if you think about it, a ()
means to invoke a function right? And curried_add
is invoked, and returns a function, which we are immediately invoking with another set of ()
.
Here's another example, let's say we have a function that creates a box:
Without looking at the definition you may forget how many or in which order to enter the arguments. Now, let's make this same function but using currying:
To be honest, I rarely if ever use currying. But this is the concept: break down a function that takes multiple arguments into many multiple functions that each take exactly one.
And btw this only works because of closures, where the inner function have access to the variables on their respective parent scope. So the last function that accepts the color string knows there's a variable called width in it's current scope.... soooo....
Regarding this you explain here:
let add_one
invokes the curried_add
function with the value 1 for (a)
and value 10 for (b)
.
Is value (a)
implicidly saved because at the end of the curried_add
function it return a + b;
?Yes, so, whenever a function runs it creates a so called execution context, a place where it stores it's own internal variables like
a
in this case. If you define a function in this context, it will have access to this execution context, in addition to creating its own which further function beneath it can also access. This explains why the final statement return a + b
works.
But the syntax (1)(10)
is explained by the fact that the first function, in this case curried_add
returns a function itself. By using this syntax you are immediately invoking it, before it stores whatever the return value is in the add_one
variable.... ah, ok... soooooo
This
let add_one = curried_add(1)(10);
is saying invoke the first function with the first value (a is stored, but not returned), and return a function that uses the second value (b, but doesn't return it).
Which is why when loging a immediately after function curried_add(a)
it prints 1, and b after function nested_add(b)
prints 10, but they are not returned (as in can't be calculated) before we explicitly ask them to be returned with return a + b;
That's about how it goes yes. I wouldn't think of it in terms of "storing" anything as these are just regular function being invoked.
If you read the code step by step you notice that
curried_add
just returns a function. It just so happens that the value returned is also a function. You can store those values in separate variables if you want, there's nothing wrong or against that, but you can invoke them at the same time and store the final result.
Let me give another example, let's say you have a series of items that have a price and are taxed differently depending on the type of article. You can create a function that calculates the total value using currying like this:
So for chips, this evaluates to:
item.tax is used for taxPercent, item.price is used for price, which means the final return for chips is:
1 + 1 * 10 * 0.01 = 1,1
.... 🤯
Did I get that formula right? 🤔
Well that's not the point 😄 the idea is that you can compose behavior using functions that would otherwise take several arguments.
In a more realistic scenario you would store the first function externally, so that items that have the same tax base would reuse that function.
Well, it kind of doesn't matter, because I'm just tracing the values through the function.
But this then goes back to my initial question of how to read syntax. Because one invokes the function after writing the function, which means writing at the bottom, looking back up, and then retracing the code.
Your example really made it simpler to understand.So even if currying isn't that much used, this has helped me read code more consistently.
Well I guess how to read it depends on how it works best for you. I guess ultimately it's very similar to how a regular function with multiple parameters work, it's just a practical difference.
I do like a logical closure. I was worried I was getting lost in the world of currying syntax, but then I realised it helped me read easier. So for that, you get todays peacock.
🦚
Thank you again ... I might be back with more head scratching!
🤯
Sure and good luck with that, its always good exercise! 👍