2013-05-14 13 views
7

Al momento sto avendo un po 'di difficoltà con i trasformatori monad. Sto definendo alcune diverse relazioni non deterministiche che fanno uso di trasformatori. Sfortunatamente, ho difficoltà a capire come tradurre in modo pulito da un modello efficace a un altro.Transformation under Transformers

Supponiamo che queste relazioni siano "foo" e "bar". Supponiamo che "foo" si riferisca a As e Bs a Cs; supponiamo che "bar" contenga Bs e Cs a Ds. Definiremo "bar" in termini di "foo". Per rendere le cose più interessanti, il calcolo di queste relazioni fallirà in modi diversi. (Poiché la relazione bar dipende dalla relazione foo, i suoi casi di fallimento sono un superset.) Ho quindi dare le seguenti definizioni del tipo:

data FooFailure = FooFailure String 
data BarFailure = BarSpecificFailure | BarFooFailure FooFailure 
type FooM = ListT (EitherT FooFailure (Reader Context)) 
type BarM = ListT (EitherT BarFailure (Reader Context)) 

Vorrei quindi aspetto di essere in grado di scrivere i rapporti con i seguenti identificativi di funzione :

foo :: A -> B -> FooM C 
bar :: B -> C -> BarM D 

mio problema è che, quando si scrive la definizione di "bar", ho bisogno di essere in grado di ricevere gli errori dalla relazione "pippo" e correttamente li rappresentano nello spazio "bar". Quindi sarei fine con una funzione della forma

convert :: (e -> e') -> ListT (EitherT e (Reader Context) a 
        -> ListT (EitherT e' (Reader Context) a 

Posso anche scrivere che bestiola eseguendo il ListT, mappatura su EitherT, e poi rimontare il ListT (perché capita che m [a] può essere convertiti in ListT ma). Ma questo sembra ... disordinato.

C'è una buona ragione per cui non posso semplicemente eseguire un trasformatore, fare alcune cose sotto e genericamente "rimetterlo"; il trasformatore che ho eseguito potrebbe avere effetti e non riesco magistralmente a annullarli. Ma c'è un modo in cui posso sollevare una funzione abbastanza in una pila di trasformatori per fare un po 'di lavoro per me quindi non devo scrivere la funzione convert mostrata sopra?

risposta

3

Penso convertito è una buona risposta, e l'utilizzo di Control.Monad.Morph e Control.Monad.Trans.Either E '(quasi) molto semplice da scrivere:

convert :: (Monad m, Functor m, MFunctor t) 
      => (e -> e') 
      -> t (EitherT e m) b -> t (EitherT e' m) b 
convert f = hoist (bimapEitherT f id) 

il piccolo problema è che ListT non è un'istanza di MFunctor. Credo che questo sia l'autore boicottaggio ListT perché doesn't follow the monad transformer laws, però, perché è facile scrivere un tipo-checking esempio

instance MFunctor ListT where hoist nat (ListT mas) = ListT (nat mas) 

In ogni caso, in generale dare un'occhiata a Control.Monad.Morph per affrontare le trasformazioni naturali (parti di) pile trasformatore. Direi che si adatta alla definizione di sollevare una funzione "appena sufficiente" in una pila.

+1

Sì, stavo boicottando ListT. Usa 'pipe' per un ListT corretto. Inoltre, puoi usare 'fmapLT' dal pacchetto errori per modificare il valore di sinistra. –

+0

Stavo cercando 'fmapLT' ...! Ma avrei giurato che era in "entrambi" e non l'ho fatto. –

+0

Eccellente; grazie ad entrambi. Questo è esattamente ciò di cui mi stavo chiedendo. :) – tvynr

Problemi correlati