Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the preferred way to combine two sinks?

Tags:

I've used zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') for this but it is considered deprecated.

like image 753
tymmym Avatar asked Aug 07 '12 11:08

tymmym


People also ask

Is it better to have two sinks or one?

Less Clutter A single sink vanity might provide a little more countertop space but you also increase the chance of cluttering the countertop. Having a double sink vanity will encourage you to tidy up your countertop better.

Are double sinks outdated kitchen?

Double-bowl sinks aren't generally used that way anymore, since dishwashers took over. It's also common to keep a rack in the second sink where washed items can air-dry; some double-bowl units come with an integrated cutting board that fits over one sink for extra work surface.

Why do people want 2 sinks in bathroom?

The most common reason to choose a double sink is that you need more space because you share the bathroom with another person. "Couples these days usually prefer the idea of two sinks for one simple reason," architect Thayer Hopkins told Houzz. "They lead busy lives and need access to the bathroom at the same time."


2 Answers

Edit

After considering this, I don't think it is possible with the current version of Data.Conduit. Pipes aren't Categories, so &&& is out of the question. And there's no way that I can think of to pull results from upstream, feed them incrementally to both sinks, and short-circuit when the first sink finishes. (Although I don't think that Data.Conduit.Util.zipSinks short-circuits this way, it seems like it would be very desirable.) Except of course, to pattern match on both Sinks (like zipSinks in the package does), but that's what we're trying to avoid here.

That said, I would love to be proven wrong here.


It's not pretty, but you can do this in a kind-of obvious way.

First imports:

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)

Now for zipSinks. Basically, you want to create a sink that pulls the input from upstream and sends it to each child sink separately. In this case, I've used CL.sourceList to do this. If await returns Nothing, maybeToList returns an empty list, so the child sinks are also run with no input. Finally, the output of each child sink is then fed into the tuple.

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)

Here are some examples of using zipSinks. It appears to work fine both inside of IO and outside of it, and in the few tests I did, the output matches the output of zipped', created using the old zipSinks.

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)
like image 111
Eric Avatar answered Sep 21 '22 07:09

Eric


((The package is conduit-0.5.2.3. The whole module is just for backwards compatibility.))


[edit]

So, my straightforward monadic guess (see below) seems to be wrong, even though the types are correct. Now, I can only guess that the answer is:

The replacing features are still in development, pretty much like all Pipe/Conduit and similar concepts and libraries.

I'd wait for the next API to solve this question and still use zipSink until then. (Maybe it was just misplaced.)

[/edit]

I'm not that familar with this package, but wouldn't it do just the same as this?

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

It is a Monad after all. (Functor, Applicative)

zipSinks :: Monad sink => sink r -> sink r' -> sink (r, r')
zipSinks s1 s2 = liftM2 (,) s1 s2
like image 24
comonad Avatar answered Sep 20 '22 07:09

comonad