10

私がmonadTを持っているとしましょう:

type Wrap a = ReaderT Env ( StateT Int ( StateT Int Identity ) ) a

ここで注意すべき重要なことは、1つのStateTが別のStateTをラップしており、両方が3番目のMonadT、つまりReaderT内にラップされていることです。

便宜上、対応するrunWrap関数:

type Env = Map.Map Char Integer

runWrap :: Env -> Int -> Int -> Wrap a -> a
runWrap env st1 st2 m = runIdentity $ evalStateT ( evalStateT ( runReaderT m env ) st2 ) st1

そして、一般的なトック状態モナド:

tock :: (Num s, MonadState s m) => m ()
tock = do modify (+1)

ここで、内部でtockを使用するラップmonadTを作成します。

aWrap :: Wrap ( Int, Int )
aWrap = do
    lift tock
    lift . lift $ tock
    x    <- get
    y    <- lift . lift $ get
    return ( x, y )

そしてそれを実行します:

env = Map.fromList [('x', 1)]
runWrap env 1 200 aWrap
// answer: (201,2)

ここでの使用はlift、MonadTのネストされたレイヤーと相互作用する方法を理解しているという点で私には理にかなっています。

ただし、これも機能し、同じ答えが得られます(201,2)::

aWrap :: Wrap ( Int, Int )
aWrap = do
    tock
    lift . lift $ tock
    x    <- get
    y    <- lift . lift $ get
    return ( x, y )

tockw / oliftと呼ぶとtock、外側のMonadT、つまりReaderTに適用されているように見えますが、これは意味がありません。しかし、なぜこれが機能するのでしょうか?

PSEnvここの存在を無視してください、それは質問とは何の関係もありません、ただ私が使用している外側のMonadTの選択だけです。

4

1 に答える 1

9

あなたはおそらくMonadStateそれを意識せずに型クラスを使用しています。この型クラスはパッケージで定義されていmtlます(そしてmonads-fd、でも定義されています)。

MonadStateStateに基づく多くのモナドスタックで、明示的なリフティングなしで、モナドのメソッドを直接使用できますState

ハドックの次の2行を見てください。

Monad m => MonadState s (StateT s m)
MonadState s m => MonadState s (ReaderT r m)

最初のものは、anyStateTがのインスタンスであると言っていますMonadState(私たちが期待するはずです!)。2つ目はReaderT、ベースモナドがのインスタンスであるものはすべてMonadState、のインスタンスでもあることを示していますMonadState。たまたまあなたの場合です。

のソースコードを見るとMonadState、次のことがわかります。

instance MonadState s m => MonadState s (ReaderT r m) where
    get = lift get
    put = lift . put
    state = lift . state

modify :: MonadState s m => (s -> s) -> m ()
modify f = state (\s -> ((), f s))

ご覧のとおり、型クラスの内部機構がリフティングを処理します。

MonadReader、、など、同様の機能を提供する他の型クラスがありMonadWriterますMonadRWS

于 2013-03-25T22:46:11.530 に答える