Implementing `foldMap` for foldable
See original GitHub issueWith a foldable it is (or should be) possible to implement foldMap
in terms of foldr
(what fantasy land calls reduce
) and vice versa.
If I have a monoid like this:
function _Add(n) {
this.n = n;
}
_Add.prototype.empty = function() {
return new _Add(0);
}
_Add.prototype.concat = function(m) {
return new _Add(this.n + m.n);
}
function Add(n) {
return new _Add(n);
}
foldMap
should behave like this when given a traversable of numbers and the above constructor function.
assert.deepEqual(foldMap(Add, traversableFromArray([1, 2, 3, 4])), Add(10));
But the straightforward implementation doesn’t work:
function foldMap(f, t) {
t.reduce(f, empty);
// ╰── does not work, there is no empty
}
The problem is that foldMap
doesn’t know how to create an identity element of the monoid inside the passed foldable. The easiest solution would be to do the same as what fantasy land does in a few places and demand an additional argument to help the implementation. In this case if users where required to pass an empty value then the simple implementation would work.
But such an argument results in an inferior API with unfortunate ugliness.
Since the function passed to foldMap
is required to return a monoid it is possible, with a bit of clunkiness, to work around the need for an initial empty value. An implementation that satisfies the previous assertion is given below:
var empty = {};
function foldMap(f, t) {
return t.reduce(function(acc, n) {
return acc === empty ? f(n) : concat(acc, f(n));
}, empty);
}
We create a dummy empty value that contains a pointer to an object (which we now will be unique) and uses it inside the function passed to reduce
to ignore the initial accumulator value.
This seems to result in the desired foldMap
at the cost of elegance in the implementation.
Any thoughts?
Issue Analytics
- State:
- Created 8 years ago
- Comments:21 (9 by maintainers)
Top GitHub Comments
I’d like to see a rewrite of the spec using explicit dictionaries for everything. My theory is that’d be more useful than the current spec but much less convenient. Definitely happy to do that as Fantasy Land 2 if it actually works, though.
AFAIK, the only places where that would be necessary is when you need to make reference to
of
orempty
. All the other FL functions require at least one instance of the type, which can then serve as your dictionary for the remaining relevant functions. These two are the special cases, and that’s why I brought up #88.I could. I don’t think I’m getting my ideas or my rationale across at all, but its probably because it’s still fairly ill-formed for me. When I get closer to trying it (probably two weeks or so) I’ll likely be back asking questions and advice, and I won’t be surprised if that advice is simply “Don’t do it!” 😄