Sav*_*nel 5 monads haskell monad-transformers
我试图遵循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])
Run Code Online (Sandbox Code Playgroud)
有一部分我没有得到.它困扰了我很大的,我有script
和myMain
和 main
.我还要困扰我,我必须在runState
内部执行,myMain
并且我必须runStateT
在我的主函数中输入初始状态.我想要直接在myMain函数中使用我的"脚本",因为myMain的整个点是能够直接在myMain中运行追加和求和,然后在打印操作旁边.我想我应该能够做到这一点,相反:
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
Run Code Online (Sandbox Code Playgroud)
我曾经认为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
Run Code Online (Sandbox Code Playgroud)
现在,除了丹尼尔之外,还有append和sumST的各种实现:
append :: Integer -> AppState ()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: AppState Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
Run Code Online (Sandbox Code Playgroud)
并且(请注意,只有类型声明会更改;实际上您可以完全省略类型声明!)
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)
Run Code Online (Sandbox Code Playgroud)
它发生,我认为届时AppState/StateT单子是不一样的国家基本单子,我是既编码和sumST追加为国家单子.从某种意义上说,他们也必须被提升到StateT monad,尽管正确的思考方式是他们必须在monad中运行(因此runState script new
).
我不确定我是否完全得到它,但我会使用它一段时间,阅读MonadState代码,并在它最终在我脑海中起作用时写下这个.
Dan*_*ner 10
问题是,你做你的append
和sumST
功能太单态!state
您应该使用更多的多态get
和put
函数,而不是直接使用函数,这样您就可以为它们提供更激动人心的类型
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)
Run Code Online (Sandbox Code Playgroud)
然后你可以准确写出myMain
你提出的建议(尽管你仍然必须给出一个初始状态main
).
作为一个风格的东西,我建议不要定义一个新ST
类型:有许多函数可以使用列表进行方便的操作,并且通过ST
在您和列表之间强加构造函数使它们无法使用它们会很烦人!如果您使用的[Integer]
是状态类型,则可以进行如下定义:
prepend :: MonadState [Integer] m => Integer -> m ()
prepend = modify . (:)
sumST :: MonadState [Integer] m => m Integer
sumST = gets sum
Run Code Online (Sandbox Code Playgroud)
看起来很漂亮,不是吗?=)