2011-09-14 13 views
5

Scusa se la domanda sembra un po 'banale ... non è per me. ho felicemente composto la seguente monade:Estrazione della composizione monad come trasformatore

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

che è, beh, una monade ben educati. ReaderT è un trasformatore monade e State è la monade di stato, e AlgRO e AlgState sono tipi di dati parametrizzati in i per lo stato mutabile e di sola lettura, rispettivamente. Ora, se io voglio fare di questo un trasformatore ordinata monade con Newtype, qualcosa di simile:

newtype SbT m i a = SbT { 
    runSbT:: m (SB i a) 
} 

come dovrei procedere? Non riesco nemmeno a mettere insieme il metodo di bind (di Monad typeclass), molto meno "lift" (di MonadTrans) ... Immagino che la derivazione automatica possa aiutare, ma voglio capire come funziona in questo caso.

Grazie in anticipo.

risposta

10

Non penso che la definizione per SbT sia ciò che desideri. Ciò definisce la composizione del functor e presupponendo che il parametro m sia un Functor o Applicative, questo dovrebbe preservare tali proprietà. Ma una composizione del genere non crea, in generale, una nuova monade su altre due. Vedi this question per ulteriori informazioni su questo argomento.

Quindi, come do si crea il trasformatore monad che si desidera, quindi? Mentre le monadi non compongono direttamente, è possibile comporre i trasformatori di Monade . Quindi, per costruire un nuovo trasformatore rispetto a quelli esistenti, in pratica si vuole semplicemente dare un nome a quella composizione. Questo differisce dal newtype che hai perché lì stai applicando direttamente lo m invece di passarlo allo stack del trasformatore.

Una cosa da tenere a mente su come definire i trasformatori monade è che essi funzionano necessariamente "al contrario" in un certo modo - quando si applica un trasformatore composito ad una monade, il trasformatore "più interno" ottiene la prima crepa in essa, e la monade trasformata che produce è ciò con cui il prossimo trasformatore riesce a lavorare, & c. Si noti che questo non è diverso dall'ordine che si ottiene quando si applica una funzione composta a un argomento, ad es. (f . g . h) x innanzitutto fornisce l'argomento h, anche se f è la "prima" funzione nella composizione.

Ok, in modo che il trasformatore composito deve prendere la monade quale viene applicato e passarlo al più interno trasformatore, che è, uhm .... oops, risulta che SB è già applicato ad una monade. Non c'è da stupirsi che non funzionasse. Dovremo rimuoverlo, per prima cosa. Dov'è? Non State --we potrebbe rimuovere quello, ma non vogliamo, perché è parte di ciò che si desidera. Hmm, ma aspetta - che cos'è State definito come, di nuovo? Oh sì:

type State s = StateT s Identity 

Aha, eccoci. Prendiamo quello Identity di là.Si va dalla definizione attuale:

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

Per la forma equivalente:

type SB i a = ReaderT (AlgRO i) (StateT (AlgState i) Identity) a 

Poi si dà il pigro out:

type SB' i m a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
type SB i a = SB' i Identity a 

Ma ora SB' guarda con sospetto, come un trasformatore monade definizione, e con una buona ragione, perché lo è. Così ricreiamo il newtype involucro, e lanciamo un paio di casi là fuori:

newtype SbT i m a = SbT { getSB :: ReaderT (AlgRO i) (StateT (AlgState i) m) a } 

instance (Functor m) => Functor (SbT i m) where 
    fmap f (SbT sb) = SbT (fmap f sb) 

instance (Monad m) => Monad (SbT i m) where 
    return x = SbT (return x) 
    SbT m >>= k = SbT (m >>= (getSB . k)) 

instance MonadTrans (SbT i) where 
    lift = SbT . lift . lift 

runSbT :: SbT i m a -> AlgRO i -> AlgState i -> m (a, AlgState t) 
runSbT (SbT m) e s = runStateT (runReaderT m e) s 

Un paio di cose da prendere nota di: La runSbT funzione di qui non è la funzione di accesso campo, ma piuttosto una funzione composta "run" per ogni trasformatore nello stack di cui siamo a conoscenza. Allo stesso modo, la funzione lift deve sollevarsi una volta per i due trasformatori interni, quindi aggiungere il wrapper finale newtype. Entrambe le cose funzionano come un singolo trasformatore monad, nascondendo il fatto che in realtà è un composito.

Se lo si desidera, dovrebbe essere semplice scrivere istanze per MonadReader e MonadState sollevando le istanze per i trasformatori composti.

+0

che lo farà. Grazie! – dsign

2

Hai intenzione di avvolgere un ulteriore m intorno alle cose nel tuo nuovo tipo? Vorrei suggerire il seguente:

newtype Sb i a = Sb { runSb :: SB i a } 

... che dovrebbe rendere il vostro instance Monad (Sb i) un po 'più facile da scrivere. Se stai davvero provando a scrivere un trasformatore monad, dovresti usare i trasformatori fino in fondo; per esempio,

type SBT m i a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
newtype SbT m i a = SbT { runSbT :: SBT m i a } 

Come secondo punto di interesse, è spesso preferibile η-ridurre type sinonimi (poiché devono sempre essere "pienamente applicata"); facendo questo con SB e SBT sarebbe simile a questa:

type SB i = ReaderT (AlgRO i) (State (AlgState i)) 
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m) 
+0

A proposito, non è possibile trasformare un trasformatore monad in 'SbT' in questo modo. I trasformatori hanno un gentile '(* -> *) -> * -> *', cioè prendendo una monade e un tipo come argomenti. Il parametro 'i' deve essere il primo, quindi puoi applicarlo parzialmente. –