+ 20

Javascript currying

Please explain me this code and also what is this !! means and arguments. callee var currying = function(fn) { var args = []; return function() { if (!!arguments.length) { [].push.apply(args, arguments); // What's the meaning of this writing? return arguments.callee; } else { return fn.apply(this, args); } } } // accumulation currying var sum = (function(num){ var ret = 0; return function(){ for(var i = 0, len = arguments.length; i < len; i++) { ret += arguments[i]; } return ret; } })(); var newSum = currying(sum); newSum(1)(2)(3)(4)() // 10 // element find currying var find = function(arr, el){ return arr.indexOf(el) !== -1; } var newFind = currying(find)([1,2,3]); newFind(1); newFind(2);

19th Apr 2019, 9:12 AM
Danielov
Danielov - avatar
12 Réponses
+ 21
Okay, let me bang out the easy stuff first: The "!!" is a double-not operator. In Javascript, "!" is the not operator. In other words, !true == false; and !false == true. However, if the ! operator is used on a non-boolean value, then it will automatically convert that value to a boolean (true or false values only). This is useful when you want to evaluate that object inside of an if-statement, which only accepts boolean inputs. In this case, it is used on the arguments.length object, which is an integer. The first time we use the "!" operator, Javascript needs to convert that integer into a boolean. The way this is resolved is that the integer is converted to false if the integer value is zero, otherwise it resolves to true. After this all happens, the second ! operator resolves, which inverts those values. So basically, if arguments.length is zero, the if-statement will resolve to true; otherwise it will be false. This is basically just a fancy way of writing: if(arguments.length != 0) { ... } Next, let me explain what "arguments.callee" is all about. The arguments.callee variable is simply a reference to the current function from inside that function's body. This is typically used to write recursive anonymous functions where we can't call the function by name. For example, imagine we were writing a recursive factorial function. We might write something like this: function factorial(x) { if(x == 0) return 1; else return x*factorial(x - 1); //notice we call the function by name } We are able to do this because we know the function's name. But now imagine that we needed to write that function anonymously, like if we wanted to pass it as an argument to another function. We can't call the function by-name, so how do we do it? We use the arguments.callee object. Ex: someFunction(function(x) { if(x == 0) return 1; else return x*arguments.callee(x); //we use arguments.callee }); I'm out of characters, but I'll write another response explaining what the code does and how it works.
20th Apr 2019, 2:14 AM
Jack McCarthy
Jack McCarthy - avatar
+ 18
This post is just an explanation of function currying. Feel free to skip this if you already know what function currying is and how it works. Function currying is basically just nesting a bunch of function calls so that they can be made individually. Imagine a function: function printStuff(first, second) { console.log(first + second); } We would call this function like this: printStuff("Hello, ", "world"); // output: "Hello, world" However, now imagine we wrote the same function this way: printStuffCurrying(first) { return function(second) { console.log(first + second); } } We would now call this function like this: printStuffCurrying("Hello, ")("world"); //output: "Hello, world" This called function currying! It is useful because it allows us to give the first few arguments to a function, and then use that as a template. For example, imagine we wanted to print a bunch of greetings. Using the original code, we'd have to write it like this: printStuff("Hello, ", "world"); printStuff("Hello, ", "sun"); printStuff("Hello, ", "moon"); printStuff("Hello, ", "sky"); See how repetitive that is! Using the curried function, we can clean all of that up. Check this out: let sayHello = printStuffCurrying("Hello, "); sayHello("world"); sayHello("sun"); sayHello("moon"); sayHello("sky"); Much better. Now, we don't have to specify that we want to put "Hello, " in front of all of our messages; it's implied. Essentially, what the code above does is turn a normal function into a curried function. I will explain that in another a reply.
20th Apr 2019, 2:29 AM
Jack McCarthy
Jack McCarthy - avatar
+ 14
Sonic You are "!!correct", which is to say, "not not correct". 😉 Jack McCarthy Welcome to the community. I absolutely enjoyed reading your responses. 🤓 Your explanations regarding the double not operator, arguments.callee function, currying, and the code in question were so articulate, well constructed, and easy to follow. You can consider me a fan and someone looking forward to more of your excellent contributions. 👌 One thing I might add as a bonus detail is the critical role closures play in making currying possible in Javascript. Closures is the mechanism by which the arguments passed into the parent function is persisted and accessible by the returned nested function. This persisted state of args remains available throughout the stack to the innermost returned nested function.
20th Apr 2019, 8:01 AM
David Carroll
David Carroll - avatar
+ 11
Okay, here's how the code works. The main thing to understand is the "currying" function. The first line of the currying function just creates an empty array "args". This array will hold the arguments we pass into the curried function. The next line returns an anymous function. Inside that function is where the real magic happens. The function starts with an if-statement which checks if the number of arguments passed into it is not equal to zero. I'll start with what happens if this evaluates to true. If there are more than zero arguments passed into the function, it simply adds those arguments to the "args" array, and then returns a reference to the current anonymous function. This is what creates the "currying" effect. Each time you call the function, you get back a reference to the same function, allowing you to call the function as many times as you want. Each time, it will simply push your arguments into an array for later use. The "[].push.apply(args, arguments)" is just a fancy way of appending all of the values of one array to another array. In this case, we are appending all of the values of arguments to args. We arguments.callee, which is just a reference to the anonymous function. Now let's see what happens if we call the function with zero arguments. We return fn.apply(this, args). This code calls fn (which is the function we want curried), with args as the arguments. Using this code, we use all of the arguments we've saved in the "args" variable in one single function call. In the example used in the code, we use the currying function on another function: sum. The sum function just adds up all of the values of it's arguments, and returns them var newSum = currying(sum); This line creates a new function, newSum, which is a curried form of the sum function. Each time we call newSum(x), where x is some number, we will get back a reference to newSum, except with x added to the args list. Finally when we cal newSum(), it calls the original sum function, with all of our saved arguments
20th Apr 2019, 2:47 AM
Jack McCarthy
Jack McCarthy - avatar
+ 9
I have never seen !! Is this a double negation David Carroll ?
20th Apr 2019, 1:37 AM
Sonic
Sonic - avatar
+ 6
You can interprete the !! as a kind of a typecast to boolean... arguments.callee is used for rekursion if you don't know the name of the function, e.g. if your functioin doesn't have a name like the one for the currying variable. In a function named my_func() arguments.callee(n) is equal to my_func(n). The code is an example for curriying. Currying means that you can write a function in two ways like: f(n, m) --> f(n)(m) Or in an example: add(5,6) //=== 11 curriedAdd(5)(6)// === 11 so the currying function in your code takes a function as argument, transforms the curring arguments to 'normal' arguments and then calls the function (from the argument) with them. If the currying function doesn't get any Argument, it will execute the return in the else-statement (just calling th function). Otherwise it will collect the 'curriyng arguments', save them to the array args and then call the function. apply executes the function before it for the array passed to it, so it pushs the arguments to args. In general: function.apply(thisArg, [argsArray]) will execute function(thisArg, elm) for each element elm in the array argsArray.
20th Apr 2019, 2:17 AM
Niwo
Niwo - avatar
+ 2
i cant tun newFind whats wrong with the code it only return [obgject] please explain https://code.sololearn.com/WuSc81fFo7zP/?ref=app
22nd Apr 2019, 1:07 AM
Danielov
Danielov - avatar
+ 2
thanks Jack McCarthy David Carroll i was also wondering how newsum arguments can use in the nested function with no parameters and still accessible in the if statement and push methods. So the arguments pass with newSum function still exist throught the currying function
22nd Apr 2019, 2:58 AM
Danielov
Danielov - avatar
+ 2
You need some () after the function call. Like that: var newFind = currying(find)([1,2,3]); console.log( newFind(1)()); console.log( newFind(2)());
22nd Apr 2019, 9:56 AM
Niwo
Niwo - avatar
+ 2
Niwo can you explain whats this array ([1,2,3]) purpose var newFind = currying(find)([1,2,3]); console.log( newFind(1)()); console.log( newFind(2)()); //why it need to pass empty parameter
22nd Apr 2019, 2:52 PM
Danielov
Danielov - avatar
+ 2
To start executing the Method, otherwise you just append the parameter. Also, your find doesn't work the second time, because it will take the 1 from the first call again because it will be saved in your variable. You can test it by writing 20 in your second call, it will still be true. So calling your currying variable with parameters will add these parameter to your currying objekt and the () will execute it...
22nd Apr 2019, 3:30 PM
Niwo
Niwo - avatar
0
SVP, je voudrais réinstaller mon JAVASCRIPT merci...
21st Apr 2019, 6:02 PM
leguem paul