2015-08-02 4 views
9

Я читал Why MonadPlus and not Monad + Monoid?, и я понимаю теоретическое различие, но я не могу понять практическую разницу, потому что для List это выглядит одинаково.Что мы можем сделать с Альтернативой, но не можем сделать с Monoid?

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

Да. Может быть, имеет различные варианты реализации

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

Но мы можем реализовать Maybe моноидные таким же образом, как альтернативные

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

Так, кто-то может показать пример кода, который объясняет практическую разницу между альтернативным и Monoid?

вопрос не является дубликатом Why MonadPlus and not Monad + Monoid?

+2

Ах, извините, но это все еще дубликат. Toxaris [перечислены достаточно четко] (http://stackoverflow.com/a/23024217/745903), что вы не можете сделать с «Monoid», в частности, с пунктами 3. и 4 .: «** 3. ** Если мы хотите использовать бесконечно много разных 'a'.MonadPlus m => ...' вместо того, чтобы это невозможно. ** 4. ** Если мы не знаем, что нам нужно. 'MonadPlus m => ...' вместо того, чтобы это невозможно. "Если вам что-то непонятно, спросите конкретно о своих сомнениях. – leftaroundabout

+1

Это просто теоретическое объяснение. Даже там первый комментарий был «Мне бы очень хотелось увидеть конкретный пример здесь». Мне нужны примеры практического кода, которые могут показать, как точно 3 и 4 влияют на использование моноидов. – ais

+1

Хорошо, я понимаю, что вы имеете в виду. Я не думаю, что полезно приготовить некоторые надуманные примеры - конечно, для небольших примеров, которые мы могли бы здесь дать, вы всегда можете сказать _ «теперь почему бы нам просто не разоблачить этот частный тип данных абстрактно? _ Или _» пара ограничений Monoid (f A), Monoid (f B) ', но что?- это не выглядит так «_» или «почему мы не ставим эти бесконечно много/неизвестных типов в экзистенциальную/гадовую обертку» _. Но все эти обходные пути не очень практичны в большом проекте. Просто найдите некоторые библиотечные функции с помощью контекста «Альтернатива» и попробуйте написать их с помощью «Monoid». – leftaroundabout

ответ

15

Вот очень простой пример того, что можно сделать с 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) 

Теперь давайте попробуем то же самое с Monoid:

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

Конечно, это не скомпилируется, потому что в fold (flattenMonoid b) нам нужно знать, что уплощение создает контейнер с элементами, которые являются экземпляром Monoid. Итак, давайте добавим, что в контексте:

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

Ах, но теперь у нас есть проблемы, потому что мы не можем удовлетворить контекст рекурсивного вызова, который требует Monoid (f (f a)). Итак, давайте добавим, что в контексте:

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) 

Ну, что только усугубляет проблему, так как теперь рекурсивные запросы вызова еще вещи, а именно Monoid (f (f (f a))) ...

Было бы здорово, если бы мы могли написать

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

или даже просто

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

и мы можем: instea d написания forall a. Monoid (f a), пишем Alternative f. (Мы можем написать typeclass, который также выражает первое, более легкое для удовлетворения ограничение).

Смежные вопросы