on master help sign in
Deleted conduit (now living in its own repo)
d4e94c0Michael
conduit/Control/Monad/Trans/Resource.hsdeleted−83
@@ -1,83 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−module Control.Monad.Trans.Resource
3− ( -- * Data types
4− ResourceT
5− , ReleaseKey
6− -- * Unwrap
7− , runResourceT
8− -- * Resource allocation
9− , with
10− , register
11− , release
12− ) where
13−
14−import Control.Monad.Trans.Reader (ReaderT, runReaderT, ask)
15−import Data.IntMap (IntMap)
16−import qualified Data.IntMap as IntMap
17−import Control.Exception.Lifted (try, finally, SomeException, mask, mask_)
18−import Control.Monad.Trans.Control (MonadBaseControl)
19−import qualified Data.IORef as I
20−import Control.Monad.Base (MonadBase, liftBase)
21−
22−data ReleaseMap m = ReleaseMap !Int !(IntMap (ResourceT m ()))
23−newtype ReleaseKey = ReleaseKey Int
24−
25−type ResourceT m = ReaderT (I.IORef (ReleaseMap m)) m
26−
27−with :: MonadBaseControl IO m
28− => ResourceT m a -- ^ allocate
29− -> (a -> ResourceT m ()) -- ^ free resource
30− -> ResourceT m (ReleaseKey, a)
31−with acquire rel = mask_ $ do
32− a <- acquire
33− key <- register $ rel a
34− return (key, a)
35−
36−modify :: MonadBase IO m => (ReleaseMap m -> (ReleaseMap m, a)) -> ResourceT m a
37−modify f = do
38− istate <- ask
39− liftBase $ I.atomicModifyIORef istate $ \a -> f a
40−
41−get :: MonadBase IO m => ResourceT m (ReleaseMap m)
42−get = ask >>= liftBase . I.readIORef
43−
44−register :: MonadBaseControl IO m
45− => ResourceT m ()
46− -> ResourceT m ReleaseKey
47−register rel = mask_ $
48− modify $ \(ReleaseMap key m) ->
49− ( ReleaseMap (key + 1) (IntMap.insert key rel m)
50− , ReleaseKey key
51− )
52−
53−release :: MonadBaseControl IO m
54− => ReleaseKey
55− -> ResourceT m ()
56−release (ReleaseKey key) = mask $ \restore -> do
57− ReleaseMap _ m <- get
58− case IntMap.lookup key m of
59− Nothing -> return () -- maybe we should throw an exception?
60− Just r -> modify (delete key) >> restore (try' r >> return ())
61− where
62− delete k (ReleaseMap next m) = (ReleaseMap next $ IntMap.delete k m, ())
63−
64−runResourceT :: MonadBaseControl IO m
65− => ResourceT m a
66− -> m a
67−runResourceT r = do
68− istate <- liftBase $ I.newIORef $ ReleaseMap minBound IntMap.empty
69− runReaderT (r `finally` cleanup) istate
70− where
71− cleanup = do
72− ReleaseMap _ m <- get
73− if IntMap.null m
74− then return ()
75− else do
76− let (key, _) = IntMap.findMin m
77− release $ ReleaseKey key
78− cleanup
79−
80−try' :: MonadBaseControl IO m
81− => m a
82− -> m (Either SomeException a)
83−try' = try
conduit/Data/Conduit.hsdeleted−93
@@ -1,93 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE MultiParamTypeClasses #-}
4−{-# LANGUAGE UndecidableInstances #-}
5−{-# LANGUAGE DeriveDataTypeable #-}
6−module Data.Conduit
7− ( -- * Helper types
8− ConduitResult (..)
9− -- * Main types
10− , Conduit
11− -- * Connect pieces together
12− , ($$)
13− , (<$$>)
14− , ($=)
15− , (=$)
16− -- * Other modules
17− -- ** Source
18− , module Data.Conduit.Types.Source
19− , module Data.Conduit.Util.Source
20− -- ** Sink
21− , module Data.Conduit.Types.Sink
22− , module Data.Conduit.Util.Sink
23− ) where
24−
25−import Control.Monad.Trans.Resource
26−import Control.Monad.Trans.Control (MonadBaseControl)
27−import Data.Conduit.Types.Source
28−import Data.Conduit.Util.Source
29−import Data.Conduit.Types.Sink
30−import Data.Conduit.Util.Sink
31−
32−data ConduitResult input output = ConduitResult [input] (Stream output)
33−
34−type Conduit a m b = Stream a -> ResourceT m (ConduitResult a b)
35−
36−infixr 0 $$
37−
38−($$) :: MonadBaseControl IO m => Source m a -> SinkM a m b -> ResourceT m b
39−source $$ SinkM msink = do
40− sinkI <- msink
41− case sinkI of
42− SinkNoData output -> return output
43− SinkData push close -> do
44− bs <- bsource source
45− connect' bs push close
46− where
47− connect' bs push close = do
48− stream <- bsourcePull bs
49− case stream of
50− EOF -> do
51− SinkResult leftover res <- close
52− bsourceUnpull bs leftover
53− return res
54− Chunks a -> do
55− SinkResult leftover mres <- push a
56− bsourceUnpull bs leftover
57− case mres of
58− Just res -> return res
59− Nothing -> connect' bs push close
60−
61−infixr 0 <$$>
62−
63−(<$$>) :: MonadBaseControl IO m => SourceM m a -> SinkM a m b -> ResourceT m b
64−SourceM msrc <$$> sink = msrc >>= ($$ sink)
65−
66−infixl 1 $=
67−
68−($=) :: MonadBaseControl IO m
69− => Source m a
70− -> Conduit a m b
71− -> Source m b
72−_ $= _ =
73− undefined
74− {-
75− source' <- unSource $ mkSource $ do
76− ConduitResult leftover result <- sourcePull source >>= pipe
77− sourcePush source leftover
78− return result
79− return source'
80− -}
81−
82−infixr 0 =$
83−
84−(=$) :: MonadBaseControl IO m => Conduit a m b -> SinkM b m c -> SinkM a m c
85−_ =$ _ = SinkM $ do
86− undefined {-
87− case sinkI of
88− SinkData sink -> return $ SinkData $ \stream -> do
89− ConduitResult leftover stream' <- pipe stream
90− SinkResult _thisislost mres <- sink stream'
91− return $ SinkResult leftover mres
92− SinkNoData mres -> return $ SinkNoData mres
93− -}
conduit/Data/Conduit/Binary.hsdeleted−37
@@ -1,37 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−module Data.Conduit.Binary
3− ( sourceFile
4− , sinkFile
5− ) where
6−
7−import Prelude hiding (FilePath)
8−import System.IO (hClose)
9−import Filesystem.Path.CurrentOS (FilePath)
10−import qualified Data.ByteString as S
11−import qualified Data.ByteString.Lazy as L
12−import Filesystem (openFile, IOMode (ReadMode, WriteMode))
13−import Data.Conduit
14−import Control.Monad.Trans.Control (MonadBaseControl)
15−import Control.Monad.Base (liftBase)
16−import Control.Monad.Trans.Resource (with, release)
17−
18−sourceFile :: MonadBaseControl IO m
19− => FilePath
20− -> SourceM m S.ByteString
21−sourceFile fp = sourceM
22− (with (liftBase $ openFile fp ReadMode) (liftBase . hClose))
23− (\(key, _) -> release key)
24− (\(_, handle) -> do
25− bs <- liftBase $ S.hGetSome handle 50
26− if S.null bs
27− then return EOF
28− else return $ Chunks [bs])
29−
30−sinkFile :: MonadBaseControl IO m
31− => FilePath
32− -> SinkM S.ByteString m ()
33−sinkFile fp = sinkM
34− (with (liftBase $ openFile fp WriteMode) (liftBase . hClose))
35− (\(key, _) -> release key)
36− (\(_, handle) bss -> liftBase (L.hPut handle $ L.fromChunks bss) >> return (SinkResult [] Nothing))
37− (const $ return $ SinkResult [] ())
conduit/Data/Conduit/List.hsdeleted−89
@@ -1,89 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−module Data.Conduit.List
3− ( fold
4− , fromList
5− , take
6− , map
7− , concatMap
8− , head
9− , consume
10− ) where
11−
12−import qualified Prelude
13−import Prelude hiding (take, map, concatMap, head)
14−import Data.Conduit
15−import Control.Monad.Trans.Control (MonadBaseControl)
16−import Control.Monad.Base (liftBase)
17−import qualified Data.IORef as I
18−import Control.Applicative ((<$>))
19−import Data.List (foldl')
20−
21−fold :: MonadBaseControl IO m
22− => (b -> a -> b)
23− -> b
24− -> SinkM a m b
25−fold f accum0 = sinkM
26− (liftBase $ I.newIORef accum0)
27− (const $ return ())
28− push
29− close
30− where
31− close iaccum = SinkResult [] <$> liftBase (I.readIORef iaccum)
32− push iaccum cs = do
33− liftBase $ I.atomicModifyIORef iaccum $
34− \accum -> (foldl' f accum cs, ())
35− return $ SinkResult [] Nothing
36−
37−fromList :: MonadBaseControl IO m => [a] -> SourceM m a
38−fromList l = sourceM
39− (liftBase (I.newIORef l))
40− (const (return ()))
41− (\il -> do
42− l' <- liftBase $ I.atomicModifyIORef il $ \x -> ([], x)
43− return $ if null l' then EOF else Chunks l')
44−
45−take :: MonadBaseControl IO m
46− => Int
47− -> SinkM a m [a]
48−take count0 = sinkM
49− (liftBase $ I.newIORef (count0, []))
50− (const $ return ())
51− push
52− close
53− where
54− close istate = SinkResult [] . snd <$> liftBase (I.readIORef istate)
55− push istate cs = do
56− (count, rest', b) <- liftBase $ I.atomicModifyIORef istate $ \(count, rest) ->
57− let (a, b) = splitAt count cs
58− count' = count - length a
59− rest' = rest ++ a
60− in ((count', rest'), (count', rest', b))
61− return $ SinkResult b $ if count == 0 then Just rest' else Nothing
62−
63−head :: MonadBaseControl IO m => SinkM a m (Maybe a)
64−head = SinkM $ return $ SinkData
65− { sinkPush = return . push
66− , sinkClose = return $ SinkResult [] Nothing
67− }
68− where
69− push [] = SinkResult [] Nothing
70− push (a:as) = SinkResult as (Just (Just a))
71−
72−map :: Monad m => (a -> b) -> Conduit a m b
73−map f a = return $ ConduitResult [] $ fmap f a
74−
75−concatMap :: Monad m => (a -> [b]) -> Conduit a m b
76−concatMap _ EOF = return $ ConduitResult [] EOF
77−concatMap f (Chunks l) = return $ ConduitResult [] $ Chunks $ Prelude.concatMap f l
78−
79−consume :: MonadBaseControl IO m => SinkM a m [a]
80−consume = sinkM
81− (liftBase $ I.newIORef id)
82− (const $ return ())
83− push
84− close
85− where
86− close ifront = SinkResult [] . ($ []) <$> liftBase (I.readIORef ifront)
87− push ifront cs = do
88− liftBase $ I.atomicModifyIORef ifront $ \front -> (front . (cs ++), ())
89− return $ SinkResult [] Nothing
conduit/Data/Conduit/Types/Sink.hsdeleted−195
@@ -1,195 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE MultiParamTypeClasses #-}
3−-- | Defines the types for a sink, which is a consumer of data.
4−module Data.Conduit.Types.Sink
5− ( SinkResult (..)
6− , Sink (..)
7− , SinkM (..)
8− ) where
9−
10−import Control.Monad.Trans.Resource (ResourceT)
11−import Control.Monad.Trans.Class (MonadTrans (lift))
12−import Control.Monad (liftM)
13−import Control.Applicative (Applicative (..))
14−import Control.Monad.Base (MonadBase (liftBase))
15−import qualified Data.IORef as I
16−
17−-- | When a sink is given data, it can return both leftover data and a result.
18−data SinkResult input output = SinkResult [input] output
19−instance Functor (SinkResult input) where
20− fmap f (SinkResult input output) = SinkResult input (f output)
21−
22−-- | In general, a sink will consume data and eventually produce an output when
23−-- it has consumed \"enough\" data. There are two caveats to that statement:
24−--
25−-- * Some sinks do not actually require any data to produce an output. This is
26−-- included with a sink in order to allow for a 'Monad' instance.
27−--
28−-- * Some sinks will consume all available data and only produce a result at
29−-- the \"end\" of a data stream (e.g., @sum@).
30−--
31−-- To allow for the first caveat, we have the 'SinkNoData' constructor. For the
32−-- second, the 'SinkData' constructor has two records: one for receiving more
33−-- input, and the other to indicate the end of a stream. Note that, at the end
34−-- of a stream, some output is required. If a specific 'Sink' implementation
35−-- cannot always produce output, this should be indicated in its return value,
36−-- using something like a 'Maybe' or 'Either'.
37−--
38−-- Invariants:
39−--
40−-- * After a 'Sink' produces a result (either via 'sinkPush' or 'sinkClose'),
41−-- neither of those two functions may be called on the @Sink@ again.
42−--
43−-- * If a @Sink@ needs to clean up any resources (e.g., close a file handle),
44−-- it must do so whenever it returns a result, either via @sinkPush@ or
45−-- @sinkClose@.
46−data Sink input m output =
47− SinkNoData output
48− | SinkData
49− { sinkPush :: [input] -> ResourceT m (SinkResult input (Maybe output))
50− , sinkClose :: ResourceT m (SinkResult input output)
51− }
52−
53−instance Monad m => Functor (Sink input m) where
54− fmap f (SinkNoData x) = SinkNoData (f x)
55− fmap f (SinkData p c) = SinkData
56− { sinkPush = liftM (fmap (fmap f)) . p
57− , sinkClose = liftM (fmap f) c
58− }
59−
60−-- | Most 'Sink's require some type of state, similar to 'Source's. Like a
61−-- @SourceM@ for a @Source@, a @SinkM@ is a simple monadic wrapper around a
62−-- @Sink@ which allows initialization of such state. See @SourceM@ for further
63−-- caveats.
64−--
65−-- Note that this type provides a 'Monad' instance, allowing you to easily
66−-- compose @SinkM@s together.
67−newtype SinkM input m output = SinkM { genSink :: ResourceT m (Sink input m output) }
68−
69−instance Monad m => Functor (SinkM input m) where
70− fmap f (SinkM msink) = SinkM (liftM (fmap f) msink)
71−
72−instance MonadBase IO m => Applicative (SinkM input m) where
73− pure x = SinkM (return (SinkNoData x))
74− SinkM mf <*> SinkM ma = SinkM $ do
75− f <- mf
76− a <- ma
77− case (f, a) of
78− (SinkNoData f', SinkNoData a') -> return (SinkNoData (f' a'))
79− _ -> do
80− let toEither (SinkData x y) = SinkPair x y
81− toEither (SinkNoData x) = SinkOutput x
82− istate <- liftBase (I.newIORef (toEither f, toEither a))
83− return $ appHelper istate
84−
85−type SinkPush input m output = [input] -> ResourceT m (SinkResult input (Maybe output))
86−type SinkClose input m output = ResourceT m (SinkResult input output)
87−data SinkEither input m output = SinkPair (SinkPush input m output) (SinkClose input m output) | SinkOutput output
88−type SinkState input m a b = I.IORef (SinkEither input m (a -> b), SinkEither input m a)
89−
90−appHelper :: MonadBase IO m => SinkState input m a b -> Sink input m b
91−appHelper istate = SinkData (pushHelper istate) (closeHelper istate)
92−
93−pushHelper :: MonadBase IO m
94− => SinkState input m a b
95− -> [input]
96− -> ResourceT m (SinkResult input (Maybe b))
97−pushHelper istate stream0 = do
98− state <- liftBase $ I.readIORef istate
99− go state stream0
100− where
101− go (SinkPair f _, eb) stream = do
102− SinkResult leftover mres <- f stream
103− case mres of
104− Nothing -> return $ SinkResult leftover Nothing
105− Just res -> do
106− let state' = (SinkOutput res, eb)
107− liftBase $ I.writeIORef istate state'
108− go state' leftover
109− go (f@SinkOutput{}, SinkPair b _) stream = do
110− SinkResult leftover mres <- b stream
111− case mres of
112− Nothing -> return $ SinkResult leftover Nothing
113− Just res -> do
114− let state' = (f, SinkOutput res)
115− liftBase $ I.writeIORef istate state'
116− go state' leftover
117− go (SinkOutput f, SinkOutput b) leftover = return $ SinkResult leftover (Just $ f b)
118−
119−closeHelper :: MonadBase IO m
120− => SinkState input m a b
121− -> ResourceT m (SinkResult input b)
122−closeHelper istate = do
123− (sf, sa) <- liftBase $ I.readIORef istate
124− case sf of
125− SinkOutput f -> go' f sa
126− SinkPair _ close -> do
127− SinkResult leftover f <- close
128− go f leftover sa
129− where
130− go f leftover sa
131− | null leftover = go' f sa
132− go f leftover (SinkOutput a) = return $ SinkResult leftover (f a)
133− go f leftover sp@(SinkPair push _) = do
134− SinkResult leftover' mres <- push leftover
135− case mres of
136− Just a -> return $ SinkResult leftover' (f a)
137− Nothing -> go f leftover' sp
138− go' f (SinkPair _ close) = do
139− SinkResult leftover a <- close
140− return $ SinkResult leftover (f a)
141− go' f (SinkOutput a) = return $ SinkResult [] (f a)
142−
143−instance MonadBase IO m => Monad (SinkM input m) where
144− return = pure
145− x >>= f = sinkJoin (fmap f x)
146−
147−instance MonadBase IO m => MonadBase IO (SinkM input m) where
148− liftBase = lift . liftBase
149−
150−instance MonadTrans (SinkM input) where
151− lift f = SinkM (lift (liftM SinkNoData f))
152−
153−sinkJoin :: MonadBase IO m => SinkM a m (SinkM a m b) -> SinkM a m b
154−sinkJoin (SinkM msink) = SinkM $ do
155− sink <- msink
156− case sink of
157− SinkNoData (SinkM inner) -> inner
158− SinkData pushO closeO -> do
159− istate <- liftBase $ I.newIORef Nothing
160− return $ SinkData (push istate pushO) (close istate closeO)
161− where
162− push istate pushO stream = do
163− state <- liftBase $ I.readIORef istate
164− case state of
165− Just (pushI, _) -> pushI stream
166− Nothing -> do
167− SinkResult leftover minner' <- pushO stream
168− case minner' of
169− Nothing -> return $ SinkResult leftover Nothing
170− Just (SinkM msink') -> do
171− sink <- msink'
172− case sink of
173− SinkData pushI closeI -> do
174− liftBase $ I.writeIORef istate $ Just (pushI, closeI)
175− if null leftover
176− then return $ SinkResult [] Nothing
177− else push istate pushO leftover
178− SinkNoData x -> return $ SinkResult leftover $ Just x
179− close istate closeO = do
180− state <- liftBase $ I.readIORef istate
181− case state of
182− Just (_, closeI) -> closeI
183− Nothing -> do
184− SinkResult leftover (SinkM minner) <- closeO
185− inner <- minner
186− case inner of
187− SinkData pushI closeI -> closeHelper' leftover pushI closeI
188− SinkNoData x -> return $ SinkResult leftover x
189− closeHelper' stream pushI closeI
190− | null stream = closeI
191− | otherwise = do
192− SinkResult leftover mres <- pushI stream
193− case mres of
194− Nothing -> closeHelper' leftover pushI closeI
195− Just res -> return $ SinkResult leftover res
conduit/Data/Conduit/Types/Source.hsdeleted−138
@@ -1,138 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE DeriveDataTypeable #-}
3−-- | Defines the types for a source, which is a producer of data.
4−module Data.Conduit.Types.Source
5− ( Stream (..)
6− , Source (..)
7− , SourceM (..)
8− , BSource (..)
9− , SourceInvariantException (..)
10− ) where
11−
12−import Control.Monad.Trans.Resource (ResourceT)
13−import Data.Monoid (Monoid (..))
14−import Control.Monad.Base (MonadBase (liftBase))
15−import qualified Data.IORef as I
16−import Control.Monad (liftM)
17−import Data.Typeable (Typeable)
18−import Control.Exception (Exception, throw)
19−
20−-- | In general, a source represents a producer of a stream of data. A source
21−-- can either produce output, or indicate that there is no more data. This type
22−-- represents the output from a 'Source'.
23−data Stream a = EOF -- ^ no more data
24− | Chunks [a] -- ^ produced some data
25−
26−instance Functor Stream where
27− fmap _ EOF = EOF
28− fmap f (Chunks a) = Chunks (map f a)
29−
30−-- | A 'Source' has two operations on it: pull some data, and close the
31−-- 'Source'. Since 'Source' is built on top of 'ResourceT', all acquired
32−-- resources should be automatically released anyway. Closing a 'Source' early
33−-- is merely an optimization to free scarce resources as soon as possible.
34−--
35−-- A 'Source' has two invariants:
36−--
37−-- * It is illegal to call 'sourcePull' after a previous call returns 'EOF'.
38−--
39−-- * It is legal to call 'sourceClose' multiple times.
40−--
41−-- 'Source's are not expected to call 'sourceClose' when they return an 'EOF'
42−-- from 'sourcePull', though based on the second invariant, they are free to do
43−-- so.
44−data Source m a = Source
45− { sourcePull :: ResourceT m (Stream a)
46− , sourceClose :: ResourceT m ()
47− }
48−
49−instance Monad m => Functor (Source m) where
50− fmap f src = src
51− { sourcePull = liftM (fmap f) (sourcePull src)
52− }
53−
54−-- | All but the simplest of 'Source's (e.g., @repeat@ and @cycle@) require
55−-- some type of state to track their current status. This may be in the form of
56−-- a mutable variable (e.g., @IORef@), or via opening a resource like a
57−-- @Handle@. While a 'Source' is given no opportunity to acquire such
58−-- resources, this type is.
59−--
60−-- A 'SourceM' is simply a monadic action that returns a 'Source'. One nice
61−-- consequence of this is the possibility of creating an efficient 'Monoid'
62−-- instance, which will only acquire one resource at a time, instead of bulk
63−-- acquiring all resources at the beginning of running the 'SourceM'.
64−--
65−-- Note that each time you \"call\" a @SourceM@, it is started from scratch. If
66−-- you want a resumable source (e.g., one which can be passed to multiple
67−-- @Sink@s), you likely want to use a 'BSource'.
68−newtype SourceM m a = SourceM { genSource :: ResourceT m (Source m a) }
69−
70−instance Monad m => Functor (SourceM m) where
71− fmap f (SourceM msrc) = SourceM (liftM (fmap f) msrc)
72−
73−instance MonadBase IO m => Monoid (SourceM m a) where
74− mempty = SourceM (return Source
75− { sourcePull = return EOF
76− , sourceClose = return ()
77− })
78− mappend a b = mconcat [a, b]
79− mconcat [] = mempty
80− mconcat (SourceM mnext:rest0) = SourceM $ do
81− -- open up the first SourceM...
82− next0 <- mnext
83− -- and place it in a mutable reference along with all of the upcoming
84− -- SourceMs
85− istate <- liftBase (I.newIORef (next0, rest0))
86− return Source
87− { sourcePull = pull istate
88− , sourceClose = close istate
89− }
90− where
91− pull istate =
92− liftBase (I.readIORef istate) >>= pull'
93− where
94− pull' (current, rest) = do
95− stream <- sourcePull current
96− case stream of
97− -- end of the current Source
98− EOF -> do
99− -- close the current Source
100− sourceClose current
101− case rest of
102− -- ... and open the next one
103− SourceM ma:as -> do
104− a <- ma
105− liftBase (I.writeIORef istate (a, as))
106− -- continue pulling base on this new state
107− pull istate
108− -- no more source, return an EOF
109− [] -> do
110− -- give an error message if the first Source
111− -- invariant is violated (read data after EOF)
112− liftBase (I.writeIORef istate (throw (PullAfterEOF "SourceM:mconcat")))
113− return EOF
114− Chunks _ -> return stream
115− close istate = do
116− -- we only need to close the current Source, since they are opened
117− -- one at a time
118− (current, _) <- liftBase (I.readIORef istate)
119− sourceClose current
120−
121−-- | When actually interacting with 'Source's, we usually want to be able to
122−-- buffer the output, in case any intermediate steps return leftover data. A
123−-- 'BSource' allows for such buffering, via the 'bsourceUnpull' function.
124−--
125−-- A 'BSource', unlike a 'SourceM', is resumable, meaning it can be passed to
126−-- multiple 'Sink's without restarting.
127−--
128−-- Finally, a 'BSource' relaxes one of the invariants of a 'Source': calling
129−-- 'bsourcePull' after an 'EOF' will simply return another 'EOF'.
130−data BSource m a = BSource
131− { bsourcePull :: ResourceT m (Stream a)
132− , bsourceUnpull :: [a] -> ResourceT m () -- ^ It is the responsibility of the 'BSource' to check if the argument is null.
133− , bsourceClose :: ResourceT m ()
134− }
135−
136−data SourceInvariantException = PullAfterEOF String
137− deriving (Show, Typeable)
138−instance Exception SourceInvariantException
conduit/Data/Conduit/Util/Sink.hsdeleted−29
@@ -1,29 +0,0 @@
1−-- | Utilities for constructing 'SinkM's. Please see "Data.Conduit.Types.Sink"
2−-- for more information on the base types.
3−module Data.Conduit.Util.Sink
4− ( sinkM
5− ) where
6−
7−import Control.Monad.Trans.Resource (ResourceT)
8−import Data.Conduit.Types.Sink
9−
10−-- | Construct a 'SinkM'. Note that your push and close functions need not
11−-- explicitly perform any cleanup.
12−sinkM :: Monad m
13− => ResourceT m state -- ^ resource and/or state allocation
14− -> (state -> ResourceT m ()) -- ^ resource and/or state cleanup
15− -> (state -> [input] -> ResourceT m (SinkResult input (Maybe output))) -- ^ push
16− -> (state -> ResourceT m (SinkResult input output)) -- ^ close
17− -> SinkM input m output
18−sinkM alloc cleanup push close = SinkM $ do
19− state <- alloc
20− return SinkData
21− { sinkPush = \input -> do
22− res@(SinkResult _ mout) <- push state input
23− maybe (return ()) (const $ cleanup state) mout
24− return res
25− , sinkClose = do
26− res <- close state
27− cleanup state
28− return res
29− }
conduit/Data/Conduit/Util/Source.hsdeleted−84
@@ -1,84 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−-- | Utilities for constructing and converting 'Source', 'SourceM' and
3−-- 'BSource' types. Please see "Data.Conduit.Types.Source" for more information
4−-- on the base types.
5−module Data.Conduit.Util.Source
6− ( sourceM
7− , bsource
8− , bsourceM
9− ) where
10−
11−import Control.Monad.Trans.Resource (ResourceT)
12−import Data.Conduit.Types.Source
13−import Control.Monad.Base (MonadBase (liftBase))
14−import qualified Data.IORef as I
15−
16−-- | Construct a 'SourceM'.
17−sourceM :: Monad m
18− => ResourceT m state -- ^ resource and/or state allocation
19− -> (state -> ResourceT m ()) -- ^ resource and/or state cleanup
20− -> (state -> ResourceT m (Stream output)) -- ^ Pull function. Note that this need not explicitly perform any cleanup.
21− -> SourceM m output
22−sourceM alloc cleanup pull = SourceM $ do
23− state <- alloc
24− return Source
25− { sourcePull = pull state
26− , sourceClose = cleanup state
27− }
28−
29−-- | State of a 'BSource'
30−data BState a = EmptyOpen -- ^ nothing in buffer, EOF not received yet
31− | EmptyClosed -- ^ nothing in buffer, EOF has been received
32− | Open [a] -- ^ something in buffer, EOF not received yet
33− | Closed [a] -- ^ something in buffer, EOF has been received
34− deriving Show
35−
36−-- | Convert a 'SourceM' into a 'BSource'.
37−bsource :: MonadBase IO m
38− => Source m output
39− -> ResourceT m (BSource m output)
40−bsource src = do
41− -- state is a boolean indicating whether EOF was reached, and a buffer
42− istate <- liftBase (I.newIORef EmptyOpen)
43− return BSource
44− { bsourcePull = do
45− mresult <- liftBase $ I.atomicModifyIORef istate $ \state ->
46− case state of
47− Open buffer -> (EmptyOpen, Just (Chunks buffer))
48− Closed buffer -> (EmptyClosed, Just (Chunks buffer))
49− EmptyOpen -> (EmptyOpen, Nothing)
50− EmptyClosed -> (EmptyClosed, Just EOF)
51− case mresult of
52− Nothing -> do
53− result <- sourcePull src
54− case result of
55− EOF -> do
56− liftBase $ I.writeIORef istate EmptyClosed
57− return EOF
58− _ -> return result
59− Just result -> return result
60− , bsourceUnpull =
61− \x ->
62− if null x
63− then return ()
64− else liftBase $ I.atomicModifyIORef istate $ \state ->
65− case state of
66− Open buffer -> (Open (x ++ buffer), ())
67− Closed buffer -> (Closed (x ++ buffer), ())
68− EmptyOpen -> (Open x, ())
69− EmptyClosed -> (Closed x, ())
70− , bsourceClose = do
71− action <- liftBase $ I.atomicModifyIORef istate $ \state ->
72− case state of
73− Open x -> (Closed x, sourceClose src)
74− Closed _ -> (state, return ())
75− EmptyOpen -> (EmptyClosed, sourceClose src)
76− EmptyClosed -> (state, return ())
77− action
78− }
79−
80−-- | Convert a 'SourceM' into a 'BSource'.
81−bsourceM :: MonadBase IO m
82− => SourceM m output
83− -> ResourceT m (BSource m output)
84−bsourceM (SourceM msrc) = msrc >>= bsource
conduit/LICENSEdeleted−30
@@ -1,30 +0,0 @@
1−Copyright (c)2011, Michael Snoyman
2−
3−All rights reserved.
4−
5−Redistribution and use in source and binary forms, with or without
6−modification, are permitted provided that the following conditions are met:
7−
8− * Redistributions of source code must retain the above copyright
9− notice, this list of conditions and the following disclaimer.
10−
11− * Redistributions in binary form must reproduce the above
12− copyright notice, this list of conditions and the following
13− disclaimer in the documentation and/or other materials provided
14− with the distribution.
15−
16− * Neither the name of Michael Snoyman nor the names of other
17− contributors may be used to endorse or promote products derived
18− from this software without specific prior written permission.
19−
20−THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21−"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22−LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23−A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24−OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25−SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26−LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27−DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28−THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29−(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30−OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
conduit/README.mddeleted−178
@@ -1,178 +0,0 @@
1−Conduits are an approach to the streaming data problem. It is meant as an
2−alternative to enumerators/iterators, hoping to address the same issues with
3−different trade-offs based on real-world experience with enumerators.
4−
5−General Goal
6−===========================
7−
8−Let's start by defining the goal of enumerators, iterators, and conduits. We
9−want a standard interface to represent streaming data from one point to
10−another, possibly modifying the data along the way.
11−
12−This goal is also achieved by lazy I/O; the problem with lazy I/O, however, is
13−that of deterministic resource cleanup. That is to say, with lazy I/O, you
14−cannot be guaranteed that your file handles will be closed as soon as you have
15−finished reading data from them.
16−
17−We want to keep the same properties of constant memory usage from lazy I/O, yet
18−have guarantees that scarce resources will be freed as early as possible.
19−
20−Enumerator
21−===========================
22−
23−__Note__: This is biased towards John Millikin's enumerator package, as that is
24−the package with which I have the most familiarity.
25−
26−The concept of an enumerator is fairly simple. We have an `Iteratee` which
27−"consumes" data. It keeps its state while being fed data by an `Enumerator`.
28−The `Enumerator` will feed data a few chunks at a time to an `Iteratee`,
29−transforming the `Iteratee`'s state at each call. Additionally, there is an
30−`Enumeratee` that acts as both an `Enumerator` and `Iteratee`.
31−
32−As a result, there are a few changes to code structure that need to take place
33−in order to fully leverage enumerators:
34−
35−* The `Enumerator`s control code flow. This is an Inversion of Control (IoC)
36− technique.
37−
38− __Practical ramification__: `Iteratee` code can be more difficult to
39− structure.
40−
41−* `Iteratee`s are not able to allocate scarce resources. Since they do not
42− have any control of the flow of the program, they cannot guarantee that
43− the resources will be released, especially in the presence of exceptions.
44−
45− __Practical ramification__: There is no way to create an `iterFile`, which
46− will stream data into a file. Instead, you must allocate a file handle
47− before entering the `Iteratee` and pass that in. In some cases, such an
48− approach would mean file handles are kept open too long.
49−
50−* None of this plays nicely with monad transformers, though this does not
51− seem to be an inherent problem with enumerators, instead with the current
52− library.
53−
54− __Practical ramification__: You cannot enumerate a file when running in a
55− `ReaderT IO`.
56−
57−* Instead of passing around a `Handle` to pull data from, your code should
58− live inside an `Iteratee`. This makes it difficult and/or impossible to
59− interleave two different sources.
60−
61− __Practical ramification__: Even with libraries designed to interoperate
62− (like http-enumerator and warp), it is not possible to create a proper
63− streaming HTTP proxy.
64−
65−* While the concepts are simple, actually writing low-level Iteratee code is
66− very complex. This in turn intimidates users from adopting the approach.
67−
68−Conduits
69−===========================
70−
71−Conduits attempt to provide a similar high-level API to enumerators, while
72−providing a drastically different low-level implementation. The first question
73−to visit is: why does the enumerator need to control flow of the program? The
74−main purpose is to ensure that resources are released properly. But this in
75−fact solved only *half* the problem; iteratees still cannot release resources.
76−
77−ResourceT
78−---------------------------
79−
80−So our first issue to address is to create a new way to deal with resource
81−allocation. We represent this as a monad transformer, `ResourceT`. It works as
82−follows:
83−
84−* You can register a cleanup action, which will return a `ReleaseKey`.
85−
86−* If you pass your `ReleaseKey` to the `release` function, your action will be
87− called automatically, and your action will be unregistered.
88−
89−* When the monad is exited (via `runRelease`), all remaining registered actions
90− will be called.
91−
92−* All of this is provided in an exception-safe manner.
93−
94−For example, you would be able to open a file handle, and then register an
95−action to close the file handle. In your code, you would call `release` on your
96−`ReleaseKey` as soon as you reach the end of the contents you are streaming. If
97−that code is never reached, the file handle will be released when the monad
98−terminates.
99−
100−Source
101−---------------------------
102−
103−Now that we have a way to deal with resources, we can take a radically
104−different approach to production of data streams. Instead of a push system,
105−where the enumerators sends data down the pipeline, we have a pull system,
106−where data is requested from the source. Additionally, a source allows
107−buffering of input data, so data can be "pushed back" onto the source to be
108−available for a later call.
109−
110−Sink
111−---------------------------
112−
113−A `Sink` is the corrollary to an `Iteratee`. It takes a stream of data, and can
114−return a result, consisting of leftover input and an output. Like an
115−`Iteratee`, a `Sink` provides a `Monad` instance, which allows easy chaining
116−together of `Sink`s.
117−
118−However, a big difference is that your code needn't live in the `Sink` monad.
119−You can easily pass around your sources and connect them to different `Sink`s.
120−As a practical example, when the Web Application Interface (WAI) is translated
121−to conduits, the application lives in the `ResourceT IO` monad, and the
122−`Request` value contains a `requestBody` record, which is a `Source IO
123−ByteString`.
124−
125−Conduit
126−---------------------------
127−
128−Conduits are simply functions that that a stream of input data and return
129−leftover input as well as a stream of output data. Conduits are far simpler to
130−implement than their corrollary, `Enumeratee`s.
131−
132−Connecting
133−---------------------------
134−
135−While you can directly pull data from a `Source`, or directly push to a `Sink`, the easiest approach is to use the built-in connect operators. These follow the naming convention from the enumerator package, e.g.:
136−
137− sourceFile "myfile.txt" $$ sinkFile "mycopy.txt"
138− sourceFile "myfile.txt" $= uppercase {- a conduit -} $$ sinkFile "mycopy.txt"
139− fromList [1..10] $$ Data.Conduit.List.map (+ 1) =$ fold (+) 0
140−
141−Trade-offs
142−===========================
143−
144−Overall, the approach achieves the goals I had hoped for. The main downside in
145−its current form is its reliance on mutable data. Instead of having an
146−`Iteratee` return a new `Iteratee`, thereby provide an illusion of mutability,
147−in conduit the sources and sinks must maintain their state internally. As a
148−result, code must live in IO and usually use something like an IORef to keep
149−track of the current state.
150−
151−I believe this to be an acceptable trade-off, since:
152−
153−1. Virtually all conduit code will be performing I/O, so staying in the `IO`
154− monad is reasonable.
155−2. By using `monad-control`, conduit can work with any monad *based* on `IO`,
156− meaning all standard transformers (except `ContT`) can be used.
157−3. Enumerator experience has shown that the majority of the time, you construct
158− `Iteratee`s by using built-in functions, such as fold and map. Therefore,
159− the complication of tracking mutable state will usually be abstracted from
160− users.
161−
162−Another minor point is that, in order to provide an efficient `Monad` instance,
163−the `Sink` type is complicated with tracking two cases: a `Sink` which expects
164−data and one which does not. As expressed in point (3) above, this should not
165−have a major impact for users.
166−
167−Finally, since most `Source`s and `Sink`s begin their life by allocating some
168−mutable variable, both types allow some arbitrary monadic action to be run
169−before actual processing begins. The monad (et al) instances and connect
170−functions are all built to run this action once and then continue operation.
171−
172−Status
173−===========================
174−
175−This is currently no more than a proof-of-concept, to see the differences
176−between enumerators and conduits for practical problems. This may serve as a
177−basis for WAI and Yesod in the future, but that will only be after careful
178−vetting of the idea. Your input is greatly appreciated!
conduit/Setup.lhsdeleted−7
@@ -1,7 +0,0 @@
1−#!/usr/bin/env runhaskell
2−
3−> module Main where
4−> import Distribution.Simple
5−
6−> main :: IO ()
7−> main = defaultMain
conduit/conduit.cabaldeleted−45
@@ -1,45 +0,0 @@
1−Name: conduit
2−Version: 0.0.0
3−Synopsis: A pull-based approach to streaming data.
4−Description: Conduits are an approach to the streaming data problem. It is meant as an alternative to enumerators/iterators, hoping to address the same issues with different trade-offs based on real-world experience with enumerators. See the README.md file for more details.
5−License: BSD3
6−License-file: LICENSE
7−Author: Michael Snoyman
8−Maintainer: michael@snoyman.com
9−Category: Data
10−Build-type: Simple
11−Cabal-version: >=1.8
12−Homepage: http://github.com/yesodweb/wai
13−extra-source-files: test/main.hs
14−
15−Library
16− Exposed-modules: Data.Conduit
17− Data.Conduit.Binary
18− Data.Conduit.List
19− Control.Monad.Trans.Resource
20− Build-depends: base >= 4 && < 5
21− , lifted-base >= 0.1 && < 0.2
22− , transformers-base >= 0.4 && < 0.5
23− , monad-control >= 0.3 && < 0.4
24− , containers
25− , transformers >= 0.2.2 && < 0.3
26− , system-fileio >= 0.3.3 && < 0.4
27− , system-filepath >= 0.4.3 && < 0.5
28− , bytestring >= 0.9
29− ghc-options: -Wall
30−
31−test-suite test
32− hs-source-dirs: test
33− main-is: main.hs
34− type: exitcode-stdio-1.0
35− cpp-options: -DTEST
36− build-depends: conduit
37− , base
38− , hspec
39− , HUnit
40− , QuickCheck
41− ghc-options: -Wall
42−
43−source-repository head
44− type: git
45− location: git://github.com/yesodweb/wai.git
conduit/sum.hsdeleted−16
@@ -1,16 +0,0 @@
1−import Data.Conduit
2−import qualified Data.Conduit.List as CL
3−import Control.Monad.Trans.Resource
4−import Data.Monoid (mappend)
5−
6−main :: IO ()
7−main = do
8− runResourceT (
9− (CL.fromList [1..5] `mappend` CL.fromList [6..10])
10− $= CL.concatMap (replicate 2)
11− $$ do
12− _ <- CL.take 10
13− CL.map (* 2) =$ sum') >>= putStrLn
14−
15−sum' :: Sink Int IO String
16−sum' = fmap show $ CL.fold (+) 0
conduit/test/main.hsdeleted−50
@@ -1,50 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−import Test.Hspec.Monadic
3−import Test.Hspec.HUnit ()
4−import Test.Hspec.QuickCheck (prop)
5−import Test.HUnit
6−
7−import qualified Data.Conduit as C
8−import qualified Data.Conduit.List as CL
9−import qualified Data.Conduit.Binary as CB
10−import Control.Monad.Trans.Resource (runResourceT)
11−import System.IO.Unsafe (unsafePerformIO)
12−import Data.Monoid
13−import qualified Data.ByteString as S
14−
15−main :: IO ()
16−main = hspecX $ do
17− describe "sum" $ do
18− it "works for 1..10" $ do
19− x <- runResourceT $ CL.fromList [1..10] C.<$$> CL.fold (+) (0 :: Int)
20− x @?= sum [1..10]
21− prop "is idempotent" $ \list ->
22− (unsafePerformIO $ runResourceT $ CL.fromList list C.<$$> CL.fold (+) (0 :: Int))
23− == sum list
24− describe "Monoid instance for Source" $ do
25− it "mappend" $ do
26− x <- runResourceT $ (CL.fromList [1..5 :: Int] `mappend` CL.fromList [6..10]) C.<$$> CL.fold (+) 0
27− x @?= sum [1..10]
28− it "mconcat" $ do
29− x <- runResourceT $ mconcat
30− [ CL.fromList [1..5 :: Int]
31− , CL.fromList [6..10]
32− , CL.fromList [11..20]
33− ] C.<$$> CL.fold (+) 0
34− x @?= sum [1..20]
35− describe "file access" $ do
36− it "read" $ do
37− bs <- S.readFile "conduit.cabal"
38− bss <- runResourceT $ CB.sourceFile "conduit.cabal" C.<$$> CL.consume
39− bs @=? S.concat bss
40− it "write" $ do
41− runResourceT $ CB.sourceFile "conduit.cabal" C.<$$> CB.sinkFile "tmp"
42− bs1 <- S.readFile "conduit.cabal"
43− bs2 <- S.readFile "tmp"
44− bs1 @=? bs2
45− describe "Monad instance for Sink" $ do
46− it "binding" $ do
47− x <- runResourceT $ CL.fromList [1..10] C.<$$> do
48− _ <- CL.take 5
49− CL.fold (+) (0 :: Int)
50− x @?= sum [6..10]