9

Obviously, if a data structure is a monoid, it's foldable, but is it safe to say if a data structure is foldable, it's a monoid?

https://en.wikibooks.org/wiki/Haskell/Foldable

If a data structure is foldable, is it a monoid?

  • 9
    `Data.List.NonEmpty` are `foldable` but not a `monoid` since you have drop the zero element. Btw, `Alternative` is a `monoid` but not `foldable` – lsmor Aug 13 '18 at 10:00
  • 1
    A foldable data structure is generally a container, such as list or set. Saying this is foldable means that you can accumulate all its contents into a single value by providing some function to add one of the values to the accumulator. If the value type is a monoid then this function can be `mappend`. Is that what you mean? – Paul Johnson Aug 13 '18 at 16:59
  • @LuisMorillo Thanks but, I thought every List is a monoid. –  Aug 14 '18 at 00:52
  • @PaulJohnson `mappend ` as the binary operator? and yes. Monoids = binary operator(including the specific identity element such as Zero for addition) + flat structure(associative). That's what I meant. –  Aug 14 '18 at 00:57
  • 2
    @bayesian-study A monoid is a data structure which have a binary (associative) operation _and_ a neutral element (a.k.a. zero element). So every list can be concatenated, giving you the binary operation. But `List.NonEmpty` has no zero element therefore it can't be a monoid. The proper TypeClass for `List.NonEmpty` is `Semigroup` ;) – lsmor Aug 14 '18 at 06:13
  • 1
    What does "if a data structure is a monoid, it's foldable" even mean? For example, `()` is a pretty trivial monoid, but I can't think of a sensible interpretation of "`()` is foldable" -- it doesn't even have the right kind. (Going the other way, with a bit of work I think I could interpret things; e.g. "`Foldable f` implies `Monoid (f a)`" is a sensible (but wrong) claim that bridges the kind mismatch.) – Daniel Wagner Aug 14 '18 at 14:14

2 Answers2

20

Your claim "if a data structure is a Monoid then it is Foldable" is not reasonably true. For example:

newtype ActionList a = ActionList (IO [a])

instance Monoid (ActionList a) where
    mempty = ActionList (return [])
    ActionList a `mappend` ActionList b = ActionList (liftA2 (++) a b)

This is a perfectly good monoid. But because all of its values are under IO, you can't observe any of them from Foldable. The only Foldable instance would be the one that always returns empty (technically this would be valid because foldMap doesn't really have any laws about its validity, but it would hard to say that this is a good instance with a straight face).

The converse, which you are asking about, is also not true. For example:

data TwoThings a = TwoThings a a

This is foldable:

instance Foldable TwoThings where
    foldMap f (TwoThings x y) = f x <> f y

However, if something is both a Foldable and a Monoid in any related way, I would expect the following homomorphism laws to hold:

foldMap f mempty = mempty
foldMap f (a <> b) = foldMap f a <> foldMap f b

And we can't get these laws to hold for TwoThings. Notice that foldMap (:[]) a for TwoThings always has two elements. But then the second law has two elements on the left and four on the right. But the laws are not required to find a counterexample, as dfeuer's answer shows.

luqui
  • 59,485
  • 12
  • 145
  • 204
  • Thanks, >because all of its values are under IO, you can't observe any of them from Foldable< but there is IO monoid, amd I thought IO monoid is definitely foldable http://www.haskellforall.com/2018/02/the-wizard-monoid.html isn't it. –  Aug 13 '18 at 10:16
  • 1
    @bayesian-study Why do you think `IO` is a foldable? If it were a foldable, we would have `toList (getChar) :: [Char]` -- what would be the resulting list? Note that this not an IO value, so evaluating that performs no IO, and always produces the same constant string. That string would be a completely arbitrary string. – chi Aug 13 '18 at 12:17
  • 9
    @bayesian-study You don't even need `IO` to not be `Foldable`. Functions suffice. There exists `instance (Monoid b) => Monoid (a -> b)`, but you cannot usefully implement `instance Foldable ((->) a)`. – Carl Aug 13 '18 at 13:02
6

Here's something Foldable (even Traversable) that has no hope of being a Monoid:

{-# language EmptyCase #-}

data F a

instance Foldable F where
  foldMap _ t = case t of
  -- or, = mempty
instance Traversable F where
  traverse _ t = case t of
  -- or, = pure $ case t of

instance Semigroup (F a) where
  -- the only option
  x <> _ = x

instance Monoid (F a) where
  mempty = ????
dfeuer
  • 48,079
  • 5
  • 63
  • 167