As typeclasses Haskell da biblioteca padrão MonadPlus
, Alternative
e Monoid
cada uma fornece dois métodos com essencialmente a mesma semântica:
- Um valor vazio:
mzero
,empty
, oumempty
. - Um operador
a -> a -> a
que une valores na typeclass juntos:mplus
,<|>
oumappend
.
Todos os três especificam essas leis às quais as instâncias devem obedecer:
mempty `mappend` x = x
x `mappend` mempty = x
Assim, parece que as três typeclasses estão fornecendo os mesmos métodos.
( Alternative
também fornece some
e many
, mas suas definições padrão geralmente são suficientes e, portanto, não são muito importantes em termos desta questão.)
Então, minha pergunta é: por que essas três classes extremamente semelhantes? Existe alguma diferença real entre eles, além de suas diferentes restrições de superclasse?
Applicative
eMonadPlus
parecem ser exatamente os mesmos (restrições da superclasse do módulo).ArrowZero
eArrowPlus
para flechas. Minha aposta: tornar as assinaturas de tipo mais limpas (o que torna as diferentes restrições de superclasse a verdadeira diferença).ArrowZero
eArrowPlus
ter tipo* -> * -> *
, o que significa que você pode passá-los para o tipo de seta uma vez para uma função que precisa usá-los para uma infinidade de tipos, para usar um,Monoid
você teria que exigir uma instância deMonoid
para cada particular instanciação, e você não tem garantia de que foram tratadas de maneira semelhante, as instâncias podem não estar relacionadas!Respostas:
MonadPlus
eMonoid
servem a diferentes propósitos.A
Monoid
é parametrizado sobre um tipo de tipo*
.class Monoid m where mempty :: m mappend :: m -> m -> m
e, portanto, pode ser instanciado para quase qualquer tipo para o qual haja um operador óbvio que seja associativo e que tenha uma unidade.
No entanto,
MonadPlus
não especifica apenas que você tem uma estrutura monoidal, mas também que essa estrutura está relacionada a comoMonad
funciona, e que essa estrutura não se preocupa com o valor contido na mônada, isso é (em parte) indicado pelo fato issoMonadPlus
leva um argumento do tipo* -> *
.class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a
Além das leis monoidais, temos dois conjuntos potenciais de leis que podemos aplicar
MonadPlus
. Infelizmente, a comunidade discorda quanto ao que deveriam ser.Pelo menos sabemos
mzero >>= k = mzero
mas há duas outras extensões concorrentes, a lei de distribuição de esquerda (sic)
mplus a b >>= k = mplus (a >>= k) (b >>= k)
e a esquerda pega a lei
mplus (return a) b = return a
Portanto, qualquer instância de
MonadPlus
deve satisfazer uma ou ambas as leis adicionais.Então sobre o quê
Alternative
?Applicative
foi definido depoisMonad
, e logicamente pertence como uma superclasse deMonad
, mas em grande parte devido às diferentes pressões sobre os designers em Haskell 98, aindaFunctor
não era uma superclasse deMonad
até 2015. Agora finalmente temosApplicative
como uma superclasse deMonad
em GHC (se não ainda em um padrão de idioma.)Efetivamente,
Alternative
é para oApplicative
queMonadPlus
éMonad
.Para estes nós obteríamos
empty <*> m = empty
analogamente ao que temos com
MonadPlus
e existem propriedades distributivas e de captura semelhantes, pelo menos uma das quais você deve satisfazer.Infelizmente, mesmo a
empty <*> m = empty
lei é uma afirmação muito forte. Não vale para Backwards , por exemplo!Quando olhamos para MonadPlus, a lei vazia >> = f = vazia é quase imposta a nós. A construção vazia não pode ter nenhum 'a's nela para chamar a função de
f
qualquer maneira.No entanto, como não
Applicative
é uma superclasse de e não é uma superclasse de , acabamos definindo ambas as instâncias separadamente.Monad
Alternative
MonadPlus
Além disso, mesmo se
Applicative
fosse uma superclasse deMonad
, você acabaria precisando daMonadPlus
classe de qualquer maneira, porque mesmo se nós obedecêssemosempty <*> m = empty
isso não é estritamente suficiente para provar que
empty >>= f = empty
Portanto, afirmar que algo é a
MonadPlus
é mais forte do que afirmar que éAlternative
.Agora, por convenção, o
MonadPlus
eAlternative
para um determinado tipo devem concordar, mas oMonoid
pode ser completamente diferente.Por exemplo, o
MonadPlus
eAlternative
paraMaybe
fazem a coisa óbvia:instance MonadPlus Maybe where mzero = Nothing mplus (Just a) _ = Just a mplus _ mb = mb
mas a
Monoid
instância eleva um semigrupo em aMonoid
. Infelizmente, porque não existia umaSemigroup
classe na época em Haskell 98, ele faz isso solicitando aMonoid
, mas não usando sua unidade. ಠ_ಠ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
é uma afirmação mais forte do queAlternative
, que por sua vez é uma afirmação mais forte do queMonoid
, e embora as instânciasMonadPlus
eAlternative
para um tipo devam estar relacionadas,Monoid
pode ser (e às vezes é) algo completamente diferente.fonte
mempty `mappend` x ≡ x
.MonadPlus
eAlternative
implementações?Monad
que é um,Alternative
mas não umMonadPlus
. Eu fiz uma pergunta sobre como encontrar um exemplo específico disso; se você conhece algum, adoraria ver.MonadPlus
duas classes estão realmente disfarçadas como uma porque a maioria das pessoas não se importa.