I challenge the view that making the identity value being able to be something other than a Promise is 'making it better'. Pointless abstraction is one of my pet peeves in this industry. This looks like it has gone from a fairly straightforward, if kludgy, piece of code to something far more complex. Why not just:
const listOfPromises = [...]
const result = Promise.all(listOfPromises).then(results => {
return results.reduce((acc, next) => acc + next)
})
?> Promise.reduce will start calling the reducer as soon as possible, this is why you might want to use it over Promise.all (which awaits for the entire array before you can call Array#reduce on it).
Whether this is ever necessary is another matter :)
let accumulator = 0
for (let item of array) {
const value = await item
// your code here
}
Is identical, doesn't use 'cool' reduce features but is much easier to read in my opinion.This way you could have the same reducer handle the results and begin updating the UI as the results come in.
An example real-world app might be a price comparison tool or social media aggregator.
Your example code works just fine for promises of course, but not all monads support a coalescing operation like Promise.all.
So even though this article only discusses folding over Promises, the core idea here can be generalised to any monad type (such as Promise, Result, Option, or anything else)
Actually, they do. Haskell calls it sequence :: (Traversable t, Monad m) => t (m a) -> m (t a) [1]
It works by consuming the structure outside the monad and rebuilding it inside. A possible implementation specialized for lists is
sequence [] = return []
sequence (h:t) = do
h' <- h
t' <- sequence t
return (h':t')
[1] http://hackage.haskell.org/package/base-4.10.0.0/docs/Prelud...Or: How to make simple things complex and make a codebase a complete puzzle for those that come after you?
I don't think you need to necessarily memorize these transformation names, but writing these types of functions is all I seem to be doing these days, transforming one thing into another line for line.
pullAllBy(pluck(things, 'bar').map(compose(xor, lol, rofl)).reduce(differenceWith('id'))
Just write your transformations inline and go work on the next feature.Complaining about learning them is like complaining about for loops. They just exist.
Just because some are more familiar with for loops than map doesn't mean that more universal, immutable, expression - based solutions are not widely familiar and easy to understand to programmers coming from other languages.
Readability is subjective.
It is indeed very unfortunate that the article conflates terminology.
So a function that turns an array into another array of different length would be endomorphic (since it maintains the same type), but not homomorphic since it has a different structure (a different set of keys).
const reduceP = async (fn, identity, listP) => {
const values = await Promise.all(listP)
return values.reduce(fn, identity)
}
The whole thing feels like a synthetic and overcomplicated example, though. In practice I'm sure I'd just write: let total = 0
while (listP.length > 0) {
total += await listP.pop()
}In any case, this is very helpful, thanks for writing/sharing.
As long as you know what the transformation is, you can convert between them without data loss.
You're right, because for a pair of functions f and g, you have an isomorphism if:
f(g(x)) == x
g(f(x)) == x
for every x. However, here of course (([a]) => {a})( (({ a }) => [a])({ key: 'data'}) )
is equal to { a: 'data' }
The OP doesn't quite master what he's talking about… forall x. objToArray(arrayToObj(x)) == x
forall x. arrayToObj(objToArray(x)) == x const objToArray = Object.entries
const arrayToObj = (a) => a.reduce((a, [k, v]) => ((a[k]=v), a), {})
arrayToObj(objToArray({ foo: 'bar' })) // { foo: 'bar' } listOfPromises.reduce(
async (m, n) => await m + await n,
0,
)