on master help sign in
Initial conduit code
commitb7cde2602334cf8889608c08ce8352bab1d7cf1c authorMichael Snoyman <michael@snoyman.com> dateThu 8 Dec 2011, 18:08 (15 years ago) parent54bc112
3 files changed+206
conduit/Control/Monad/Trans/Resource.hs+85 conduit/Data/Conduit.hs+112 conduit/sum.hs+9
conduit/Control/Monad/Trans/Resource.hsnew file+85
@@ -0,0 +1,85 @@
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 Data.Unique (newUnique, hashUnique)
18+import Control.Exception.Lifted (try, finally, SomeException, mask, mask_)
19+import Control.Monad.Trans.Control (MonadBaseControl)
20+import qualified Data.IORef as I
21+import Control.Monad.Base (MonadBase, liftBase)
22+
23+newtype ReleaseMap m = ReleaseMap (IntMap (ResourceT m ()))
24+newtype ReleaseKey = ReleaseKey Int
25+
26+type ResourceT m = ReaderT (I.IORef (ReleaseMap m)) m
27+
28+with :: MonadBaseControl IO m
29+ => ResourceT m a -- ^ allocate
30+ -> (a -> ResourceT m ()) -- ^ free resource
31+ -> ResourceT m (ReleaseKey, a)
32+with acquire rel = mask_ $ do
33+ a <- acquire
34+ key <- register $ rel a
35+ return (key, a)
36+
37+modify :: MonadBase IO m => (ReleaseMap m -> ReleaseMap m) -> ResourceT m ()
38+modify f = do
39+ istate <- ask
40+ liftBase $ I.atomicModifyIORef istate $ \a -> (f a, ())
41+
42+get :: MonadBase IO m => ResourceT m (ReleaseMap m)
43+get = ask >>= liftBase . I.readIORef
44+
45+register :: MonadBaseControl IO m
46+ => ResourceT m ()
47+ -> ResourceT m ReleaseKey
48+register rel = mask_ $ do
49+ key <- liftBase $ fmap hashUnique newUnique
50+ modify (insert key rel)
51+ return (ReleaseKey key)
52+ where
53+ insert k v (ReleaseMap m) = ReleaseMap $ IntMap.insert k v m
54+
55+release :: MonadBaseControl IO m
56+ => ReleaseKey
57+ -> ResourceT m ()
58+release (ReleaseKey key) = mask $ \restore -> do
59+ ReleaseMap m <- get
60+ case IntMap.lookup key m of
61+ Nothing -> return () -- maybe we should throw an exception?
62+ Just r -> modify (delete key) >> restore (try' r >> return ())
63+ where
64+ delete k (ReleaseMap m) = ReleaseMap $ IntMap.delete k m
65+
66+runResourceT :: MonadBaseControl IO m
67+ => ResourceT m a
68+ -> m a
69+runResourceT r = do
70+ istate <- liftBase $ I.newIORef $ ReleaseMap IntMap.empty
71+ runReaderT (r `finally` cleanup) istate
72+ where
73+ cleanup = do
74+ ReleaseMap m <- get
75+ if IntMap.null m
76+ then return ()
77+ else do
78+ let (key, _) = IntMap.findMin m
79+ release $ ReleaseKey key
80+ cleanup
81+
82+try' :: MonadBaseControl IO m
83+ => m a
84+ -> m (Either SomeException a)
85+try' = try
conduit/Data/Conduit.hsnew file+112
@@ -0,0 +1,112 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE DeriveDataTypeable #-}
3+module Data.Conduit
4+ ( -- * Helper types
5+ Stream (..)
6+ , SinkResult (..)
7+ , ConduitResult (..)
8+ -- * Main types
9+ , Source (..)
10+ , Sink (..)
11+ , Conduit
12+ -- * Connect pieces together
13+ , connect
14+ , connectLeft
15+ , connectRight
16+ -- * Helper functions
17+ , mkSource
18+ ) where
19+
20+import Control.Monad.Trans.Resource
21+import Control.Monad.Trans.Control (MonadBaseControl)
22+import Control.Monad.Base (liftBase)
23+import Control.Exception.Lifted (throwIO, Exception)
24+import qualified Data.IORef as I
25+import Data.Typeable (Typeable)
26+import Control.Monad (liftM)
27+
28+data Stream a = EOF | Chunks [a]
29+ deriving Show
30+instance Functor Stream where
31+ fmap _ EOF = EOF
32+ fmap f (Chunks l) = Chunks (map f l)
33+
34+data SinkResult input output = SinkResult [input] (Maybe output)
35+instance Functor (SinkResult input) where
36+ fmap f (SinkResult input moutput) = SinkResult input (fmap f moutput)
37+
38+data ConduitResult input output = ConduitResult [input] (Stream output)
39+
40+data SourceInside m a = SourceInside
41+ { sourcePull :: ResourceT m (Stream a)
42+ , sourcePush :: [a] -> ResourceT m ()
43+ }
44+newtype Source m a = Source (ResourceT m (SourceInside m a))
45+
46+type SinkInside a m b = (Stream a -> ResourceT m (SinkResult a b))
47+newtype Sink input m output = Sink (ResourceT m (SinkInside input m output))
48+instance Monad m => Functor (Sink input m) where
49+ fmap f (Sink msink) = Sink $ do
50+ sink <- msink
51+ return $ \stream -> liftM (fmap f) (sink stream)
52+
53+type Conduit a m b = Stream a -> ResourceT m (ConduitResult a b)
54+
55+mkSource :: MonadBaseControl IO m
56+ => ResourceT m (Stream a)
57+ -> Source m a
58+mkSource pull = Source $ do
59+ buffer <- liftBase $ I.newIORef []
60+ return SourceInside
61+ { sourcePull = do
62+ x <- liftBase $ I.atomicModifyIORef buffer (\a -> ([], a))
63+ if null x
64+ then pull
65+ else return $ Chunks x
66+ , sourcePush = \s ->
67+ if null s
68+ then return ()
69+ else liftBase $
70+ I.atomicModifyIORef buffer (\a -> (s ++ a, ()))
71+ }
72+
73+connect :: MonadBaseControl IO m => Source m a -> Sink a m b -> ResourceT m b
74+connect (Source msource) (Sink msink) = do
75+ sink <- msink
76+ source <- msource
77+ connect' source sink
78+ where
79+ connect' source sink = do
80+ stream <- sourcePull source
81+ SinkResult leftover mres <- sink stream
82+ sourcePush source leftover
83+ case (stream, mres) of
84+ (_, Just res) -> return res
85+ (Chunks _, Nothing) -> connect' source sink
86+ (EOF, Nothing) -> liftBase (throwIO NothingAfterEOF)
87+
88+data NothingAfterEOF = NothingAfterEOF
89+ deriving (Show, Typeable)
90+instance Exception NothingAfterEOF
91+
92+connectLeft :: MonadBaseControl IO m
93+ => Source m a
94+ -> Conduit a m b
95+ -> Source m b
96+connectLeft (Source msource) pipe = Source $ do
97+ source <- msource
98+ source' <- unSource $ mkSource $ do
99+ ConduitResult leftover result <- sourcePull source >>= pipe
100+ sourcePush source leftover
101+ return result
102+ return source'
103+ where
104+ unSource (Source s) = s
105+
106+connectRight :: MonadBaseControl IO m => Conduit a m b -> Sink b m c -> Sink a m c
107+connectRight pipe (Sink msink) = Sink $ do
108+ sink <- msink
109+ return $ \stream -> do
110+ ConduitResult leftover stream' <- pipe stream
111+ SinkResult _thisislost mres <- sink stream'
112+ return $ SinkResult leftover mres
conduit/sum.hsnew file+9
@@ -0,0 +1,9 @@
1+import Data.Conduit
2+import Data.Conduit.List
3+import Control.Monad.Trans.Resource
4+
5+main :: IO ()
6+main = runResourceT (fromList [1..10] `connect` sum') >>= putStrLn
7+
8+sum' :: Sink Int IO String
9+sum' = fmap show $ fold (+) 0