Стандартные библиотеки Haskell typeclasses 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
служат для разных целей.
A 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)
а левый закон catch
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
, и существуют аналогичные свойства дистрибутива и catch, по крайней мере один из которых вы должны удовлетворить.
К сожалению, даже закон empty <*> m = empty
слишком силен. Например, это не относится к Backwards!
Когда мы смотрим на 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
. К сожалению, в то время, когда в Haskell 98 не существовал класс Semigroup
, он делает это, переставляя 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
может быть (и иногда есть) чем-то совершенно другим.