2012-08-07 11 views
21

これにはzipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r')を使用しましたが、廃止予定です。2つのシンクを組み合わせる好ましい方法は何ですか?

+0

"*結合*"のシンクにはどのような振る舞いがありますか?私は古い文書と 'zipSinks'の実装を見てみましたが、一見すると容易に識別できない動作です。 –

+0

@DanBurton: 'zipSinks'は2つのシンクを取り、対応するシンクの結果とペアを生成するシンクを返します。たとえば、 'sizeCrc32Sink = zipSinks sizeSink crc32Sink'はサイズとチェックサムをカウントします。私はOleg [ここ](http://okmij.org/ftp/Streams.html#1enum2iter)で記述されているのと同じ動作です。 – tymmym

+0

わかりました。それは基本的に待ち時間をフックアップし、アップストリーム出力を両方のシンクに同時に供給し、入力ストリームを2つに分岐します。 Data.Conduit.Utilのドキュメントでは、「ユースケースを扱うためのより簡単な方法があります」と述べていますが、コンジット内部の実装に掘り下げる必要があるため、このユースケースでは簡単な方法はありません。 –

答えて

6

((パッケージがconduit-0.5.2.3ある。module全体がちょうど後方互換性のためである。))


[編集]

だから、私の単純なモナドの推測(下記参照)型が正しいにもかかわらず、が間違っていると思われます()。 答えは:

交換機能はまだ開発中です。すべてのパイプ/コンジットおよび同様の概念とライブラリによく似ています。

私は次のAPIがこの問題を解決するまで待っていて、それまではzipSinkを使用しています。 (多分それは単に見当違いました。)

[/編集]

私はこのパッケージとその慣れないんだけど、それはこのようにちょうど同じことをしないだろうか?結局のところMonadです。

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = (,) <$> s1 <*> s2 

結局のところ、Monadです。 (FunctorApplicative

zipSinks :: Monad sink => sink r -> sink r' -> sink (r, r') 
zipSinks s1 s2 = liftM2 (,) s1 s2 
+2

タイプは大丈夫ですが、セマンティクスはありません。あなたのバージョンの 'zipSinks'はシンクを連続して実行し、最初のシンクは完全にソースを消費します。 – tymmym

7

編集

これを検討した後、私はそれがData.Conduitの現在のバージョンでは可能ではないと思います。パイプはカテゴリではないので、&&&は問題になりません。上流からの結果を取り込み、両方のシンクに徐々にフィードし、最初のシンクが終了したときに短絡する方法はありません。 (私はData.Conduit.Util.zipSinksがこのように短絡しているとは思わないが、それは非常に望ましいと思われる)。もちろん、両方のシンク(例えばパッケージ内のzipSinksのように)を一致させるためには、ここで回避しようとしています。

私はと言っています。はここで間違っていると言います。


これはかなりわかりませんが、これは一種の明らかな方法で行うことができます。

まず輸入:zipSinksのための今

module Main where 

import Control.Monad.Trans 
import Data.Conduit 
import qualified Data.Conduit.Binary as CB 
import qualified Data.Conduit.List as CL 
import qualified Data.Conduit.Text as CT 
import qualified Data.Conduit.Util as CU 
import Data.Maybe 
import Data.Text (unpack) 

。基本的には、アップストリームから入力をプルするシンクを作成し、それを別々に各子シンクに送信する必要があります。この場合、私はCL.sourceListを使ってこれを行いました。 awaitNothingを返す場合、maybeToListは空のリストを返します。したがって、子シンクも入力なしで実行されます。最後に、各子シンクの出力がタプルに供給されます。

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = do 
    l <- fmap maybeToList await 
    o1 <- lift $ CL.sourceList l $$ s1 
    o2 <- lift $ CL.sourceList l $$ s2 
    return (o1, o2) 

zipSinksの使用例を次に示します。 IOとその外側の両方で正常に動作するように見えますが、いくつかのテストでは、出力はzipSinksを使用して作成されたzipped'の出力と一致しています。

doubleHead :: Monad m => Sink Int m (Maybe Int) 
doubleHead = await >>= return . fmap (2*) 

-- old version 
zipped' :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped' = CU.zipSinks CL.head doubleHead 

-- new version 
zipped :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped = zipSinks CL.head doubleHead 

fromList = CL.sourceList [7, 8, 9] $$ zipped 
-- (Just 7, Just 14) 

fromFile :: String -> IO (Maybe Int, Maybe Int) 
fromFile filename = runResourceT $ 
     CB.sourceFile filename 
    $= CB.lines 
    $= CT.decode CT.utf8 
    $= CL.map (read . unpack) 
    $$ zipped 

-- for a file with the lines: 
-- 
-- 1 
-- 2 
-- 3 
-- 
-- returns (Just 1, Just 2) 
+0

ニース! (注: 'zipSinks'で' input'を使うのではなく、 'doubleHead'には' await >> = return。fmap(2 *) 'を、同様に' l- fmap maybeToList await'を書くこともできます。 – huon

+0

ええ、私はおそらく、いくつかの場所でファンクターを使用している可能性があることに気づきました。私は残念ながら、それは通常はセカンドパスの編集であると私はまだハスケルでn00bの十分です。 はい、 'Data.Conduits.Internals'は無関係です。もともと、私はそれから 'sinkToPipe'を使うことを考えていました。 これらを指摘していただきありがとうございます。私は答えを更新します。 – Eric

+2

あなたの 'zipSinks'のバージョンは、最初の要素だけを組み合わせています。たとえば、 'runResourceT $ CL.sourceList [1,2,3] $$ zipSinks(CL.take 2)(CL.take 2)'は '([1]、[1])'を返しますが、 '([1 、2]、[1,2])。 – tymmym

関連する問題