问题是这个。我有:
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。
我发现的一种可能性是定义我自己的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;
};
然而,这是一个kludge,并且特定于一个功能 - 我需要重新编写每个withX :: X -> IO a -> IO a
,例如Control.Exception.handle
如果有的话,更好的方法是什么?
编辑:在句柄的情况下,我找到了Control.Monad.CatchIO。在另一种情况下,我使用了另一个更简洁的kludge(不值得发布)以避免上面的kludge。仍在寻求更好的解决方案!
答案 0 :(得分: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)
也就是说,给定从f
到m
的monad同态n
,您可以使用升降机从t m
到t n
获得monad同态
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
请注意我隐藏在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)
我们目前不在transformers
或mtl
包中提供,因为它需要Rank2Type
,但实施起来非常简单。
如果有足够的需求,我很乐意将其打包成monad-extras
个包裹。
现在,我说了一部分,因为虽然这回答了帖子主题中类型给出的问题,但它并没有解决与你的问题相关的大部分文本所反映的需求!
为此,您可能想要遵循luqui的建议。 =)
答案 1 :(得分:4)
我相信interleavableIO package解决了这个问题。它在this cafe thread中进行了讨论。
答案 2 :(得分:4)
monad-control包将执行此操作。我想你想要来自Control.Monad.IO.Control的函数liftIOOp_
。
具体地,
liftIOOp_ (withArgs newArgs) f
应该做你想做的事。您也可以使用bracket
功能解除liftIOOp
之类的内容。
答案 3 :(得分:0)
似乎你可以使用runReaderT
来获得你想要的效果:
*> :t withArgs [] (runReaderT f FooBar)
withArgs [] (runReaderT f FooBar) :: IO Answer
其中FooBar
是一些数据构造函数,f
定义如上。