2011-09-14 36 views
5

Disculpa si la pregunta parece un poco trivial ... no es para mí. he compuesto felizmente el siguiente mónada:Abstracción de la composición de la mónada como un transformador

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

que es, así, una mónada bien comportado. ReaderT es un transformador de mónada y State es la mónada State, y AlgRO y AlgState son tipos de datos parametrizados en i para estado mutable y de solo lectura, respectivamente. Ahora, si yo quiero hacer de ese un transformador ordenada mónada con newtype, algo como esto:

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

cómo debo proceder? No puedo ni siquiera armar el método bind (de Monad type class), mucho menos "lift" (de MonadTrans) ... Supongo que la derivación automática podría ayudar, pero quiero entender cómo funciona en este caso.

Gracias de antemano.

Respuesta

10

No creo que esa definición para SbT sea lo que quiere. Eso define functor composición, y suponiendo que el parámetro m es un Functor o Applicative, esto debería conservar esas propiedades. Pero una composición como esa no crea, en general, una nueva mónada entre otras dos. Consulte this question para obtener más información sobre ese tema.

Entonces, ¿cómo hacer usted crea el transformador de mónada que desea, entonces? Mientras que las mónadas no se componen directamente, se pueden componer los transformadores de mónada. Por lo tanto, para construir un nuevo transformador a partir de los existentes, básicamente solo desea dar un nombre a esa composición. Esto difiere del newtype que tiene porque aplica el m directamente, en lugar de pasarlo a la pila del transformador.

Una cosa a tener en cuenta acerca de la definición de los transformadores de mónada es que necesariamente funcionan "hacia atrás" de ciertas maneras: cuando aplica un transformador compuesto a una mónada, el transformador "interno" obtiene la primera grieta en él, y la mónada transformada que produce es con la que trabaja el siguiente transformador, & c. Tenga en cuenta que esto no es diferente del orden que obtiene al aplicar una función compuesta a un argumento, p. (f . g . h) x da el argumento primero a h, aunque f es la "primera" función en la composición.

bien, así que su transformador compuesto tiene que tomar la mónada que se aplica a y pasarlo al transformador más interna, que es, uhm .... Vaya, resulta que es SB ya aplicado a una mónada. No es de extrañar que esto no funcionara. Tendremos que eliminar eso, primero. ¿Dónde está? No State --we podría eliminar eso, pero no queremos, porque es parte de lo que desea. Hmm, pero espera - ¿qué es State definido como, otra vez? Oh, sí:

type State s = StateT s Identity 

Aha, ahí vamos. Vamos a sacar ese Identity de allí.Vamos a partir de su definición actual:

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

Para la forma equivalente:

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

Luego lanzar la vago perezoso:

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

Pero ahora SB' parece sospechosamente a un transformador mónada definición, y con buena razón, porque lo es. Así que recrear la envoltura newtype, y tirar algunos casos por ahí:

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 par de cosas a tomar nota de: La función runSbT aquí no es el descriptor de acceso de campo, sino más bien una función compuesta "correr" para cada transformador en la pila que conocemos. Del mismo modo, la función lift tiene que levantar una vez para los dos transformadores internos, luego agregar la envoltura final newtype. Ambos hacen que funcione como un solo transformador de mónada, ocultando el hecho de que en realidad es un compuesto.

Si lo desea, también debería ser sencillo escribir instancias para MonadReader y MonadState, al levantar las instancias de los transformadores compuestos.

+0

que lo hará. ¡Gracias! – dsign

2

¿Tenía la intención de ajustar un m adicional alrededor de cosas en su nuevo tipo? Yo sugeriría lo siguiente:

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

... que debe hacer su instance Monad (Sb i) un poco más fácil de escribir. Si realmente está tratando de escribir un transformador de mónada, entonces debe usar transformadores hasta el fondo; por ejemplo,

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

Como segundo punto de interés, a menudo es preferible eta-reducir type sinónimos (ya que siempre deben ser "totalmente aplicada"); haciendo esto con SB y SBT se vería así:

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

Por cierto, no se puede hacer un transformador de mónada con 'SbT' de esa manera. Los transformadores tienen tipo '(* -> *) -> * -> *', es decir, toman una mónada y un tipo como argumentos. El parámetro 'i' tiene que ser el primero, por lo que puede aplicarlo parcialmente. –

Cuestiones relacionadas