簡體   English   中英

我如何實際執行StateT monad和IO?

[英]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])

有一部分我沒有得到。 我有很多麻煩,我有scriptmyMain 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代碼,並在它最終在我腦海中起作用時寫下這個。

問題是你的appendsumST函數太單態了! 你應該使用更多的多態getput函數,而不是直接使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM