2015-11-07 74 views
2

我正在寫一個增長很大的管道,並帶有嵌套monad變換。將lift的每個yieldawait調用到基地conduitM是一項繁瑣的工作。更不用說每次添加或撤消一層轉換時,我需要在每個可能的位置更改lift的數量。管道是否有'liftIO`等價物?

我一直在尋找類似的功能liftIO,但不是提升了IO操作應該解除yieldawait到基於ConduitM任意變換單子,但我似乎無法找到一個。有沒有辦法實現這樣的事情?


編輯:響應@ BradleyHardy的答案,在這裏我提供一個具體的例子:

{-# LANGUAGE LambdaCase #-} 

import Control.Monad     as MON 
import Control.Monad.IO.Class   as MIO 
import Control.Monad.Trans.Class  as MTC 
import Control.Monad.Trans.Maybe  as MTM 
import Data.Conduit      as CDT 
import System.IO      as IO 

stdinS :: Source IO String 
stdinS = void . runMaybeT . forever $ do 
    (liftIO isEOF) >>= \case 
     True -> mzero 
     False -> (lift . yield) =<< (liftIO getLine) 

myK :: Sink String IO() 
myK = void . runMaybeT . forever . runMaybeT $ do 
    a <- maybe (lift mzero) return =<< (lift . lift $ await) 
    b <- if a == "listen" 
     then maybe (lift mzero) return =<< (lift . lift $ await) 
     else mzero 
    liftIO . putStrLn $ "I heard: " ++ b 

main :: IO() 
main = do 
    stdinS $$ myK 

你會如何改變myKConduitM在堆棧的頂部?不可否認,在這個特殊的例子中,使用MaybeT是過於複雜的,但是在我的實際(更大的)導管MaybeT中,結構比例如遞歸。

+2

'ConduitM m'是'MonadIO'的一個實例,如果'MonadIO m',那麼你就有'liftIO :: MonadIO m => IO a - > ConduitM i o m a'。 'ConduitM'也有所有MTL類的實例(MonadReader/Writer等),因爲'ConduitM'是一個monad變換器,所以你不必寫'lift ask :: ConduitM io(Reader r)r'(例如),你可以做'ask :: ConduitM io(Reader r)r'。 – user2407038

+0

@ user2407038:謝謝你的回答,但我不太確定我應該怎樣處理'yield'和'await'。 – trVoldemort

+0

你應該做的屈服和等待似乎與解除管道中的IO行爲有什麼關係?也許你應該包括一個你想達到的具體例子。如果你的意思是類似於MonadConduit類(類似於MonadReader/Writer/etc)的類,這個不存在,但是'pipes'解決了這個問題,並且基本上和'Conduit'完全相同(參見[Control.Proxy。 Trans](http://hackage.haskell.org/package/pipes-3.2.0/docs/Control-Proxy-Trans.html)) – user2407038

回答

0

我認爲更好的方法是使用包含的Data.Conduit.Lift模塊。這與將所有runMaybeTrunStateT等替換爲runMaybeCrunStateC一樣簡單。瞧,ConduitM被推到變壓器堆棧的頂部。

3

ConduitM本身一個單子轉換,而且,看Haddock page它,我們可以看到,它定義實例以MonadStateMonadReader等,這應該表明,其實意圖模式是有ConduitM在變壓器堆棧的頂部,在這種情況下,您不需要將任何操作放入其中。

實際上,它甚至爲MonadBase定義了一個實例,它允許您從位於堆棧底部的monad提起操作(使用liftBase函數),以便重新排序堆棧意味着它很難要訪問新的東西在底部,MonadBase爲你解決這個問題。不過,如果您的底部有IO,這可能不是很有幫助。

如果可能的話,我建議您嘗試重新排序變壓器堆棧,以便將ConduitM置於頂端。

編輯:另一種選擇是創建自己的類,MonadConduit,它定義廣義yieldawait功能,並添加實例爲ConduitM,你在它上面使用的所有其他變壓器。如果可能的話,我認爲這不像重新排序堆棧那麼優雅。

+0

感謝您的回答。不幸的是,我無法看到將'ConduitM'放在應用程序頂部的方法。我正在編寫的管道等待並在每次迭代中產生不可預測數量的項目,「await」和「yield」深埋在複雜嵌套的if和case中。 – trVoldemort

+0

至於'liftBase',我注意到'conduitM'不是*自己的基礎:'MonadBase base m => MonadBase base(ConduitM i o m)'。這意味着該基地將是'IO' - 而不是*'conduitM ...' - 如果我的'conduitM'基於'IO'-monad。 – trVoldemort

+0

@trVoldemort你的堆棧中有什麼圖層?有沒有像'ListT'或'LogicT'這樣的東西,如果交換時可能會有不同的行爲?如果是這樣,我認爲你可以重新考慮你的程序結構。 至於'MonadBase'的東西,是的,你是對的,但這不是我的意思。爲了清晰起見,我會編輯我的答案。 –

相關問題