Ottima domanda!
A trasformatore monad è un tipo che aggiunge alcune funzionalità a una monade di base arbitraria, preservando la monadeità. Purtroppo, i trasformatori monad sono inesprimibili in C# perché fanno un uso essenziale dei tipi più alti. Quindi, lavorando in Haskell,
class MonadTrans (t :: (* -> *) -> (* -> *)) where
lift :: Monad m => m a -> t m a
transform :: Monad m :- Monad (t m)
Esaminiamo questa linea per linea. La prima riga dichiara che un trasformatore monad è un tipo t
, che accetta un argomento di tipo * -> *
(ovvero un tipo che prevede un argomento) e lo trasforma in un altro tipo di tipo * -> *
. Quando ti rendi conto che tutte le monadi hanno il tipo * -> *
puoi vedere che l'intenzione è che lo t
trasforma le monadi in altre monadi.
La riga successiva dice che tutti i trasformatori monade devono supportare un'operazione lift
, che richiede una monade arbitrario m
e ascensori in mondo del trasformatore t m
.
Infine, il metodo transform
dice che per qualsiasi monad m
, t m
deve essere anche una monade. Sto utilizzando l'agente :-
da the constraints
package.
Questo avrà più senso con un esempio. Ecco un trasformatore monad che aggiunge Maybe
-ness a una base arbitraria monad m
. L'operatore nothing
ci consente di interrompere il calcolo.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
nothing :: Monad m => MaybeT m a
nothing = MaybeT (return Nothing)
Affinché MaybeT
per essere un trasformatore Monade, deve essere una monade ogniqualvolta suo argomento è una monade.
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
MaybeT m >>= f = MaybeT $ m >>= maybe (return Nothing) (runMaybeT . f)
Ora per scrivere l'implementazione MonadTrans
. L'implementazione di lift
avvolge il valore di ritorno del monad di base in un Just
.L'implementazione di transform
non è interessante; dice semplicemente al risolutore di vincoli di GHC di verificare che MaybeT
sia effettivamente una monade ogni volta che il suo argomento è.
instance MonadTrans MaybeT where
lift = MaybeT . fmap Just
transform = Sub Dict
Ora possiamo scrivere un calcolo monadica che usa MaybeT
aggiungere mancato, ad esempio, il State
monade. lift
ci consente di utilizzare i metodi standard State
get
e put
, ma abbiamo anche accesso a nothing
se abbiamo bisogno di fallire il calcolo. (Ho pensato di usare il tuo esempio di IEnumerable
(aka []
), ma c'è qualcosa di perverso sull'aggiunta mancata una monade che supporta già esso.)
example :: MaybeT (State Int)()
example = do
x <- lift get
if x < 0
then nothing
else lift $ put (x - 1)
Ciò che rende trasformatori monade veramente utile è la loro impilabilità. Questo ti permette di comporre grandi monadi con molte capacità di molte piccole monadi con una capacità ciascuna. Ad esempio, potrebbe essere necessario eseguire una determinata applicazione IO, leggere le variabili di configurazione e generare eccezioni; questo sarebbe essere codificato con un tipo come
type Application = ExceptT AppError (ReaderT AppConfig IO)
Ci sono strumenti in the mtl
package che la aiutano ad astratta sulla raccolta e l'ordine di trasformatori monade precisa in una determinata pila, che consente di elidere le chiamate a lift
.
ho trovato un riferimento davvero interessante di monadi, che conciliano: http://heinrichapfelmus.github.com/operational/Documentation.html#alternatives-to-monad-transformers Sostiene un modo generale di combinare monadi. Dovresti anche leggere questo documento, descrive come combinare i tipi di dati in modo generale ed estensibile: http://www.cs.ru.nl/~wouters/Publications/DataTypesALaCarte.pdf –