2016-05-06 21 views
3

Lassen Sie uns sagen, dass wir einen Stapel von Monaden mit einem Zustand Monade Transformator als Außen meisten Transformator wie diese haben:Staat Monaden: Der Übergang von einem Zustand Typ in einen anderen

-- | SEWT: Composition of State . Except . Writer monad transformers in that 
-- order where Writer is the innermost transformer. 
-- the form of the computation is: s -> (Either e (a, s), w) 
newtype SEWT s e w m a = SEWT { 
    _runSEWT :: StateT s (ExceptT e (WriterT w m)) a } 
    deriving (Functor, Applicative, Monad, 
       MonadState s, MonadError e, MonadWriter w) 

-- | 'runSEWT': runs a 'SEWT' computation given an initial state. 
runSEWT :: SEWT s e w m a -> s -> m (Either e (a, s), w) 
runSEWT ev e = runWriterT $ runExceptT $ runStateT (_runSEWT ev) e 

Wir dann tun wollen, in einigen Formular: SEWT s e w m a -> s -> SEWT t e w m a. Dies ist natürlich nicht möglich mit (>>=) oder einem do Block, da eine Zustandsmonade mit s als Zustand nicht die gleiche Monade wie eine mit t ist.

dann kann ich so etwas wie dies heraufbeschwören:

-- | 'sewtTransition': transitions between one 'SEWT' computation with state s, 
-- to another with state s. The current state and result of the given 
-- computation is given to a mapping function that must produce the next 
-- computation. The initial state must also be passed as the last parameter. 
transitionState :: (Monad m, Monoid w) => ((a, s) -> SEWT t e w m a) 
       -> m (SEWT s e w m a) -> s -> m (SEWT t e w m a) 
transitionState _trans _comp _init = do 
    (res, logs) <- _comp >>= flip runSEWT _init 
    return $ do tell logs 
       case res of Left fail -> throwError fail 
          Right succ -> _trans succ 

-- 'withState': behaves like 'transitionState' but ignores the state of 
-- the first computation. 
withState :: (Monad m, Monoid w) 
      => m (SEWT s e w m a) -> s -> m (SEWT t e w m a) 
withState = transitionState $ return . fst 

Aber gibt es vielleicht eine elegantere und allgemeine Art und Weise von einem Zustand Typ in einen anderen zu bewegen?

Ich bin sowohl an Lösungen interessiert, bei denen die zweite Berechnung nicht vom Endzustand (nur das Ergebnis) der ersten Berechnung abhängt, als auch davon, wo sie ist.

Edit1: Verbesserte Übergangsfunktionen:

transSEWT :: Functor m => (((a, y), x) -> (a, y)) -> SEWT x e w m a -> x -> SEWT y e w m a 
transSEWT f x_c x_i = SEWT $ StateT $ \y_i -> ExceptT . WriterT $ 
    first ((\(a, x_f) -> f ((a, y_i), x_f)) <$>) <$> runSEWT x_c x_i 

changeSEWT :: Functor m => SEWT x e w m a -> x -> SEWT y e w m a 
changeSEWT = transSEWT fst 

transS :: Monad m => (((a, y), x) -> (a, y)) -> StateT x m a -> x -> StateT y m a 
transS f x_c x_i = StateT $ \y_i -> do (a, x_f) <- runStateT x_c x_i 
             return $ f ((a, y_i), x_f) 

changeS :: Monad m => StateT x m a -> x -> StateT y m a 
changeS = transS fst 
+0

Verknüpfung zusammen mit anderer Frage: http://stackoverflow.com/questions/28690448/what-is-indexed-monad – Centril

Antwort

6

Ihre Idee kann mit der indiziert Zustand Monade implementiert werden.

newtype IState i o a = IState { runIState :: i -> (o, a) } 

Ein Wert vom Typ IState i o a ist ein Stateful-Berechnung, die einen Wert vom Typ liefert a von i zu o im Prozeß den Typ des impliziten Zustand zu transformieren. Vergleichen Sie dies mit dem regulären State Monade, die Ihnen nicht erlauben, die Art der seinen Zustand zu ändern:

type State s = IState s s 

Sequencing indiziert Zustand Monaden sollte sicherstellen, dass die Ein- und Ausgänge in einer Reihe aufstellen. Der Ausgabetyp einer Berechnung ist die Eingabe des nächsten. Geben Sie Atkey parameterised monad (jetzt häufiger bekannt als die indizierte Monade), die Klasse der Monade-ähnliche Dinge beschreiben einen Pfad durch eine gerichtete Grafik.

class IMonad m where 
    ireturn :: a -> m i i a 
    (>>>=) :: m i j a -> (a -> m j k b) -> m i k b 

(>>>) :: IMonad m => m i j a -> m j k b -> m i k b 
mx >>> my = mx >>>= const my 

eine indizierte Monade Bindung ist wie Dominosteine ​​zu spielen: Wenn Sie eine Möglichkeit haben, i-j und einen Weg, um j-k zu bekommen, >>>= Ihre Dominosteine ​​zusammen in eine größere Berechnung kleben, die aus geht i bis k. McBride beschreibt eine leistungsfähigere Version dieser indizierten Monade in Kleisli Arrows of Outrageous Fortune, aber diese ist völlig ausreichend für unsere Zwecke.

Wie ich oben beschrieben habe, ist Domino-ähnliche Sequenzierung genau das, was für die indizierte Zustands-Monade benötigt wird, die eine Ausrichtung der Ein- und Ausgänge erfordert.

Das Abrufen eines Werts aus einer indizierten Statusmonade ändert den Typ des Status nicht.

get :: IState s s s 
get = IState $ \s -> (s, s) 

Setzen Sie einen Wert in den indizierten Zustand Monad verwirft den alten Zustand. Dies bedeutet, dass der Typ des Eingabezustands beliebig sein kann.

put :: s -> IState i s() 
put x = IState $ \_ -> (x,()) 

Beachten Sie, dass der gesamte Code mit IState zu arbeiten, ist genau das gleiche wie State! Es sind nur die Typen, die klüger geworden sind.

Hier ist eine einfache IState Berechnung, die einen Zustand des Typs Int erwartet, ändert den Zustand in String und gibt eine boolesche Antwort zurück. All dies wird statisch überprüft.

myStateComputation :: IState Int String Bool 
myStateComputation = 
    -- poor man's do notation. You could use RebindableSyntax 
    get    >>>= \s -> 
    put (show s)  >>> 
    ireturn (s > 5) 

main = print $ runIState myStateComputation 3 
-- ("3", False) 
+0

Das war ein wahres Vergnügen zu lesen. Ich nehme an, wir haben dann in der Kleisli-Kategorie ">> = >> :: IMonad m => (a -> m i j b) -> (b -> m j k c) -> a -> m i k c". Wenn Sie einige Punkte erweitern könnten: a) Ist es "üblich", RebindableSyntax dafür zu verwenden? b) Es gibt 'Control.Monad.Indexed', aber gibt es ein Hack-Paket, um dieses mit einem Monad-Transformator-Stack wie dem oben genannten zu verwenden, oder muss ich meine eigenen rollen? – Centril

+0

Ich spucke nur hier herum, aber ich erwarte, dass indexierte Monad-Transformatoren in einem McBride-ähnlichen Indexierungsschema wahrscheinlich glatter verlaufen als in einem Atkey-ähnlichen. Das Schöne an McBrides Idee ist, dass sobald man die Grundlagen verstanden hat, alle normalen Monaden direkt übersetzt werden: Transformatoren würden die Art '((k -> *) -> (k -> *)) -> ((k -> *) -> (k -> *)) 'und du würdest' ilift :: ma ~> tma' haben, wo 'typ f ~> g = forall i. f i -> g i. Lesen Sie _Kleisili Arrows of Outrageous Fortune_ für die ganze Geschichte. –

+0

In Bezug auf 'RebindableSyntax' - ist genau diese Art der Erweiterung gedacht. Ob es wirklich eine gute Idee ist, es zu benutzen, ist wirklich eine technische Entscheidung - ich würde Dinge wie "Wer wird den Code lesen?" Vor dem Einschalten betrachten. –