![](/img/trans.png)
[英]How do you use the StateT Monad Transformers in Haskell?
[英]How do I actually execute a StateT monad along with IO?
我試圖遵循Combine狀態中給出的建議和IO動作來構建AppState以及IO monad。 我得到的是這個:
module Main where
import Control.Monad.State
import Control.Monad.Trans
data ST = ST [Integer] deriving (Show)
type AppState = StateT ST IO
new = ST []
append :: Integer -> State ST ()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: State ST Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
script = do
append 5
append 10
append 15
sumST
myMain :: AppState ()
myMain = do
liftIO $ putStrLn "myMain start"
let (res, st) = runState script new
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runStateT myMain (ST [15])
有一部分我沒有得到。 我有很多麻煩,我有script
和myMain
和 main
。 我還困擾我,我必須在myMain
執行runState
,並且我必須在我的main函數runStateT
初始狀態提供給runStateT
。 我想要直接在myMain函數中使用我的“腳本”,因為myMain的整個點是能夠直接在myMain中運行append和sum,並且緊跟打印操作。 我想我應該能夠做到這一點,相反:
myMain :: AppState ()
myMain = do
liftIO $ putStrLn "myMain start"
append 5
append 10
append 15
r <- sumST
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runState myMain
我曾經認為monad變換器的重點在於我可以在函數中執行狀態monad操作(如上所述)並將IO操作提升到該函數中。 設置所有這些的正確方法是什么,以便我可以刪除其中一個間接層?
除了Daniel的解決方案(我已經標記了解決方案)之外,我還發現了一些可能會對情況有所了解的變化。 首先,myMain和main的最終實現:
myMain :: AppState ()
myMain = do
liftIO $ putStrLn "myMain start"
append 5
append 10
append 15
res <- sumST
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runStateT myMain new
現在,除了丹尼爾之外,還有append和sumST的各種實現:
append :: Integer -> AppState ()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: AppState Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
並且(請注意,只有類型聲明會更改;實際上您可以完全省略類型聲明!)
append :: MonadState ST m => Integer -> m ()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: MonadState ST m => m Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
它發生,我認為屆時AppState / StateT單子是不一樣的基本狀態單子,我是既編碼和sumST追加為國家單子。 從某種意義上說,它們也必須被提升到StateT monad中,盡管正確的思考方式是它們必須在monad中運行 (因此, runState script new
)。
我不確定我是否完全得到它,但我會使用它一段時間,閱讀MonadState代碼,並在它最終在我腦海中起作用時寫下這個。
問題是你的append
和sumST
函數太單態了! 你應該使用更多的多態get
和put
函數,而不是直接使用state
函數,這樣你就可以給它們更激動人心的類型
append :: MonadState ST m => Integer -> m ()
append v = do
ST lst <- get
put (ST (lst ++ [v]))
sumST :: MonadState ST m => m Integer
sumST = do
ST lst <- get
return (sum lst)
然后你可以寫出你提出的myMain
(雖然你仍然需要在main
給出一個初始狀態)。
作為一個風格的東西,我建議不要定義一個新的ST
類型:有很多函數用列表做方便的事情,並且通過在你和列表之間強加一個ST
構造函數使它們無法使用可能很煩人! 如果使用[Integer]
作為狀態類型,則可以進行如下定義:
prepend :: MonadState [Integer] m => Integer -> m ()
prepend = modify . (:)
sumST :: MonadState [Integer] m => m Integer
sumST = gets sum
看起來很漂亮,不是嗎? =)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.