2011-07-06 64 views
5

問題是這樣的。我有:mapMonadTrans :: MonadTrans xT =>(m a - > n b) - > xT m a - > xT n b

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

我需要運行修改參數。然而,由於m是未知的,我不能簡單地用

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

,因爲我需要以某種方式改變(withArgs參數)爲m的所有的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; 
}; 

然而,這是一個雜牌,而具體到一個功能 - 我需要重新寫每withX :: X -> IO a -> IO a,例如Control.Exception.handle

什麼,如果有的話,是一個更好的方法來做到這一點?

編輯:在處理的情況下,我發現了Control.Monad.CatchIO。在另一種情況下,我使用了另一個簡短的kludge(不值得發佈)來避免上面的kludge。仍在尋求更好的解決方案!

+0

如果您卸下'F'類型簽名是什麼?我想知道是否限制'MonadIO'的限制太多了。 –

+0

我需要在'f'中做I/O。否則,這將是盛大的。 (事實上​​,我有在它的功能的數據類型的,以產生類型B的某個值,並且功能必須足夠一般使得類型的一些值可以做I/O得到灣) –

+0

@strake :請注意,Control.Monad.CatchIO存在問題。也就是說,如果您使用的是短路單聲道變壓器(例如ErrorT),則可能無法按照您的預期運行。無論是設計缺陷還是誤用,都有待解釋,但您應該意識到這一點。有關詳細信息,請參閱http://andersk.mit.edu/haskell/monad-peel/。 –

回答

4

monad-control包將執行此操作。我想你想要的功能liftIOOp_Control.Monad.IO.Control

具體來說,

liftIOOp_ (withArgs newArgs) f 

應該做你想要什麼。您也可以使用liftIOOp函數舉起類似bracket的東西。

+0

王牌!正是我需要的。謝謝! –

4

我相信interleavableIO package解決了這個問題。它在this cafe thread中討論。

+0

它可能,但不幸的是,它非常令人困惑。它是如何使用的? –

+0

@strake,不幸的是,我從來沒有用過它,我只記得它正在討論。也許另一個SO問題是爲了? – luqui

8

你正在尋找的一部分是monad homomorphism到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) 

也就是說,給出一個單子同態fmn,您可以用葫蘆獲得t m單子同態t 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恰好是它沒有的情況下,試着寫沒有它的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) 

我們目前不提供它transformersmtl包,因爲它需要一個Rank2Type,但它是非常簡單的實現。

如果它足夠的需求,我會高興地在monad-extras套餐包起來。現在

,我說的部分,因爲雖然這個回答您的文章的主題由類型給定的問題,它沒有解決由大與你的問題相關聯的文本反映的需要!

對於這一點,你可能要遵循luqui的建議。 =)

+0

好主意。其實(正如我剛纔注意到的),其他人意識到了這一點:http://hackage.haskell.org/package/mmtl –

0

看來你可以使用runReaderT得到你想要的效果,以及:

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

其中FooBar是一些數據的構造和f如上述定義。

相關問題