2012-07-06 12 views
5

私はCombine state with IO actionsのアドバイスに従って、IOモナドと共にAppStateを構築しようとしています。私が得たことは次のとおりです。IOと一緒にStateTモナドを実際にどのように実行するのですか?

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

私が得意ではない部分があります。それは私が大いに悩まされているのです。scriptmyMainmainです。また、私はrunStatemyMainの中で実行しなければならないこと、そして私の最初の状態を私の主な機能のrunStateTに供給しなければならないことが気になります。 myMainのポイント全体がmyMainとappendを直接実行することができるので、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 

私はモナド変換子のポイントはので、私は関数の中で私のStateモナド操作を実行する(上記のような)とにIO操作を持ち上げることができたと思っていた:私は、私は代わりに、これを行うことができるはずだと思いますその機能。間接指示のレイヤーの1つを削除できるように、これをすべて設定する正しい方法は何ですか? (私は解決策をフラグが付けられている)ダニエルのソリューションに加えて


、私はまた、状況にいくつかの光を当てるかもしれないいくつかのバリエーションを発見しました。まず、最終myMainの実装とメイン:今すぐ

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 

、ダニエルのに加えて、追記や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とappendの両方をコーディングしていました。ある意味では、彼らはStateTモナドにも持ち込まなければなりませんでしたが、正しい考え方は、をモナドで実行する必要があったということです(したがって、runState script new)。

私はそれを完全に得ることはできませんが、私はしばらくそれに取り組み、MonadStateコードを読んで、最終的に私の頭の中でこれについて何か書きます。

+0

ああ、 'state'は私が想定していたより多相です。これは私の頭の中に何らかの理由で' 'State'コンストラクタをそれ以上エクスポートしないという謝罪であったからです。 TIL! –

答えて

10

問題は、appendsumSTの機能が単調すぎることです。代わりに、直接state機能を使用して、あなたはあなた'LLのに(あなたがそれらをよりエキサイティングな種類

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を書くことができ、より多くの多型getput機能を使用する必要がありますまだmainに初期状態を与えなければなりません)。

私は、新しいSTタイプを定義しないことを提案します:リストと便利なことをする関数がたくさんあり、リストとの間にSTコンストラクタを置くことによってそれらを使用不可能にすることができます迷惑な!あなたの代わりにあなたの状態の種類として[Integer]を使用する場合は、このような定義を行うことができます。

prepend :: MonadState [Integer] m => Integer -> m() 
prepend = modify . (:) 

sumST :: MonadState [Integer] m => m Integer 
sumST = gets sum 

はありません、かなり良さそうに見えますか? =)

+0

実際には、これをより複雑なデータ型の簡素化と考えると、appendとsumSTをコーディングすることは意味があります。 Like(私の実際のアプリケーションで)ST {datastore :: MyData、event_stream :: [Events]} –

関連する問題