Различие между классами типов MonadPlus, Alternative и Monoid?
Стандартные библиотеки классов типов Haskell MonadPlus
, Alternative
, а также Monoid
каждый из них предоставляет два метода с практически одинаковой семантикой:
- Пустое значение:
mzero
,empty
, или жеmempty
, - Оператор
a -> a -> a
который объединяет значения в классе типов:mplus
,<|>
, или жеmappend
,
Все три определяют эти законы, которым должны придерживаться экземпляры:
mempty `mappend` x = x
x `mappend` mempty = x
Таким образом, кажется, что все три класса типов предоставляют одинаковые методы.
(Alternative
также обеспечивает some
а также many
, но их определения по умолчанию обычно достаточно, и поэтому они не слишком важны с точки зрения этого вопроса.)
Итак, мой вопрос: почему у этих трех чрезвычайно похожих классов? Есть ли какая-то реальная разница между ними, помимо их ограничений суперкласса?
1 ответ
MonadPlus
а также Monoid
служить разным целям.
Monoid
параметризован по типу вида *
,
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
закон является слишком сильным требованием. Например, это не относится к Backwards!
Когда мы смотрим на MonadPlus, закон о пустом >>= f = empty почти навязывается нам. Пустая конструкция не может содержать никаких "а" для вызова функции 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
может быть (а иногда и есть) что-то совершенно другое.