Related
So consider you have a one line auto return arrow function:
const test = (val) => val;
How would you check the val without doing:
const test = (val) => {
console.log(val);
return val;
};
You can actually use || between the arrow function and the returned value like so:
const test = (val) => console.log(val) || val;
This will not interfere with the process and will also log val without the hassle of adding {} and return and undoing it once you're done
echo is a great function for this -
const echo = (x) =>
( console.log(x)
, x
)
const test = x => echo(x) + 1
const arr = [ 1, 2, 3 ]
const result = arr.map(test)
console.log(result)
Arrow functions are a functional style. Utilising functional techniques will make it more enjoyable to work with arrow expressions -
const effect = f => x =>
(f(x), x)
const echo =
effect(console.log)
const test = x => echo(x + 1) // <-- wrap echo around anything
const arr = [ 1, 2, 3 ]
const result = arr.map(test)
console.log(result)
// 2
// 3
// 4
// [ 2, 3, 4 ]
You can use echof to wrap an entire function, such as echof(test) -
const effect = f => x =>
(f(x), x)
const comp = (f, g) =>
x => f(g(x))
const echo =
effect(console.log)
const echof = f =>
comp(echo, f)
const test = x => x * x
const arr = [ 1, 2, 3 ]
const result = arr.map(echof(test)) // <-- echof
console.log(result)
// 1
// 4
// 9
// [ 1, 4, 9 ]
Actual doSomething function posts ele to a remote API to do some calculations.
My calc function supposed to get the summation of the remote API's calculation for each element, It should run for every element without affecting how nested they are located.
However, Currently, I can't get this to work. How do I fix this?
const doSomething = (ele) => new Promise(resolve => {
console.log(ele);
resolve(ele * 2);//for example
})
const calc = (arr) => new Promise(
async(resolve) => {
console.log(arr.filter(ele => !Array.isArray(ele)));
let sum = 0;
const out = await Promise.all(arr.filter(ele => !Array.isArray(ele))
.map(ele => doSomething(ele)));
sum += out.reduce((a, b) => a + b, 0);
const out2 = await Promise.all(arr.filter(ele => Array.isArray(ele))
.map(ele => calc(ele)));
sum += out2.reduce((a, b) => a + b, 0);
resolve(sum);
}
)
const process = async () => {
console.log('processing..');
const arr = [1, 2, 3, 4, 5, [6,7], 1, [8,[10,11]]];
const out = await calc(arr);
console.log(out);
}
process();
While it may look like I've addressed issues that are non-existent - the original code in the question had ALL the flaws I address in this answer, including Second and Third below
yes, the code in the question now works! But it clearly was flawed
First: no need for Promise constructor in calc function, since you use Promise.all which returns a promise, if you make calc async, just use await
Second: dosomething !== doSomething
Third: out2 is an array, so sum += out2 is going to mess you up
Fourth: .map(ele => doSomething(ele)) can be written .map(doSoemthing) - and the same for the calc(ele) map
So, working code becomes:
const doSomething = (ele) => new Promise(resolve => {
resolve(ele * 2); //for example
})
const calc = async(arr) => {
const out = await Promise.all(arr.filter(ele => !Array.isArray(ele)).map(doSomething));
let sum = out.reduce((a, b) => a + b, 0);
const out2 = await Promise.all(arr.filter(ele => Array.isArray(ele)).map(calc));
sum += out2.reduce((a, b) => a + b, 0);
return sum;
}
const process = async() => {
console.log('processing..');
const arr = [1, 2, 3, 4, 5, [6, 7], 1, [8, [10, 11]]];
const out = await calc(arr);
console.log(out);
}
process();
Can I suggest a slightly different breakdown of the problem?
We can write one function that recursively applies your function to all (nested) elements of your array, and another to recursively total the results.
Then we await the result of the first call and pass it to the second.
I think these functions are simpler, and they are also reusable.
const doSomething = async (ele) => new Promise(resolve => {
setTimeout(() => resolve(ele * 2), 1000);
})
const recursiveCall = async (proc, arr) =>
Promise .all (arr .map (ele =>
Array .isArray (ele) ? recursiveCall (proc, ele) : proc (ele)
))
const recursiveAdd = (ns) =>
ns .reduce ((total, n) => total + (Array .isArray (n) ? recursiveAdd (n) : n), 0)
const process = async() => {
console.log('processing..');
const arr = [1, 2, 3, 4, 5, [6, 7], 1, [8, [10, 11]]];
const processedArr = await recursiveCall (doSomething, arr);
const out = recursiveAdd (processedArr)
console.log(out);
}
process();
I think a generic deepReduce solves this problem well. Notice it's written in synchronous form -
const deepReduce = (f, init = null, xs = []) =>
xs.reduce
( (r, x) =>
Array.isArray(x)
? deepReduce(f, r, x)
: f(r, x)
, init
)
Still, we can use deepReduce asynchronously by initialising with a promise and reducing with an async function -
deepReduce
( async (r, x) =>
await r + await doSomething(x)
, Promise.resolve(0)
, input
)
.then(console.log, console.error)
See the code in action here -
const deepReduce = (f, init = null, xs = []) =>
xs.reduce
( (r, x) =>
Array.isArray(x)
? deepReduce(f, r, x)
: f(r, x)
, init
)
const doSomething = x =>
new Promise(r => setTimeout(r, 200, x * 2))
const input =
[1, 2, 3, 4, 5, [6,7], 1, [8,[10,11]]]
deepReduce
( async (r, x) =>
await r + await doSomething(x)
, Promise.resolve(0)
, input
)
.then(console.log, console.error)
// 2 + 4 + 6 + 8 + (10 + 14) + 2 + (16 + (20 + 22))
// => 116
console.log("doing something. please wait...")
further generalisation
Above we are hand-encoding a summing function, (+), with the empty sum 0. In reality, this function could be more complex and maybe we want a more general pattern so we can construct our program piecewise. Below we take synchronous add and convert it to an asynchronous function using liftAsync2(add) -
const add = (x = 0, y = 0) =>
x + y // <-- synchronous
const main =
pipe
( deepMap(doSomething) // <-- first do something for every item
, deepReduce(liftAsync2(add), Promise.resolve(0)) // <-- then reduce
)
main([1, 2, 3, 4, 5, [6,7], 1, [8,[10,11]]])
.then(console.log, console.error)
// 2 + 4 + 6 + 8 + (10 + 14) + 2 + (16 + (20 + 22))
// => 116
deepMap and deepReduce generics. These are in curried form so they can plug directly into pipe, but that is only a matter of style -
const deepReduce = (f = identity, init = null) => (xs = []) =>
xs.reduce
( (r, x) =>
Array.isArray(x)
? deepReduce(f, r)(x)
: f(r, x)
, init
)
const deepMap = (f = identity) => (xs = []) =>
xs.map
( x =>
Array.isArray(x)
? deepMap(f)(x)
: f(x)
)
liftAsync2 takes a common binary (has two parameters) function and "lifts" it into the asynchronous context. pipe and identity are commonly available in most functional libs or easy to write yourself -
const identity = x =>
x
const pipe = (...fs) =>
x => fs.reduce((r, f) => f(r), x)
const liftAsync2 = f =>
async (x, y) => f (await x, await y)
Here's all of the code in a demo you can run yourself. Notice because deepMap synchronously applies doSomething to all nested elements, all promises are run in parallel. This is in direct contrast to the serial behaviour in the first program. This may or may not be desirable so it's important to understand the difference in how these run -
const identity = x =>
x
const pipe = (...fs) =>
x => fs.reduce((r, f) => f(r), x)
const liftAsync2 = f =>
async (x, y) => f (await x, await y)
const deepReduce = (f = identity, init = null) => (xs = []) =>
xs.reduce
( (r, x) =>
Array.isArray(x)
? deepReduce(f, r)(x)
: f(r, x)
, init
)
const deepMap = (f = identity) => (xs = []) =>
xs.map
( x =>
Array.isArray(x)
? deepMap(f)(x)
: f(x)
)
const doSomething = x =>
new Promise(r => setTimeout(r, 200, x * 2))
const add =
(x, y) => x + y
const main =
pipe
( deepMap(doSomething)
, deepReduce(liftAsync2(add), Promise.resolve(0))
)
main([1, 2, 3, 4, 5, [6,7], 1, [8,[10,11]]])
.then(console.log, console.error)
// 2 + 4 + 6 + 8 + (10 + 14) + 2 + (16 + (20 + 22))
// => 116
console.log("doing something. please wait...")
I want to make a function that, given a list of predicates, produces an array filter.
Using lodash, I define:
const { curryRight, filter, flowRight } = require('lodash');
const curriedFilter = curryRight(filter);
const filterFromPredicates = (predicateList) => flowRight(...predicateList.map(curriedFilter));
But this gives the wrong answer:
const filter = filterFromPredicates([(x) => x > 2, (x) => x > 4])
filter([1,2,3,4,5,6,7,8,9]) // -> [1,2,3,4,5,6,7,8,9]
where I would expect [5,6,7,8,9]
However defining the curried filter as follows works:
const curriedFilter = (predicate) => (array) => array.filter(predicate);
Am I misunderstanding the usage of curryRight?
Your code doesn't work because the map passes 3 arguments to the curriedFilter - the predicate, the index, and the original array. You'll need to pass only the predicate to curriedFilter.
const { curryRight, filter, flow, ary } = _;
const curriedFilter = ary(curryRight(filter), 1);
const filterFromPredicates = (predicateList) => flow(...predicateList.map(curriedFilter)); // limit the number of params passed to curriedFilter
const fn = filterFromPredicates([x => x > 2, x => x < 6]); // don't override the filter you've imported
const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Another option would be to limit the number of parameters curriedFilter accepts with _.ary():
const { curryRight, filter, flow, ary } = _;
const curriedFilter = ary(curryRight(filter), 1);
const filterFromPredicates = (predicateList) => flow(...predicateList.map(curriedFilter)); // limit the number of params passed to curriedFilter
const fn = filterFromPredicates([x => x > 2, x => x < 6]); // don't override the filter you've imported
const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
You can use _.overEvery() to generate the predicate you'll pass to the curried filter:
const { flow, overEvery, curryRight, filter } = _;
const filterFromPredicates = flow(overEvery, curryRight(filter));
const fn = filterFromPredicates(x => x > 2, x => x < 6); // don't override the filter you've imported
const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
If you use lodash/fp you want need the _.curryRight() since the functions are auto-curried, and the parameters are iteratee-first data-last:
const { flow, overEvery, filter } = _;
const filterFromPredicates = flow(overEvery, filter);
const fn = filterFromPredicates([x => x > 2, x => x < 6]); // don't override the filter you've imported
const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]
console.log(result);
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
How about this:
function filterFromPredicates(predicates) {
return (...args) => predicates
.map(predfn=> predfn(...args))
.every(a=>a)
}
[1,2,3,4,5,6,7,8,9].filter(filterFromPredicates([(x) => x > 2, (x) => x > 4]))
if you want everything in the same function then :
function filterFromPredicates(predicates) {
return (someArray)=> someArray.filter((...args) => predicates
.map(predfn=> predfn(...args))
.every(a=>a))
}
const filter = filterFromPredicates([(x) => x > 2, (x) => x > 4])
filter([1,2,3,4,5,6,7,8,9])
That will return as you wanted ^
I'm following an article about Transducers in JavaScript, and in particular I have defined the following functions
const reducer = (acc, val) => acc.concat([val]);
const reduceWith = (reducer, seed, iterable) => {
let accumulation = seed;
for (const value of iterable) {
accumulation = reducer(accumulation, value);
}
return accumulation;
}
const map =
fn =>
reducer =>
(acc, val) => reducer(acc, fn(val));
const sumOf = (acc, val) => acc + val;
const power =
(base, exponent) => Math.pow(base, exponent);
const squares = map(x => power(x, 2));
const one2ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
res1 = reduceWith(squares(sumOf), 0, one2ten);
const divtwo = map(x => x / 2);
Now I want to define a composition operator
const more = (f, g) => (...args) => f(g(...args));
and I see that it is working in the following cases
res2 = reduceWith(more(squares,divtwo)(sumOf), 0, one2ten);
res3 = reduceWith(more(divtwo,squares)(sumOf), 0, one2ten);
which are equivalent to
res2 = reduceWith(squares(divtwo(sumOf)), 0, one2ten);
res3 = reduceWith(divtwo(squares(sumOf)), 0, one2ten);
The whole script is online.
I don't understand why I can't concatenate also the last function (sumOf) with the composition operator (more). Ideally I'd like to write
res2 = reduceWith(more(squares,divtwo,sumOf), 0, one2ten);
res3 = reduceWith(more(divtwo,squares,sumOf), 0, one2ten);
but it doesn't work.
Edit
It is clear that my initial attempt was wrong, but even if I define the composition as
const compose = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x);
I still can't replace compose(divtwo,squares)(sumOf) with compose(divtwo,squares,sumOf)
Finally I've found a way to implement the composition that seems to work fine
const more = (f, ...g) => {
if (g.length === 0) return f;
if (g.length === 1) return f(g[0]);
return f(more(...g));
}
Better solution
Here it is another solution with a reducer and no recursion
const compose = (...fns) => (...x) => fns.reduceRight((v, fn) => fn(v), ...x);
const more = (...args) => compose(...args)();
usage:
res2 = reduceWith(more(squares,divtwo,sumOf), 0, one2ten);
res3 = reduceWith(more(divtwo,squares,sumOf), 0, one2ten);
full script online
Your more operates with only 2 functions. And the problem is here more(squares,divtwo)(sumOf) you execute a function, and here more(squares,divtwo, sumOf) you return a function which expects another call (fo example const f = more(squares,divtwo, sumOf); f(args)).
In order to have a variable number of composable functions you can define a different more for functions composition. Regular way of composing any number of functions is compose or pipe functions (the difference is arguments order: pipe takes functions left-to-right in execution order, compose - the opposite).
Regular way of defining pipe or compose:
const pipe = (...fns) => x => fns.reduce((v, fn) => fn(v), x);
const compose = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x);
You can change x to (...args) to match your more definition.
Now you can execute any number of functions one by one:
const pipe = (...fns) => x => fns.reduce((v, fn) => fn(v), x);
const compose = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x);
const inc = x => x + 1;
const triple = x => x * 3;
const log = x => { console.log(x); return x; } // log x, then return x for further processing
// left to right application
const pipe_ex = pipe(inc, log, triple, log)(10);
// right to left application
const compose_ex = compose(log, inc, log, triple)(10);
I still can't replace compose(divtwo,squares)(sumOf) with compose(divtwo,squares,sumOf)
Yes, they are not equivalent. And you shouldn't try anyway! Notice that divtwo and squares are transducers, while sumOf is a reducer. They have different types. Don't build a more function that mixes them up.
If you insist on using a dynamic number of transducers, put them in an array:
[divtwo, squares].reduceRight((t, r) => t(r), sumOf)
I love that ECMAScript 6 allows you to write curried functions like this:
var add = x => y => z => x + y + z;
However, I hate that we need to parenthesize every argument of a curried function:
add(2)(3)(5);
I want to be able to apply curried functions to multiple arguments at once:
add(2, 3, 5);
What should I do? I don't care about performance.
Currying and the application of curried functions are controversial issues in Javascript. In simple terms, there are two opposing views, which I illustrate both briefly.
- Use of a separate curry function only when necessary
The adaptation of concepts from other languages or paradigms is in principle a good thing. This adaptation though, should be done with the elementary means of the target language. What does that mean for currying in javascript?
curried functions are called as a sequence of unary functions:add3(1)(2)(3); // 6
own functions are manually curried with arrows const add3 = x => y => z => x + y + z;
third party functions or methods are curried by a separate curry function
- Use of a separate curry implementation by default
There's a problem with the proposed $/uncurry function:
const $ = (func, ...args) => args.reduce((f, x) => f(x), func);
const sum = x => y => z => x + y + z;
$(sum, 1, 2, 3); // 6
$(sum, 1, 2)(3); // 6
$(sum, 1)(2, 3); // z => x + y + z
In this way uncurried functions can only once be applied with an unlimited number of arguments. Any subsequent calls must be made unary. The function does exactly what it promises. However, it does not allow application of curried functions, such as JavaScript developers are used to. Most of the current curry implementations are more flexible. Here's an extended implementation:
const uncurry = f => (...args) => args.reduce(
(g, x) => (g = g(x), typeof g === "function" && g.length === 1
? uncurry(g)
: g), f
);
const sum = uncurry(x => y => z => x + y + z);
sum(1, 2, 3); // 6
sum(1, 2)(3); // 6
sum(1)(2, 3); // 6
This implementation works, if you like auto-uncurrying: Once a uncurried function itself produces a curried function as a return value, this returned function is automatically uncurried. If you prefer more control, the following implementation might be more appropriate.
The final uncurry implementation
const partial = arity => f => function _(...args) {
return args.length < arity
? (...args_) => _(...args.concat(args_))
: f(args);
};
const uncurry = arity => f => partial(arity)(args => args.reduce((g, x) => g(x), f));
const sum = uncurry(3)(x => y => z => x + y + z);
sum(1, 2, 3); // 6
sum(1, 2)(3); // 6
sum(1)(2, 3); // 6
This tiny arity parameter brings us the desired control. I think it's worth it.
A curry solution for the rest
What do we do with functions that are beyond our control and hence haven't been manually curried?
const curryN = uncurry(2)(arity => f => partial(arity)(args => f(...args)));
const add = curryN(2, (x, y) => x + y);
const add2 = add(2);
add2(4); // 6
Fortunately, we were able to reuse partial and keep curryN concise. With this solution also variadic functions or such with optional parameters can be curried.
Bonus: "Funcualizing" and currying Methods
To curry methods, we need to transform this nasty, implicit this property in an explicit parameter. It turns out that we can reuse partial for an adequate implementation once again:
const apply = uncurry(2)(arity => key => {
return arity
? partial(arity + 1)(args => args[arity][key](...args.slice(0, arity)))
: o => o[key]();
});
apply(0, "toLowerCase")("A|B|C"); // "a|b|c"
apply(0, "toLowerCase", "A|B|C"); // "a|b|c"
apply(1, "split")("|")("A|B|C"); // ["A", "B", "C"]
apply(1, "split")("|", "A|B|C"); // ["A", "B", "C"]
apply(1, "split", "|", "A|B|C"); // ["A", "B", "C"]
apply(2, "includes")("A")(0)("A|B|C"); // true
apply(2, "includes", "A", 0, "A|B|C"); // true
In this blog post currying is discussed in detail.
Most people write curried functions like this:
var add = curry(function (x, y, z) {
return x + y + z;
});
add(2, 3, 5);
Mostly because they don't want to write this:
var add = function (x) {
return function (y) {
return function (z) {
return x + y + z;
};
};
};
add(2)(3)(5);
However, nobody agrees on how to implement curry.
Then, ECMAScript 6 solved the first problem for us:
var add = x => y => z => x + y + z;
But, we still have to solve the second problem ourselves:
add(2)(3)(5);
It's high time that we solve this problem:
var $ = (func, ...args) => args.reduce((f, x) => f(x), func);
I hope you like Lisp syntax:
$(add, 2, 3, 5);
Sorry jQuery. Function application is more fundamental.
Also, Bergi's solution is awesome:
const uncurry = func => (...args) => {
var result = func;
for (let arg of args)
result = result(arg);
return result;
}
var add = uncurry(x => y => z => x + y + z);
add(2, 3, 5);
However, I still prefer using $.
You can easily write a function that applies multiple arguments to such a curried function:
const uncurry = fn => (...args) => args.reduce((f, x) => f(x), fn);
// or alternatively:
const uncurry = fn => (...args) => {
let f = fn;
for (const x of args) f = f(x);
return f;
}
Now you can invoke add like so:
uncurry(add)(2, 3, 4)
and if you still hate that you could also use
const $ = uncurry(uncurry);
$(add, 2, 3, 4)