Currying with functions that take unlimited arguments - javascript

Lets say i have the following add function that takes an unlimited number of arguments.
function add () {
var total = 0;
var args = Array.prototype.slice.call(arguments, 0);
for (var i=0; i<args.length; i++) {
total += arguments[i];
}
return total;
}
and the following curry function.
function curryFunction(orig_func) {
var ap = Array.prototype;
var args = arguments;
function fn() {
if (arguments.length != 0) {
ap.push.apply(fn.args, arguments);
return fn;
} else {
return orig_func.apply(this, fn.args);
}
};
return function() {
fn.args = ap.slice.call( args, 1 );
return fn.apply( this, arguments );
};
}
I then want to do something like:
var f = curryFunction(add);
var a = f(3)(4)(3);
var b = f(10)(3);
var result1 = a(); // returns 10
var result2 = b(); // returns 13
However i always get 13 for both a() and b() i assume is because in line
fn.args = ap.slice.call(args, 1);
the existing array [3,4,3] is overwriting with []. Can someone please provide me with a hint on how to make this work? Thanks

The problem is that fn is scoped to curryFunction and so is shared between a and b.
All you have to do is move the definition of fn into the anonymous return function. It's then created when you call f, and the problematic fn.args = line is only called once.
Proof: jsFiddle.

Currying a function which takes indefinitely many arguments can be implemented as follows;
Lets say we have a function called addAll() which returns the sum of all provided arguments.
var addall = (...a) => a.reduce((p,c) => p + c);
And we have a curry function which takes a function and returns curried version ad infinitum up until the returned function is called with no arguments, only when the result of all previously provided arguments will be returned. OK here is the curry function.
var curry = f => (...a) => a.length ? curry(f.bind(f,...a))
: f();
Lets see it in action;
var addAll = (...a) => a.reduce((p,c) => p + c),
curry = f => (...a) => a.length ? curry(f.bind(f,...a)) : f(),
curried = curry(addAll),
result = curried(10,11)(10)(37)(10,17,42)();
console.log(result);
result = curried("a","b")("c")("d")("e","f","g")();
console.log(result);

Related

Why does the default JS bind function works and why my polyfill for the bind returns undefined?

I am trying to write the polyfill for the js bind function over a multiply function. The js bind function is giving the correct answer 8 but my polyfill is giving undefined. The main multiply function is returning all the params correctly though.
Function.prototype.myBind = function (...args) {
let obj = this;
let params = args.slice(1);
return function (...param2) {
obj.apply(args[0], [...params, ...param2]);
};
};
let mul = (a, b) => {
console.log(13, a, b, a * b);
return a * b;
};
let mulFour = mul.bind(this, 4);
let myMulFour = mul.myBind(this, 4);
console.log(mulFour(2));
console.log(myMulFour(2));
CertainPerformance was correct, needed to return the obj.apply(args[0], [...params, ...param2]); again.
The polyfill would go like this:
Function.prototype.myBind = function (...args) {
let obj = this;
let params = args.slice(1);
return function (...param2) {
return obj.apply(args[0], [...params, ...param2]);
};
};

Passing additional vs one argument into the function

I am implementing compose function using reduceRight menthod as follows
const compose = fns => (...args) =>
fns.reduceRight((acc, fn) => fn(acc, ...[args.slice(1)]), args[0]);
const func3 = (x, y) => (y > 0 ? x + 3 : x - 3);
const func2 = x => x ** 2;
const func1 = x => x - 8;
const fnOne = compose([func1, func2, func3])('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose([func1, func2, func3])('3', -1);
console.log(fnTwo); //should be -8
the first function is supposed to receive two arguments and return the result to the next function as the only one argument. The problem is that the first function is passing two arguments to the next function instead of one. Let me know if you have any ideas how to fix it. Any help is very much appreciated.
The problem here is that you're not modifying the args variable.
Let's look at what happens in detail:
At the first call of your reductor, acc becomes func3(args[0], ...[args.shift(1)]) === func3(args[0], args[1], args[2], ...).
At the second call, acc becomes func2(acc, [args.shift(1)]), which is func2(func3(args[0], args[1], args[2], ...), args[1], args[2], ...).
You can already see where the problem lies: args1 is never dropped from the array, because Array.slice() creates a copy and does not modify the actual array.
To solve your problem you should instead use:
const compose = fns => (...args) =>
fns.reduceRight((acc, fn) => fn(acc, ...args.splice(0, fn.length - 1)), args[0]);
You need to call the first function outside the reduceRight() loop, since it's not being called the same way as all the other functions. It gets its arguments from ...args and its value should be used as the initial accumulator argument to reduce.
const compose = fns => (...args) => {
let last = fns.pop();
return fns.reduceRight((acc, fn) => fn(acc), last(...args))
};
const func3 = function(x, y) {
console.log(`func3 got ${arguments.length} arguments`);
return (y > 0 ? x + 3 : x - 3);
};
const func2 = function(x) {
console.log(`func2 got ${arguments.length} arguments`);
return x ** 2;
};
const func1 = function(x) {
console.log(`func2 got ${arguments.length} arguments`);
return x - 8;
};
const fnOne = compose([func1, func2, func3])('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose([func1, func2, func3])('3', -1);
console.log(fnTwo); //should be -8

How to correctly serialize Javascript curried arrow functions?

const makeIncrementer = s=>a=>a+s
makeIncrementer(10).toString() // Prints 'a=>a+s'
which would make it impossible to de-serialize correctly (I would expect something like a=>a+10 instead.
Is there a way to do it right?
This is a great question. While I don't have a perfect answer, one way you could get details about the argument/s is to create a builder function that stores the necessary details for you. Unfortunately I can't figure out a way to know which internal variables relate to which values. If I figure out anything else i'll update:
const makeIncrementer = s => a => a + s
const builder = (fn, ...args) => {
return {
args,
curry: fn(...args)
}
}
var inc = builder(makeIncrementer, 10)
console.log(inc) // logs args and function details
console.log(inc.curry(5)) // 15
UPDATE: It will be a mammoth task, but I realised, that if you expand on the builder idea above, you could write/use a function string parser, that could take the given args, and the outer function, and rewrite the log to a serialised version. I have a demo below, but it will not work in real use cases!. I have done a simple string find/replace, while you will need to use an actual function parser to replace correctly. This is just an example of how you could do it. Note that I also used two incrementer variables just to show how to do multiples.
function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace)
}
const makeIncrementer = (a, b) => c => c + a + b
const builder = (fn, ...args) => {
// get the outer function argument list
var outers = fn.toString().split('=>')[0]
// remove potential brackets and spaces
outers = outers.replace(/\(|\)/g,'').split(',').map(i => i.trim())
// relate the args to the values
var relations = outers.map((name, i) => ({ name, value: args[i] }))
// create the curry
var curry = fn(...args)
// attempt to replace the string rep variables with their true values
// NOTE: **this is a simplistic example and will break easily**
var serialised = curry.toString()
relations.forEach(r => serialised = replaceAll(serialised, r.name, r.value))
return {
relations,
serialised,
curry: fn(...args)
}
}
var inc = builder(makeIncrementer, 10, 5)
console.log(inc) // shows args, serialised function, and curry
console.log(inc.curry(4)) // 19
You shouldn't serialize/parse function bodies since this quickly leads to security vulnerabilities. Serializing a closure means to serialize its local state, that is you have to make the closure's free variables visible for the surrounding scope:
const RetrieveArgs = Symbol();
const metaApply = f => x => {
const r = f(x);
if (typeof r === "function") {
if (f[RetrieveArgs])
r[RetrieveArgs] = Object.assign({}, f[RetrieveArgs], {x});
else r[RetrieveArgs] = {x};
}
return r;
}
const add = m => n => m + n,
f = metaApply(add) (10);
console.log(
JSON.stringify(f[RetrieveArgs]) // {"x":10}
);
const map = f => xs => xs.map(f)
g = metaApply(map) (n => n + 1);
console.log(
JSON.stringify(g[RetrieveArgs]) // doesn't work with higher order functions
);
I use a Symbol in order that the new property doesn't interfere with other parts of your program.
As mentioned in the code you still cannot serialize higher order functions.
Combining ideas from the two answers so far, I managed to produce something that works (though I haven't tested it thoroughly):
const removeParentheses = s => {
let match = /^\((.*)\)$/.exec(s.trim());
return match ? match[1] : s;
}
function serializable(fn, boundArgs = {}) {
if (typeof fn !== 'function') return fn;
if (fn.toJSON !== undefined) return fn;
const definition = fn.toString();
const argNames = removeParentheses(definition.split('=>', 1)[0]).split(',').map(s => s.trim());
let wrapper = (...args) => {
const r = fn(...args);
if (typeof r === "function") {
let boundArgsFor_r = Object.assign({}, boundArgs);
argNames.forEach((name, i) => {
boundArgsFor_r[name] = serializable(args[i]);
});
return serializable(r, boundArgsFor_r);
}
return r;
}
wrapper.toJSON = function () {
return { function: { body: definition, bound: boundArgs } };
}
return wrapper;
}
const add = m => m1 => n => m + n * m1,
fn = serializable(add)(10)(20);
let ser1, ser2;
console.log(
ser1 = JSON.stringify(fn) // {"function":{"body":"n => m + n * m1","bound":{"m":10,"m1":20}}}
);
const map = fn => xs => xs.map(fn),
g = serializable(map)(n => n + 1);
console.log(
ser2 = JSON.stringify(g) // {"function":{"body":"xs => xs.map(fn)","bound":{"fn":{"function":{"body":"n => n + 1","bound":{}}}}}}
);
const reviver = (key, value) => {
if (typeof value === 'object' && 'function' in value) {
const f = value.function;
return eval(`({${Object.keys(f.bound).join(',')}}) => (${f.body})`)(f.bound);
}
return value;
}
const rev1 = JSON.parse(ser1, reviver);
console.log(rev1(5)); // 110
const rev2 = JSON.parse(ser2, reviver);
console.log(rev2([1, 2, 3])); // [2, 3, 4]
This works for arrow functions, that do not have default initializers for the arguments. It supports higher order functions as well.
One still has to be able to wrap the original function into serializable before applying it to any arguments though.
Thank you #MattWay and #ftor for valuable input !

New method does not see "this" (JavaScript)

Making a calculator that accepts new methods. But when I add a new method it does not see object's "this". Why Console.log returns "undefined"?
function Calculator() {
this.numbers = function() {
this.numberOne = 2;
this.numberTwo = 5;
},
this.addMethod = function(op, func) {
this[op] = func(this.numberOne, this.numberTwo);
// WHY LOG RETURNS "undefined"?
console.log(this.numberOne);
}
}
let calc = new Calculator();
calc.addMethod("/", (a, b) => (a / b));
document.write(calc["/"]);
You did not define this.numberOne and this.numberTwo before you tried to call the function on it. Moreover, you are printing this.one which is never defined in your code.
If you tried the following snippet:
function Calculator() {
this.numbers = function() {
this.numberOne = 2;
this.numberTwo = 5;
},
this.addMethod = function(op, func) {
this[op] = func(this.numberOne, this.numberTwo);
// WHY LOG RETURNS "undefined"?
console.log(this.numberOne);
}
}
let calc = new Calculator();
calc.numbers();
calc.addMethod("/", (a, b) => (a / b)); // 2/5
document.write(calc["/"]);
Then the code will work as expected because calc.numberOne and calc.numberTwo are defined
Your numbers were not getting initialized.
Also you used this.one what's that? Did you mean numberOne.
Check out the working code below :
function Calculator() {
this.numberOne = 2;
this.numberTwo = 5;
this.addMethod = function(op, func) {
this[op] = func(this.numberOne, this.numberTwo);
// WHY LOG RETURNS "undefined"?
console.log(this.numberOne, this.numberTwo );
}
}
let calc = new Calculator();
calc.addMethod("/", (a, b) => (a / b));
document.write(calc["/"]);

new Function() with variable parameters

I need to create a function with variable number of parameters using new Function() constructor. Something like this:
args = ['a', 'b'];
body = 'return(a + b);';
myFunc = new Function(args, body);
Is it possible to do it without eval()?
Thank you very much, guys! Actually, a+b was not my primary concern. I'm working on a code which would process and expand templates and I needed to pass unknown (and variable) number of arguments into the function so that they would be introduced as local variables.
For example, if a template contains:
<span> =a </span>
I need to output the value of parameter a. That is, if user declared expanding function as
var expand = tplCompile('template', a, b, c)
and then calls
expand(4, 2, 1)
I need to substitute =a with 4. And yes, I'm well aware than Function is similar to eval() and runs very slow but I don't have any other choice.
You can do this using apply():
args = ['a', 'b', 'return(a + b);'];
myFunc = Function.apply(null, args);
Without the new operator, Function gives exactly the same result. You can use array functions like push(), unshift() or splice() to modify the array before passing it to apply.
You can also just pass a comma-separated string of arguments to Function:
args = 'a, b';
body = 'return(a + b);';
myFunc = new Function(args, body);
On a side note, are you aware of the arguments object? It allows you to get all the arguments passed into a function using array-style bracket notation:
myFunc = function () {
var total = 0;
for (var i=0; i < arguments.length; i++)
total += arguments[i];
return total;
}
myFunc(a, b);
This would be more efficient than using the Function constructor, and is probably a much more appropriate method of achieving what you need.
#AndyE's answer is correct if the constructor doesn't care whether you use the new keyword or not. Some functions are not as forgiving.
If you find yourself in a scenario where you need to use the new keyword and you need to send a variable number of arguments to the function, you can use this
function Foo() {
this.numbers = [].slice.apply(arguments);
};
var args = [1,2,3,4,5]; // however many you want
var f = Object.create(Foo.prototype);
Foo.apply(f, args);
f.numbers; // [1,2,3,4,5]
f instanceof Foo; // true
f.constructor.name; // "Foo"
ES6 and beyond!
// yup, that easy
function Foo (...numbers) {
this.numbers = numbers
}
// use Reflect.construct to call Foo constructor
const f =
Reflect.construct (Foo, [1, 2, 3, 4, 5])
// everything else works
console.log (f.numbers) // [1,2,3,4,5]
console.log (f instanceof Foo) // true
console.log (f.constructor.name) // "Foo"
You can do this:
let args = '...args'
let body = 'let [a, b] = args;return a + b'
myFunc = new Function(args, body);
console.log(myFunc(1, 2)) //3
If you're just wanting a sum(...) function:
function sum(list) {
var total = 0, nums;
if (arguments.length === 1 && list instanceof Array) {
nums = list;
} else {
nums = arguments;
}
for (var i=0; i < nums.length; i++) {
total += nums[i];
}
return total;
}
Then,
sum() === 0;
sum(1) === 1;
sum([1, 2]) === 3;
sum(1, 2, 3) === 6;
sum([-17, 93, 2, -841]) === -763;
If you want more, could you please provide more detail? It's rather difficult to say how you can do something if you don't know what you're trying to do.
A new feature introduced in ES5 is the reduce method of arrays. You can use it to sum numbers, and it is possible to use the feature in older browsers with some compatibility code.
There's a few different ways you could write that.
// assign normally
var ab = ['a','b'].join('');
alert(ab);
// assign with anonymous self-evaluating function
var cd = (function(c) {return c.join("");})(['c','d']);
alert(cd);
// assign with function declaration
function efFunc(c){return c.join("");}
var efArray = ['e','f'];
var ef = efFunc(efArray);
alert(ef);
// assign with function by name
var doFunc = function(a,b) {return window[b](a);}
var ghArray = ['g','h'];
var ghFunc = function(c){return c.join("");}
var gh = doFunc(ghArray,'ghFunc');
alert(gh);
// assign with Class and lookup table
var Function_ = function(a,b) {
this.val = '';
this.body = b.substr(0,b.indexOf('('));
this.args = b.substr(b.indexOf('(')+1,b.lastIndexOf(')')-b.indexOf('(')-1);
switch (this.body) {
case "return":
switch (this.args) {
case "a + b": this.val = a.join(''); break;
}
break;
}
}
var args = ['i', 'j'];
var body = 'return(a + b);';
var ij = new Function_(args, body);
alert(ij.val);
Maybe you want an annoymous function to call an arbitary function.
// user string function
var userFunction = 'function x(...args) { return args.length}';
Wrap it
var annoyFn = Function('return function x(...args) { return args.length}')()
// now call it
annoyFn(args)
new Function(...)
Declaring function in this way causes
the function not to be compiled, and
is potentially slower than the other
ways of declaring functions.
Let is examine it with JSLitmus and run a small test script:
<script src="JSLitmus.js"></script>
<script>
JSLitmus.test("new Function ... ", function() {
return new Function("for(var i=0; i<100; i++) {}");
});
JSLitmus.test("function() ...", function() {
return (function() { for(var i=0; i<100; i++) {} });
});
</script>
What I did above is create a function expression and function constructor performing same operation. The result is as follows:
FireFox Performance Result
IE Performance Result
Based on facts I recommend to use function expression instead of function constructor
var a = function() {
var result = 0;
for(var index=0; index < arguments.length; index++) {
result += arguments[index];
}
return result;
}
alert(a(1,3));
function construct(){
this.subFunction=function(a,b){
...
}
}
var globalVar=new construct();
vs.
var globalVar=new function (){
this.subFunction=function(a,b){
...
}
}
I prefer the second version if there are sub functions.
the b.apply(null, arguments) does not work properly when b inherits a prototype, because 'new' being omitted, the base constructor is not invoked.
In this sample i used lodash:
function _evalExp(exp, scope) {
const k = [null].concat(_.keys(scope));
k.push('return '+exp);
const args = _.map(_.keys(scope), function(a) {return scope[a];});
const func = new (Function.prototype.bind.apply(Function, k));
return func.apply(func, args);
}
_evalExp('a+b+c', {a:10, b:20, c:30});

Categories

Resources