9

I read Why MonadPlus and not Monad + Monoid? and I understand a theoretical difference, but I cannot figure out a practical difference, because for List it looks the same.

mappend [1] [2] == [1] <|> [2]

Yes. Maybe has different implementations

mappend (Just "a") (Just "b") /= (Just "a") <|> (Just "b")

But we can implement Maybe Monoid in the same way as Alternative

instance Monoid (Maybe a) where
  Nothing `mappend` m = m
  m `mappend` _ = m

So, can someone show the code example which explains a practical difference between Alternative and Monoid?

The question is not a duplicate of Why MonadPlus and not Monad + Monoid?

duplode
  • 33,731
  • 7
  • 79
  • 150
ais
  • 2,514
  • 2
  • 17
  • 24
  • 2
    Ahm... sorry, but this is still a duplicate. Toxaris [listed quite clearly](http://stackoverflow.com/a/23024217/745903) what you can't do with `Monoid`, in particular points 3. and 4.: “**3.** If we want to use infinitely many different `a`.`MonadPlus m => ...` instead of not possible. **4.** If we don't know what `a` we need. `MonadPlus m => ...` instead of not possible.” If something about that isn't clear to you, please ask specifically about your doubts. – leftaroundabout Aug 02 '15 at 11:20
  • 1
    It's just theoretical explanation. Even there the first comment was " I would really like to see a concrete example here". I want practical code examples which can show how exactly 3 and 4 affect monoid usage. – ais Aug 02 '15 at 11:44
  • 1
    Ok, I see what you mean. By I don't think it's helpful to cook up some contrived examples – sure, for small examples we could give here you can always say _“now why don't we just expose that private data type abstractly”_ or _“those are a couple of `Monoid (f A), Monoid (f B)` constraints, but so what? – it doesn't look that bad”_ or _“why don't we put those infinitely many / unknown types in an existential/GADT wrapper”_. But all these workarounds aren't really practical in a big project. Just look up some library functions with `Alternative` context, and try to write them with `Monoid`. – leftaroundabout Aug 02 '15 at 14:12
  • 1
    Have you deleted the closed question and posted it again? Please avoid that, it is really confusing. – duplode Aug 02 '15 at 16:16
  • @duplode It was marked as duplicate – ais Aug 02 '15 at 16:42
  • possible duplicate of [Why MonadPlus and not Monad + Monoid?](http://stackoverflow.com/questions/23023961/why-monadplus-and-not-monad-monoid) – phadej Aug 02 '15 at 17:18
  • @phadej No one can write a simple code example which will show the difference, but everyone think http://stackoverflow.com/questions/23023961/why-monadplus-and-not-monad-monoid is answer to the question. – ais Aug 02 '15 at 17:38
  • 2
    @ais Please don't delete a question just because it was closed. Instead, either rally to get it reopened, or ask a fresh question (as you did here) and reference it, explaining _exactly_ what makes the fresh question different. I think you gave a good explanation of what makes this question different. But hiding the history of the question is a bit underhanded, and may rub people the wrong way. – Daniel Wagner Aug 02 '15 at 20:08

1 Answers1

15

Here is a very simple example of something one can do with Alternative:

import Control.Applicative
import Data.Foldable

data Nested f a = Leaf a | Branch (Nested f (f a))

flatten :: (Foldable f, Alternative f) => Nested f a -> f a
flatten (Leaf x) = pure x
flatten (Branch b) = asum (flatten b)

Now let's try the same thing with Monoid:

flattenMonoid :: (Foldable f, Applicative f) => Nested f a -> f a
flattenMonoid (Leaf x) = pure x
flattenMonoid (Branch b) = fold (flattenMonoid b)

Of course, this doesn't compile, because in fold (flattenMonoid b) we need to know that the flattening produces a container with elements that are an instance of Monoid. So let's add that to the context:

flattenMonoid :: (Foldable f, Applicative f, Monoid (f a)) => Nested f a -> f a
flattenMonoid (Leaf x) = pure x
flattenMonoid (Branch b) = fold (flattenMonoid b)

Ah, but now we have a problem, because we can't satisfy the context of the recursive call, which demands Monoid (f (f a)). So let's add that to the context:

flattenMonoid :: (Foldable f, Applicative f, Monoid (f a), Monoid (f (f a))) => Nested f a -> f a
flattenMonoid (Leaf x) = pure x
flattenMonoid (Branch b) = fold (flattenMonoid b)

Well, that just makes the problem worse, since now the recursive call demands even more stuff, namely Monoid (f (f (f a)))...

It would be cool if we could write

flattenMonoid :: ((forall a. Monoid a => Monoid (f a)), Foldable f, Applicative f, Monoid (f a)) => Nested f a -> f a

or even just

flattenMonoid :: ((forall a. Monoid (f a)), Foldable f, Applicative f) => Nested f a -> f a

and we can: instead of writing forall a. Monoid (f a), we write Alternative f. (We can write a typeclass that expresses the first, easier-to-satisfy constraint, as well.)

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380