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.
che lo farà. Grazie! – dsign