mapMonadTrans :: MonadTrans xT =>(ma - > nb) - > xT ma - > xT nb

M F*_*yck 5 monads haskell monad-transformers

问题是这个.我有:

f :: MonadIO m => ReaderT FooBar m Answer;
f = (liftIO getArgs) >>= ...
Run Code Online (Sandbox Code Playgroud)

我需要使用修改后的参数来运行它.但是,由于m是未知的,我不能简单地使用

mapReaderT (withArgs args) :: ReaderT r IO b -> ReaderT r IO b
Run Code Online (Sandbox Code Playgroud)

因为我需要以某种方式将所有m转换(withArgs args)为m.

我发现的一种可能性是定义我自己的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;
};
Run Code Online (Sandbox Code Playgroud)

但是,这是一个kludge,并且特定于一个函数 - 我需要重新编写每个函数withX :: X -> IO a -> IO a,例如Control.Exception.handle

如果有的话,更好的方法是什么?

编辑:在句柄的情况下,我找到了Control.Monad.CatchIO.在另一种情况下,我使用了另一个更简洁的kludge(不值得发布)以避免上面的kludge.仍在寻求更好的解决方案!

Edw*_*ETT 8

你正在寻找的部分原因是将monad同态提升到monad变换器中.

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)
Run Code Online (Sandbox Code Playgroud)

也就是说,给定一个单子同态f,从mn,可以从获得单子同态t mt n使用吊车.

monad同态略强于上述类型强制,即它负责保留monad定律.

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
Run Code Online (Sandbox Code Playgroud)

请注意,我在的类型悄悄量词hoist,MonadHoist原来需要这种灵活性对几乎所有的例子!(Reader恰好是它没有的情况.尝试在没有它的情况下编写MaybeT.)

一般来说,Monad变换器可以实例化这个类.例如:

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)
Run Code Online (Sandbox Code Playgroud)

我们目前不提供它transformersmtl包,因为它需要一个Rank2Type,但实现起来非常简单.

如果有足够的需求,我很乐意将其打包成一个monad-extras包.

现在,我说了一部分,因为虽然这回答了帖子主题中类型给出的问题,但它并没有解决与你的问题相关的大部分文本所反映的需求!

为此,您可能想要遵循luqui的建议.=)


Joh*_*n L 4

monad -control包可以做到这一点。我认为您想要Control.Monad.IO.ControlliftIOOp_的功能。

具体来说,

liftIOOp_ (withArgs newArgs) f
Run Code Online (Sandbox Code Playgroud)

应该做你想做的事。bracket您也可以使用该liftIOOp功能举起类似的东西。