MonadPlus
и Monoid
служат разным целям.
Monoid
Параметр A параметризуется по типу вида *
.
class Monoid m where
mempty :: m
mappend :: m -> m -> m
и поэтому его можно создать практически для любого типа, для которого существует очевидный ассоциативный оператор, имеющий единицу измерения.
Однако MonadPlus
не только указывает, что у вас есть моноидальная структура, но также и то, что эта структура связана с тем, как она Monad
работает, и что эта структура не заботится о значении, содержащемся в монаде, это (частично) указывается фактом это MonadPlus
требует своего рода аргумента * -> *
.
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
В дополнение к моноидным законам у нас есть два потенциальных набора законов, к которым мы можем применить MonadPlus
. К сожалению, сообщество не согласны с тем, какими они должны быть.
По крайней мере, мы знаем
mzero >>= k = mzero
но есть два других конкурирующих расширения, левый (sic) закон распределения
mplus a b >>= k = mplus (a >>= k) (b >>= k)
и закон левого улова
mplus (return a) b = return a
Таким образом, любой экземпляр MonadPlus
должен удовлетворять одному или обоим этим дополнительным законам.
Так что насчет Alternative
?
Applicative
был определен позже Monad
и логически принадлежит к суперклассу Monad
, но в значительной степени из-за различного давления на разработчиков еще в Haskell 98, даже Functor
не был суперклассом Monad
до 2015 года. Теперь у нас, наконец, есть Applicative
суперкласс Monad
GHC (если не пока что в языковом стандарте.)
Фактически, Alternative
это Applicative
то, что MonadPlus
нужно Monad
.
Для этого мы получим
empty <*> m = empty
аналогично тому, что у нас есть, MonadPlus
и существуют аналогичные свойства распределения и захвата, по крайней мере, одно из которых вы должны удовлетворить.
К сожалению, даже empty <*> m = empty
закон слишком силен. Например, он не работает в обратном направлении !
Когда мы смотрим на MonadPlus, нам почти навязывают закон пустой >> = f = пустой. Пустая конструкция не может содержать никаких букв для вызова функции f
.
Однако, так как Applicative
это не суперкласс Monad
и Alternative
это не суперкласс MonadPlus
, мы заводиться определения обоих экземпляров отдельно.
Более того, даже если бы это Applicative
был суперкласс Monad
, вам все MonadPlus
равно понадобился бы этот класс, потому что даже если бы мы подчинялись
empty <*> m = empty
этого недостаточно, чтобы доказать, что
empty >>= f = empty
Так что утверждать, что что-то есть, MonadPlus
сильнее, чем утверждать, что это так Alternative
.
Итак, по соглашению, MonadPlus
и Alternative
для данного типа должны согласовываться, но Monoid
могут быть совершенно разными.
Например, MonadPlus
и Alternative
для Maybe
делают очевидную вещь:
instance MonadPlus Maybe where
mzero = Nothing
mplus (Just a) _ = Just a
mplus _ mb = mb
но Monoid
экземпляр поднимает полугруппу в Monoid
. К сожалению, поскольку Semigroup
в то время в Haskell 98 не существовало класса, он делает это, запрашивая Monoid
, но не используя его модуль. ಠ_ಠ
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
mappend (Just a) (Just b) = Just (mappend a b)
mappend Nothing x = x
mappend x Nothing = x
mappend Nothing Nothing = Nothing
TL; DR MonadPlus
является более сильным , чем требование Alternative
, которое в свою очередь является более сильным , чем требование Monoid
, и в то время , MonadPlus
и Alternative
экземпляры для типа должны быть связаны, то Monoid
может быть (а иногда) нечто совершенно иное.
Applicative
иMonadPlus
кажутся точно такими же (по модулю ограничений суперкласса).