2013-04-30 76 views
6

一般主題:雖然我發現將monad堆疊在一起的想法非常有吸引力,但我在繪製代碼的執行方式方面遇到很多麻煩,以及運行這些圖層的適當命令是什麼。下面是一個堆棧的例子:Writer,State,State和Error,沒有特定的順序(或者是否存在?)。您如何推斷monadT堆棧中函數的執行順序?

----------------------- 
-- 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" 

請注意mStack,是否有錯誤被拋出與否無關的功能的任何其他部分。

現在非常想輸出看起來像這樣:

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

或一般:

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

但我無法得到它的工作。具體地講,我試圖運行堆棧彷彿錯誤是在最外層上:

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

但正在此錯誤消息:

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

上述實例之外,在一般情況下,當我我打電話:

runMonadT_1 (runMonadT_2 expr param2) param1

是與01的功能先運行,然後將輸出傳送到與monadT_1相關的功能?換句話說,與代碼在上面的函數mStack中看起來一樣,執行的順序完全取決於monadT的運行順序(除了由lift引入的任何剛性結構)?

回答

6

你會得到更翔實的類型的錯誤,如果你曾試圖使用一個明確的單子轉換堆棧鍵入您的計算:

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

假如你這樣做,ghc會錯誤類型更早捕獲。其原因是,你在最頂層使用以下兩條命令中mStack

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

如果你要這給一個明確的堆棧,那麼你會趕上的錯誤:既modifylift get會瞄準他們遇到的第一層StateT層,碰巧是相同的StateT層。

modifyErrorT層開始,直到它碰到外StateT層向下前進,並得出結論認爲外StateT必須使用一個Int狀態。 get從外層StateT層開始,注意到它已經在StateT層中,並且完全忽略了內層StateT層,因此它得出結論外層StateT層必須存儲Map

ghc然後說:「什麼給?這層不能同時存儲IntMap!「,這說明了你得到的類型錯誤。但是,因爲你使用類型類而不是具體的monad變量堆棧,所以ghc不可能知道這是直到你指定一個具體層疊等待類型的錯誤

解決方案很簡單:。只需添加另一個liftget,現在將針對內StateT層像你預期

我個人更喜歡以避免mtl完全可以使用transformers函數庫,並且一直使用具體的monad變壓器堆棧。因爲你必須準確地使用lift來確定你想要的圖層,但是它會減少頭痛。