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
34.5k7 gold badges88 silver badges159 bronze badges
asked Aug 2, 2015 at 9:59
8
  • 2
    Ahm... sorry, but this is still a duplicate. Toxaris listed quite clearly 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. Commented Aug 2, 2015 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. Commented Aug 2, 2015 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. Commented Aug 2, 2015 at 14:12
  • 1
    Have you deleted the closed question and posted it again? Please avoid that, it is really confusing. Commented Aug 2, 2015 at 16:16
  • 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. Commented Aug 2, 2015 at 20:08

1 Answer 1

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.)

answered Aug 2, 2015 at 19:57
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.