Skip to content

Instantly share code, notes, and snippets.

@sjoerdvisscher
Created March 24, 2012 22:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sjoerdvisscher/2188862 to your computer and use it in GitHub Desktop.
Save sjoerdvisscher/2188862 to your computer and use it in GitHub Desktop.
Bijection between Twan van Laarhoven's Pipe and the data types from Conduit
-- http://www.reddit.com/r/haskell/comments/rbgvz/conduits_vs_pipes_using_void_as_an_input_or/
import Control.Monad
import Data.Void
data Pipe m i o r =
NeedInput (i -> Pipe m i o r) (Pipe m Void o r)
| HaveOutput (Pipe m i o r) (m r) o
| Finished (Maybe i) r
| PipeM (m (Pipe m i o r)) (m r)
close :: Monad m => Pipe m i o r -> m r
close (NeedInput _ a) = close a
close (HaveOutput _ a _) = a
close (Finished _ a) = return a
close (PipeM _ a) = a
convert :: Monad m => Pipe m i o r -> Pipe m Void o r
convert (NeedInput _ pipe) = pipe
convert (HaveOutput pipe mr o) = HaveOutput (convert pipe) mr o
convert (Finished _ r) = Finished Nothing r
convert (PipeM mc mr) = PipeM (liftM convert mc) mr
instance Monad m => Monad (Pipe m i o) where
return = Finished Nothing
m >>= f = case m of
Finished _ r -> f r
PipeM mc mr -> PipeM (liftM (>>= f) mc) (mr >>= close . f)
NeedInput fc pipe -> NeedInput ((>>= f) . fc) (pipe >>= convert . f)
HaveOutput pipe mr o -> HaveOutput (pipe >>= f) (mr >>= close . f) o
{-----------------------------------------------------------------------------
Sink
------------------------------------------------------------------------------}
data Sink i m o =
Processing (i -> Sink i m o) (SinkClose m o)
| Done (Maybe i) o
| SinkM (m (Sink i m o))
type SinkClose m o = m o
toSink :: Monad m => Pipe m i Void r -> Sink i m r
toSink (NeedInput a b) = Processing (toSink . a) (close b)
toSink (HaveOutput _ _ v) = absurd v
toSink (Finished a b) = Done a b
toSink (PipeM a _) = SinkM (toSink `liftM` a)
fromSink :: Monad m => Sink i m r -> Pipe m i Void r
fromSink (Processing a b) = NeedInput (fromSink . a) (PipeM (Finished Nothing `liftM` b) b)
fromSink (Done a b) = Finished a b
fromSink (SinkM a) = PipeM (fromSink `liftM` a) (close . fromSink =<< a)
{-----------------------------------------------------------------------------
Conduit
------------------------------------------------------------------------------}
data Conduit i m o =
NeedInput' (i -> Conduit i m o) (ConduitClose m o)
| HaveOutput' (Conduit i m o) (m ()) o
| Finished' (Maybe i)
| ConduitM (m (Conduit i m o)) (m ())
type ConduitClose m o = Source m o
toConduit :: Monad m => Pipe m i o () -> Conduit i m o
toConduit (NeedInput a b) = NeedInput' (toConduit . a) (toSource b)
toConduit (HaveOutput a b c) = HaveOutput' (toConduit a) b c
toConduit (Finished a ()) = Finished' a
toConduit (PipeM a b) = ConduitM (toConduit `liftM` a) b
fromConduit :: Monad m => Conduit i m o -> Pipe m i o ()
fromConduit (NeedInput' a b) = NeedInput (fromConduit . a) (fromSource b)
fromConduit (HaveOutput' a b c) = HaveOutput (fromConduit a) b c
fromConduit (Finished' a) = Finished a ()
fromConduit (ConduitM a b) = PipeM (fromConduit `liftM` a) b
{-----------------------------------------------------------------------------
Source
------------------------------------------------------------------------------}
data Source m a =
Open (Source m a) (m ()) a
| Closed
| SourceM (m (Source m a)) (m ())
toSource :: Monad m => Pipe m Void a () -> Source m a
toSource (NeedInput _ a) = toSource a
toSource (HaveOutput a b c) = Open (toSource a) b c
toSource (Finished _ _) = Closed
toSource (PipeM a b) = SourceM (toSource `liftM` a) b
fromSource :: Monad m => Source m a -> Pipe m Void a ()
fromSource (Open a b c) = HaveOutput (fromSource a) b c
fromSource Closed = Finished Nothing ()
fromSource (SourceM a b) = PipeM (fromSource `liftM` a) b
@michaelt
Copy link

The Functor instance for Pipe i o m r is easy with these constructors, but I came to grief with Monad and Applicative, but maybe it's that I don't have enough of the needed intuition to know what to throw out. For example, the switch from m () as the second constructor in CondiutM and SinkM to m r in PipeM means that the function in =<< f needs to be used twice, so that you end up with two m (Pipe i o m r) s and don't know what to do with them. In Conduit you can just carry them over.

instance (Monad m) => Monad (Pipe m i o) where
  return = Finished Nothing
  m >>= f = case m of
    Finished ma r         ->  f r
    PipeM mc mr            ->  undefined  -- PipeM (liftM (>>= f) mc ) (mr >>= close . f) 
    NeedInput fc pipe      -> undefined   --       ... using your close function
    HaveOutput pipe mr o -> undefined

@sjoerdvisscher
Copy link
Author

I think using close is exactly right, because closing is what m r is for. I added your instance to the code.

@michaelt
Copy link

With that I was able to figure out the Applicative instance after the fashion of Control.Pipe.Common and a few other things. The 'Category' instance went through without much difficulty at all, though I left it and MonadTrans implicit. https://gist.github.com/2203876

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment