3 ms·
In a language that I don't quite have the cojones to write, the ideal API for reduce comes from Haskell’s Data.Foldable, where it is called foldMap. If you don
by crdrost 4y ago
In a language that I don't quite have the cojones to write, the ideal API for reduce comes from Haskell’s Data.Foldable, where it is called foldMap.
If you don't know Haskell the idea is simple, it is that the essence of MapReduce is for you to have an aggregating data type A with two operations,
empty(): A
combine(A, A): A
such that
combine(combine(x,y), z) = combine(x, combine(y, z))
combine(empty(), x) = x
combine(x, empty()) = x
The requirement that `combine(x, y) = combine(y, x)` is not strictly necessary if the MapReduce implementation maintains shards and shard indices and is careful to only merge neighboring shards, the first property is then sufficient.
To make this into MapReduce, you need your underlying data type X and an injection, (x: X) → A. This is the mapping part of MapReduce.
So for example “First10” is such an aggregation; given the first 10 (or fewer) in shard 1 and the first 10 (or fewer) in shard 2, you can form the first 10 (or fewer) in shards 1+2. The “map” function can use the empty value to provide “filtering,” so you get “the first 10 which satisfy this logical condition” directly.
Sum is another such aggregation, Count is a specialization of it, Mean is technically just a pair of Sum and Count. Oh, did I forget to mention? Given two aggregations you can easily produce an aggregation that does both. “give me the sum of this field and the first 10 records summed and the count of terms” ... One (parallelizable) pass for all three, because the aggregations compose.
So a macro API is possible where this is instead something like, (I don't know Clojure)
(loopM [x [1 2 3 4 5 6 7]]
(if (< x 5)
[(sum x) (count) (sum (* x x)) (first 3 x)]))
#=> [15, 5, 55, [1, 2, 3]]