2010-11-09 10 views
15

Diciamo che ho una funzioneCombinando monadi StateT e degli Stati

f :: State [Int] Int 

e una funzione:

g :: StateT [Int] IO Int 

voglio usare f in g e superare lo stato tra di loro. Esiste una funzione di libreria per
StateT (return . runState f)? O in generale, dato un trasformatore monade con una monade corrispondente, esiste una funzione di libreria per esso?

+0

credo che modifica di TomMD non è corretta. Credo che l'originale 'g :: StateT [Int] IO Int' dovrebbe stare in piedi. – glguy

+0

Mi sono piaciute le altre modifiche, ho corretto la parentesi ... – HaskellElephant

+1

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

risposta

5

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.

+0

Non ho visto il pacchetto Monatron. Dovrò guardare più da vicino per giudicarlo. Mi piace la tua idea di definire una classe di tipo per quando funziona, qualcuno può confermare o disconfermare che Monatron fa questo? – HaskellElephant

4

Tale funzione non è definibile per tutti i trasformatori monad. La monade Cont r, ad esempio, non può essere sollevata in ContT r IO perché ciò richiederebbe di trasformare una continuazione nella monade IO (a -> IO r) in una pura continuazione (a -> r).

+0

Non ci ho pensato. Come hai detto tu, questo non è possibile per ogni monadtransformer. Quindi richiederebbe un tipo speciale di connessione tra il trasformatore e la monade corrispondente, quindi ... – HaskellElephant

+0

La trasformazione non andrebbe nella direzione opposta? Poiché 'r' in un tipo di continuazione è solitamente polimorfico, si può semplicemente scrivere' (Cont. RunCont) :: Cont (m r) a -> Cont t r m a'. –

+0

@camccann stavo andando per una classe generalizzata di trasformazioni (Monad m => m a -> (TransformerOf m) m 'a) (notazione abusiva un po'). Se provi a scrivere un'istanza per (Contro a -> Cont t r a), rimarrai bloccato al punto che ho descritto. – Heatsink

4

Quello che chiediamo è una mappatura (noto come un morfismo monade) da una monade StateT m-StateT n. Userò la libreria mmorph, che fornisce un set di strumenti molto utile per lavorare con i morfismi di monad.

Per eseguire il State -> StateT m trasformazione che si sta cercando, inizieremo definendo un morfismo di generalizzare il Identity Monade incorporato in State,

generalize :: Monad m => Identity a -> m a 
generalize = return . runIdentity 

Avanti dobbiamo provare a sollevare questo morfismo ad agire su la monade interna del tuo StateT. Cioè, vogliamo una funzione che ha dato una mappatura da una monade all'altra (ad esempio il nostro morfismo generalize), ci darà una funzione che agisce sulla base monade di un trasformatore monade, ad es. t Identity a -> t m a. Troverete questo assomiglia la funzione hoist di s' mmorphMFunctor classe,

hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b 

Mettendo insieme i pezzi,

myAction :: State s Int 
myAction = return 2 

myAction' :: Monad m => StateT s m Int 
myAction' = hoist generalize myAction