0

計算があるとしましょう

class A  where
    foo ::  () ->  ()

instance A IO where
    foo x = do
        print "prefix"
        x
        print "suffix"

今、私が書きたいとしましょう

instance A  => A (MyMonadTransformerT γ )

次に、を実装するfoo際に、その引数を「ラップ解除」する必要がありますfoo x = lift (foo (unlift x))。このunlift関数は、モナド計算には適していません。状態トランスフォーマーの場合、プログラムの状態の変更を強制的に忘れることになります。

より一般的なメソッドを作成することはうまくいくようです。これはリフティング関数も取り、その結果は計算t () -> t ()になりますt

class Monad  => A'  where
    foo' :: Monad t =>
        (forall z .  z -> t z) -- lifting function
        -> t ()
        -> t ()
    foo ::  () ->  ()
    foo = foo' id

instance A' IO where
    foo' lift x = do
        lift (print "prefix")
        x
        lift (print "suffix")

instance A'  => A' (StateT γ ) where
    foo' lift' x = foo' (lift' . lift) x

computation :: Num a => StateT a IO ()
computation = do
    foo (put 1 >> lift (print "middle"))
    v <- get
    lift $ print ("value", v)

run_computation :: Num a => IO a
run_computation = execStateT computation 0

質問。これが最善の方法ですか?もっと一般的なものを書くことができますか?CPS スタイルのコード? ありがとう!!

4

1 に答える 1

2

まず第一に、そのclassビジネスを忘れてください。機能が必要なようです。

この問題は、 、 などのMonad*クラスによって対処されます。したがって、IO を実行できるが、他のことを実行できるモナド計算がある場合、IO アクションを実行できる任意のモナドを型パラメーターとして使用します。MonadIOMonadStatem

foo :: (MonadIO m) => m () -> m ()
foo x = do
    liftIO $ putStrLn "prefix"
    x
    liftIO $ putStrLn "suffix"

必要な操作に戻す方法を示しているmため、今は問題ではありません。MonadIO

クラスは、Monad*新しいトランスフォーマーに直面していくぶんモジュール化されていません。必要なインスタンスの数は、モナド トランスフォーマーの数の 2 乗になります。この問題には、次善の解決策がいくつかあります。そのようなことが気になる場合は、いつでもクラスを具体化できます。

foo :: (Monad m) => (forall a. IO a -> m a) -> m () -> m ()
foo lift x = do
    lift $ putStrLn "prefix"
    x
    lift $ putStrLn "suffix"

これを行うかどうかは、抽象化のレベルによって異なります。コンテンツ コードを構築するライブラリを作成している場合は前者が必要であり、他のライブラリ コードを構築するライブラリを作成している場合はおそらく後者が必要です。ただし、モナドスタックは交換されないため、どちらにしても少し注意が必要です。

于 2012-02-16T01:40:54.247 に答える