2013-04-30 10 views
6

Temat ogólny: Chociaż uważam, że pomysł układania monad razem jest bardzo interesujący, mam wiele problemów z wyobrażeniem sobie, w jaki sposób kod jest wykonywany, i jakie są odpowiednie rozkazy, aby uruchomić warstwy. Poniżej znajduje się jeden przykład stosu: Writer, State, State i Error, w określonej kolejności (lub jest tam?).Skąd bierze się kolejność wykonywania funkcji w monaderze?

----------------------- 
-- Utility Functions -- 
----------------------- 

type Memory = Map String Int 
type Counter = Int 
type Log  = String 

tick :: (MonadState Counter m) => m() 
tick = modify (+1) 

record :: (MonadWriter Log m) => Log -> m() 
record msg = tell $ msg ++ "; " 

------------------ 
-- MonadT Stack -- 
------------------ 

mStack :: (MonadTrans t, MonadState Memory m, MonadState Counter (t m), MonadError ErrMsg (t m), MonadWriter Log (t m)) => t m Int 
mStack = do 
    tick 
    m <- lift get 
    let x = fromJust (M.lookup "x" m) in x 
    record "accessed memory" 
    case True of 
     True -> return 100 
     False -> throwError "false" 

Uwaga w mStack, czy zostanie zgłoszony błąd lub nie ma nic wspólnego z jakąkolwiek inną część funkcji.

Teraz idealnie chcę wyjście wyglądać następująco:

(Right 100, 1, "accessed memory", fromList [...]) 

lub ogólnie:

(output of errorT, output of stateT Counter, output of writerT, output of StateT Memory) 

Ale nie mogę zmusić go do pracy. Konkretnie Próbowałem uruchomiony stos jakby Błąd znajduje się na najbardziej zewnętrznej warstwie:

mem1 = M.fromList [("x",10),("y",5)] 
runIdentity $ runWriterT (runStateT (runStateT (runErrorT mStack) 0) mem1) "" 

Ale otrzymuję komunikat o błędzie:

Couldn't match type `Int' with `Map [Char] Int' 

Powyższy przykład na bok, w ogóle, kiedy apeluję:

runMonadT_1 (runMonadT_2 expr param2) param1,

są funkcje związane z monadT_2 uruchom najpierw, a następnie to wyjście jest podłączone do funkcji związanych z monadT_1? Innymi słowy, tak imperatywny jak kod wygląda w powyższej funkcji mStack, kolejność wykonywania jest całkowicie zależna od kolejności, w jakiej monadT są uruchamiane (oprócz sztywności w strukturze wprowadzonej przez lift)?

Odpowiedz

6

Można by zdobyć typu błędu bardziej pouczające jeśli próbowali wpisać obliczeń za pomocą wyraźnego transformatora monada stos:

mStack :: ErrorT String (StateT (Map String Int) (StateT Int Writer)) Int 

Gdybyś zrobił, ghc byłby złapany błąd typ wcześniej. Powodem jest to, że należy wykonać następujące dwa polecenia w ciągu mStack na najwyższym, poziomie:

modify (+1) -- i.e. from `tick` 
... 
yourMap <- lift get 

Jeśli było to dać wyraźny stos, to by złapać błąd: zarówno modify i lift get idą celować w pierwszą napotkaną warstwę StateT, która ma tę samą warstwę StateT.

modify rozpoczyna się od warstwy ErrorT i przechodzi w dół, dopóki nie natrafi na zewnętrzny StateT warstwę i stwierdza, że ​​zewnętrzna StateT muszą stosować stanu Int. get rozpoczyna się od zewnętrznej warstwy , zauważa, że ​​jest już w warstwie StateT i całkowicie ignoruje wewnętrzną warstwę StateT, więc konkluduje, że zewnętrzna warstwa StateT musi przechowywać Map.

ghc następnie mówi: "Co daje?Ta warstwa nie może przechowywać ani Int ani Map! ", Co wyjaśnia otrzymany błąd typu, ale ponieważ użyłeś klas typów zamiast konkretnego stosu transformatora monady, nie było możliwości, aby ghc mógł wiedzieć, że to było błąd wpisz czekając aż określony konkretny stos

rozwiązanie jest proste:.. wystarczy dodać kolejny lift do get i będzie teraz kierować wewnętrzną StateT warstwę jak zamierzałeś

ja osobiście wolę, aby uniknąć mtl klasy całkowicie i zawsze pracują z konkretnym stosem transformatora monad przy użyciu samej biblioteki transformers. erbose, ponieważ musisz precyzyjnie określać, która warstwa ma być używana, aby uzyskać lift, ale powoduje to mniej bóle głowy w dół drogi.

Powiązane problemy