question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Implementing `foldMap` for foldable

See original GitHub issue

With 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:closed
  • Created 8 years ago
  • Comments:21 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
puffnfreshcommented, Jul 9, 2015

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.

0reactions
CrossEyecommented, Jul 9, 2015

I agree. I think the main counterargument is consistency with other function where you have to pass a dictionary.

AFAIK, the only places where that would be necessary is when you need to make reference to of or empty. 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.

Such an implementation would help me work in this preferred manner. We’ll see if I can make it work, make it usable, and make it efficient. But that API is the goal.

But you can mostly achieve that just by having functions that invoke the respective methods (like R.invoker)?

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!” 😄

Read more comments on GitHub >

github_iconTop Results From Across the Web

Implementation of Foldable in Haskell - Stack Overflow
Anyway, I think the easiest way in this case is to implement just the foldMap part: import Data.Foldable import Data.
Read more >
Foldable and Traversible | Tim's code stuff
We make this type of binary tree an instance of foldable by implementing either of the minimum defining functions, foldMap or foldr :....
Read more >
Data.Foldable - Hackage - Haskell.org
This fold is right-associative and lazy in the accumulator. When you need a strict left-associative fold, use foldMap' instead, with id as the...
Read more >
Haskell/Foldable - Wikibooks, open books for an open world
The Foldable type class provides a generalisation of list folding ( foldr and friends) and operations derived from it to arbitrary data structures....
Read more >
Foldable Typeclass in Haskell - Serokell
We can implement the instance of Foldable whenever a data type has one ... Note that you shouldn't implement foldMap in terms of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found