5

問題はこれです。私は持っている:

f :: MonadIO m => ReaderT FooBar m Answer;
f = (liftIO getArgs) >>= ...

引数を変更してこれを実行する必要があります。ただ、mが不明なので単純には使えません

mapReaderT (withArgs args) :: ReaderT r IO b -> ReaderT r IO b

すべての m に対して (withArgs args) を m に変換する必要があるためです。

私が見つけた 1 つの可能性は、次のように、独自の withArgs を定義することです。

import System.Environment (setArgs, freeArgv);
withArgv new_args act = do {
  pName <- liftIO System.Environment.getProgName;
  existing_args <- liftIO System.Environment.getArgs;
  bracket (liftIO $ setArgs new_args)
          (\argv -> do {
                      _ <- liftIO $ setArgs (pName:existing_args);
                      liftIO $ freeArgv argv;
                    })
          (const act);
};

withArgs xs act = do {
  p <- liftIO System.Environment.getProgName;
  withArgv (p:xs) act;
};

ただし、これは面倒で、1 つの関数に固有のものですwithX :: X -> IO a -> IO a

もしあれば、これを行うためのより良い方法は何ですか?

編集: ハンドルの場合、Control.Monad.CatchIO が見つかりました。他のケースでは、上記のクラッジを避けるために、さらに別の短いクラッジ (投稿する価値はありません) を使用しました。まだより良い解決策を探しています!

4

4 に答える 4

9

あなたが探しているものの一部は、モナド準同型をモナド変換子に巻き上げることです。

class MonadHoist t where
    hoist :: (Monad m, Monad n) => (forall a. m a -> n a) -> t m a -> t n a

    t :: Monad m => t Identity a -> t m a
    t = hoist (return . runIdentity)

つまり、からへのモナド準同型が与えられると、fホイストmを使用してからへnのモナド準同型を得ることができます。t mt n

モナド準同型は、上記のタイプよりもわずかに強力です。つまり、モナドの法則を維持する責任があります。

f . return = return
f . fmap g = fmap g . f
f . join = join . f . fmap f
         = join . fmap f . f -- by the second law
         = (>>= f) . f       -- >>= in terms of join

hoistのタイプにこっそり入った数量詞に注意してくださいMonadHoist。ほとんどすべてのインスタンスでその柔軟性が必要であることがわかりました。(Readerたまたまそうでない場合が1つあります。それなしでMaybeTを書いてみてください。)

モナド変換子は、一般に、このクラスをインスタンス化できます。例えば:

instance MonadHoist (StateT s) where
    hoist f (StateT m) = StateT (f . m)

instance MonadHoist (ReaderT e) where
    hoist f (ReaderT m) = ReaderT (f . m)

instance MonadHoist MaybeT where
    hoist f (MaybeT m) = MaybeT (f m)

が必要になるため、現在transformers、パッケージで提供していませんが、実装は非常に簡単です。mtlRank2Type

十分な需要があれば、喜んでパッケージにまとめmonad-extrasます。

さて、これはあなたの投稿のトピックのタイプによって与えられた質問に答えますが、あなたの質問に関連するテキストの大部分によって反映される必要性に対処しないので、私は一部を言いました!

そのためには、おそらくルキのアドバイスに従いたいと思うでしょう。=)

于 2011-07-06T21:03:18.820 に答える
4

monad -controlパッケージがこれを行います。Control.Monad.IO.ControlliftIOOp_の関数が必要だと思います。

具体的には、

liftIOOp_ (withArgs newArgs) f

あなたがやりたいことをする必要があります。bracketこの機能を使えば、同じようなものを持ち上げることもできliftIOOpます。

于 2011-07-06T20:33:49.737 に答える
4

interleavableIO パッケージがこの問題に対処していると思います。このカフェスレッドで議論されています。

于 2011-07-06T19:01:08.517 に答える
0

runReaderT必要な効果を得るためにも使用できるようです。

*> :t withArgs [] (runReaderT f FooBar)
withArgs [] (runReaderT f FooBar) :: IO Answer

ここで、FooBarはデータコンストラクターでありf、上記のように定義されています。

于 2011-07-06T21:48:35.217 に答える