7

State モナド内にステートフルな関数がたくさんあります。プログラムのある時点で、いくつかの IO アクションが必要になるため、次のようなタイプのペアを取得する StateT 内に IO をラップしました。

mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a

物事を単純にするために、IO コンテキストを関数のメイン セットに渡したくありません。また、それらをモナド スタック型でラップすることは避けたいと思います。しかし、トップレベル関数からそれらを呼び出すには、リフトに似たものが必要ですが、内側のモナドから値を持ち上げようとしているわけではありません。むしろ、私は StateT モナドの状態を State モナドの同等のものに変換したいと考えています。これを行うために、私は次のものを持っています:

wrapST :: (State Sometype a) -> StateT Sometype IO a
wrapST f = do s <- get
              let (r,s2) = runState f s 
              put s2
              return r

これは、次のようなものをインターリーブするために使用されます。

toplevel = do liftIO $ Some IO functions
              wrapST $ Some state mutations
              liftIO $ More IO functions
              ....

かなり明白なコード ブロックのように思えるので、この関数には標準名があり、標準ライブラリのどこかに既に実装されているのだろうか? 説明を簡潔にしようとしましたが、明らかにこれは、スタックから 1 つのトランスフォーマーを取り出し、ラップされた値をトランスフォーマー型のいとこに変換し、スタック内で下のモナドをスキップし、結果を終わり。

4

2 に答える 2

9

最初の型は任意のモナド スタックと互換性があるため、 のStateT SomeType m a代わりに型を使用するようにコードをリファクタリングすることをお勧めします。State SomeType aこのように変更するwrapSTと、ステートフル関数を直接呼び出すことができるため、関数はもう必要ありません。

わかった。関数があるとしますsubOne :: Monad m => State Int Int:

subOne = do a <- get
            put $ a - 1
            return a

ここで、このようなすべての関数の型を から に変更State SomeType aStateT SomeType m a、そのままmにします。このようにして、関数は任意のモナド スタックで動作できます。IO を必要とする関数については、下部のモナドが IO でなければならないことを指定できます。

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

これで、両方の機能を一緒に使用できるはずです。

-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
            put $ a - 1

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

toZero :: StateT Int IO ()
toZero = do subOne     -- A really pure function
            printState -- function may perform IO
            a <- get
            when (a > 0) toZero

PS: GHC 7 を使用していますが、一部のライブラリが途中で変更されたため、GHC 6 では少し異なる場合があります。

于 2011-05-11T17:34:45.613 に答える