2013-03-25 11 views
9

Powiedzmy mam monadT:Dlaczego w tym przypadku nie musisz używać "windy" podczas interakcji z zagnieżdżoną StateT monadT?

type Wrap a = ReaderT Env (StateT Int (StateT Int Identity)) a 

Ważne jest, aby pamiętać, jest to, że jeden StateT jest owijanie drugiego, a oba są owinięte wewnątrz trzeciego MonadT, mianowicie ReaderT.

i odpowiednia funkcja runWrap dla wygody:

type Env = Map.Map Char Integer 

runWrap :: Env -> Int -> Int -> Wrap a -> a 
runWrap env st1 st2 m = runIdentity $ evalStateT (evalStateT (runReaderT m env) st2) st1 

a generycznego monady państwowej tock:

tock :: (Num s, MonadState s m) => m() 
tock = do modify (+1) 

I teraz utworzyć monadT oblewania gdzie wewnątrz używam tock:

aWrap :: Wrap (Int, Int) 
aWrap = do 
    lift tock 
    lift . lift $ tock 
    x <- get 
    y <- lift . lift $ get 
    return (x, y) 

Uruchom go:

env = Map.fromList [('x', 1)] 
runWrap env 1 200 aWrap 
// answer: (201,2) 

Użycie tutaj lift ma dla mnie sens pod względem zrozumienia sposobu interakcji w/zagnieżdżonych warstwach MonadT.

Jednak ta działa również i daj mi tę samą odpowiedź: (201,2):

aWrap :: Wrap (Int, Int) 
aWrap = do 
    tock 
    lift . lift $ tock 
    x <- get 
    y <- lift . lift $ get 
    return (x, y) 

Myślę wywołując tock w/o lift, to brzmi jakby tock jest stosowany do zewnętrznej MonadT, mianowicie ReaderT , co nie ma sensu. Ale dlaczego to działa?

P.S. Proszę zignorować obecność Env tutaj, nie ma nic wspólnego z pytaniem, po prostu wybór zewnętrznej Monady, której używam.

Odpowiedz

8

Prawdopodobnie używasz egzemplarza typu MonadState, nie będąc tego świadomym. Ta czcionka jest zdefiniowana w pakiecie mtl (i również w monads-fd).

MonadState pozwala na użycie metod monady State, bezpośrednio i bez wyraźnego podnoszenia, w wielu stosach monad na podstawie State.

Spójrz na następujących dwóch linii w haddocks:

Monad m => MonadState s (StateT s m) 
MonadState s m => MonadState s (ReaderT r m) 

Pierwszy z nich mówi, że każdy StateT jest instancją MonadState (jak należy się spodziewać!). Drugi mówi, że dowolny ReaderT, którego podstawową monadą jest instancja MonadState, jest również instancją MonadState. Co się dzieje w twoim przypadku.

Patrząc na source code dla MonadState, znajdziemy:

instance MonadState s m => MonadState s (ReaderT r m) where 
    get = lift get 
    put = lift . put 
    state = lift . state 

modify :: MonadState s m => (s -> s) -> m() 
modify f = state (\s -> ((), f s)) 

Jak widać, wewnętrzna maszyneria typeclass dba o zniesieniu.

Istnieją inne typografie, które oferują podobną funkcjonalność, jak MonadReader, MonadWriter i MonadRWS.

Powiązane problemy