In ancora più generale, ciò che si sta tentando di fare è applicare una trasformazione a un livello interno di una pila di trasformatori. Per due monadi arbitrari, la firma di tipo potrebbe essere simile a questo:
fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 a) -> t m1 a -> t m2 a
Fondamentalmente un livello superiore fmap
. In realtà, sarebbe probabilmente fare ancora più senso di combinare con una mappa sopra il parametro finale così:
fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 b) -> t m1 a -> t m2 b
Chiaramente questo non sta per essere possibile in tutti i casi, anche se quando la "fonte" monade è Identity
è probabile che sia più facile, ma posso immaginare di definire un'altra classe di tipi per i luoghi in cui funziona. Non penso che ci sia qualcosa di simile nelle tipiche librerie di trasformatori monad; tuttavia, alcuni navigazione sul hackage salta fuori qualcosa di molto simile in the Monatron
package:
class MonadT t => FMonadT t where
tmap' :: FunctorD m -> FunctorD n -> (a -> b)
-> (forall x. m x -> n x) -> t m a -> t n b
tmap :: (FMonadT t, Functor m, Functor n) => (forall b. m b -> n b)
-> t m a -> t n a
tmap = tmap' functor functor id
nella firma per tmap'
, i tipi FunctorD
sono fondamentalmente implementazioni ad hoc di fmap
invece di utilizzare direttamente Functor
istanze.
Inoltre, per due costruttori di tipo Functor-come F e G, una funzione con un tipo come (forall a. F a -> G a)
descrive a natural transformation da F a G. C'è molto probabilmente un'altra attuazione della tabella di trasformatore che si desidera da qualche parte nel pacchetto category-extras
ma io Non sono sicuro di quale sia la versione teorica della categoria di un trasformatore monade, quindi non so come possa essere chiamato.
Dal tmap
richiede solo un'istanza Functor
(che qualsiasi Monad
deve avere) e una trasformazione naturale, e qualsiasi Monad
ha una trasformazione naturale dal Identity
Monade fornito dal return
, la funzione che si desidera può essere scritta genericamente per qualsiasi istanza di FMonadT
come tmap (return . runIdentity)
- l'inserimento della monade "base" è definito come sinonimo del trasformatore applicato a Identity
, in ogni caso, che è generalmente il caso delle librerie di trasformatori.
Tornando al tuo esempio specifico, nota che Monatron ha effettivamente un'istanza di FMonadT
per StateT
.
credo che modifica di TomMD non è corretta. Credo che l'originale 'g :: StateT [Int] IO Int' dovrebbe stare in piedi. – glguy
Mi sono piaciute le altre modifiche, ho corretto la parentesi ... – HaskellElephant
Questa domanda mi sembra quella che sto cercando, ma le risposte sono molto più complicate di http://stackoverflow.com/questions/17325485/combining-statet- io-con-stato, che ha fatto il lavoro per me. – crockeea