72

Lo standard libreria Haskell typeclasses MonadPlus, Alternative e Monoid ciascuno forniscono due metodi con essenzialmente la stessa semantica:Distinzione tra typeclass MonadPlus, Alternative e Monoid?

  • Un valore vuoto: mzero, empty o mempty.
  • Un operatore a -> a -> a che unisce i valori nella classe di caratteri insieme: mplus, <|> o mappend.

Tutti e tre specificare queste leggi alle quali le istanze dovrebbero aderire:

mempty `mappend` x = x 
x `mappend` mempty = x 

Così, sembra che i tre typeclasses sono tutti fornendo gli stessi metodi.

(Alternative fornisce anche some e many, ma le loro definizioni di default sono in genere sufficienti, e quindi non sono troppo importanti in termini di questa domanda.)

Quindi, la mia domanda è: perché hanno questi tre estremamente classi simili? C'è una vera differenza tra loro, oltre ai loro diversi vincoli di superclasse?

+0

Questa è una buona domanda. In particolare, 'Applicativo' e' MonadPlus' sembrano essere * esattamente * gli stessi (vincoli di superclasse modulo). – Peter

+1

Ci sono anche 'ArrowZero' e' ArrowPlus' per le frecce. La mia scommessa: rendere più pulite le firme dei tipi (il che rende i diversi vincoli della superclasse * la * vera differenza). –

+1

@CatPlusPlus: beh, 'ArrowZero' e' ArrowPlus' hanno tipo '* -> * -> *', il che significa che è possibile passarli per il tipo di freccia una volta per una funzione che deve essere utilizzata per una moltitudine di tipi , per usare un 'Monoid' dovresti richiedere un'istanza di' Monoid' per ogni particolare istanza, e non avresti alcuna garanzia che siano stati gestiti in un modo simile, le istanze potrebbero non essere correlate! –

risposta

96

MonadPlus e Monoid servono a diversi scopi.

A Monoid è parametrizzato su un tipo di tipo *.

class Monoid m where 
    mempty :: m 
    mappend :: m -> m -> m 

e quindi può essere istanziata per quasi qualsiasi tipo per i quali esiste un operatore ovvio che è associativa e che ha un'unità.

Tuttavia, MonadPlus non solo specifica che si dispone di una struttura monoidale, ma anche che tale struttura è legata al modo in cui le opere, Monade che tale struttura non si preoccupano il valore contenuto nella monade, questo è (in parte) indicato dal fatto che MonadPlus accetta un argomento di tipo * -> *.

class Monad m => MonadPlus m where 
    mzero :: m a 
    mplus :: m a -> m a -> m a 

Oltre alle leggi monoid, abbiamo due potenziali serie di leggi che possiamo applicare a MonadPlus. Purtroppo, la comunità non è d'accordo su cosa dovrebbero essere.

Almeno sappiamo

mzero >>= k = mzero 

ma ci sono altre due estensioni in competizione, la legge di sinistra (sic) la distribuzione

mplus a b >>= k = mplus (a >>= k) (b >>= k) 

e la legge fermo sinistra

mplus (return a) b = return a 

Pertanto, qualsiasi istanza di MonadPlus dovrebbe soddisfare una o entrambe queste leggi aggiuntive.

Che dire di Alternative?

Applicative è stato definito dopo Monad, e logicamente appartiene come una superclasse di Monad, ma in gran parte a causa delle diverse pressioni sui progettisti indietro nel Haskell 98, anche Functor non era una superclasse di Monad fino al 2015. Ora abbiamo finalmente hanno Applicative come una superclasse di Monad a GHC (se non ancora in un linguaggio standard.)

in effetti, è quello di AlternativeApplicative cosa MonadPlus è quello di Monad.

Per questi otterremmo

empty <*> m = empty 

analogamente a quanto abbiamo con MonadPlus ed esistono distributive e di cattura simili proprietà, almeno uno dei quali si dovrebbe soddisfare.

Sfortunatamente, anche la legge empty <*> m = empty è un'affermazione troppo forte. Ad esempio, non è valido per Backwards!

Quando guardiamo a MonadPlus, il vuoto >> = f = la legge vuota è quasi forzata su di noi. La costruzione vuota non può avere alcun 'a's in esso per chiamare comunque la funzione f.

Tuttavia, dal momento che non è Applicative una superclasse di Monad e Alternative non è una superclasse di MonadPlus, finiamo per definire entrambi i casi separatamente.

Inoltre, anche se Applicative era una superclasse di Monad, che ci si finisce per dover classe MonadPlus in ogni modo, perché anche se abbiamo obbediamo

empty <*> m = empty 

che non è abbastanza strettamente per dimostrare che

empty >>= f = empty 

Quindi affermare che qualcosa è un MonadPlus è più forte di rivendicare che è Alternative.

Ora, per convenzione, il MonadPlus e Alternative per un determinato tipo dovrebbe concordare, ma la Monoid può essere completamente diversa.

Per esempio il MonadPlus e Alternative per Maybe fare la cosa più ovvia:

instance MonadPlus Maybe where 
    mzero = Nothing 
    mplus (Just a) _ = Just a 
    mplus _  mb = mb 

ma l'istanza Monoid solleva un semigruppo in un Monoid. Purtroppo perché non esisteva una classe Semigroup al momento in Haskell 98, lo fa richiedendo un Monoid, ma non utilizzando la sua unità.ಠ_ಠ

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; DRMonadPlus è un'indicazione forte di Alternative, che a sua volta è un'indicazione forte di Monoid, e mentre i MonadPlus e Alternative istanze di un tipo devono essere riferiti al Monoid può essere (e qualche volta è) qualcosa di completamente diverso.

+1

Una risposta molto perspicace! In particolare, non mi ero reso conto che 'empty <*> m = empty' non implica' empty >> = f = empty'. – 00dani

+2

Risposta eccellente, tuttavia l'ultima definizione sembra essere errata, non soddisfa 'mempty \' mappend \ 'x ≡ x'. – Vitus

+0

@Vitus: corretto. La definizione è '' 'Nothing' mappend' x = x''' e viceversa. – hammar

Problemi correlati