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