streaming 0.1.0.16 → 0.1.0.17
raw patch · 2 files changed
+107/−8 lines, 2 files
Files
- Streaming/Prelude.hs +42/−4
- streaming.cabal +65/−4
Streaming/Prelude.hs view
@@ -17,6 +17,19 @@ > import qualified Pipes.Prelude as P > import qualified System.IO as IO + Here are some correspondences between the types employed here and elsewhere:++> streaming | pipes | conduit | io-streams+> ----------------------------------------------------------------------------------------------------------+> Stream (Of a) m () | Producer a m () | Source m a | InputStream a+> | ListT m a | ConduitM () o m () | Generator r ()+> ----------------------------------------------------------------------------------------------------------+> Stream (Of a) m r | Producer a m r | ConduitM () o m r | Generator a r+> ----------------------------------------------------------------------------------------------------------+> Stream (Of a) m (Stream (Of a) m r) | Producer a m (Producer a m r) | +> -----------------------------------------------------------------------------------------------------------+> Stream (Stream (Of a) m) r | FreeT (Producer a m) m r |+ -} {-# LANGUAGE RankNTypes, BangPatterns, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveTraversable #-}@@ -200,7 +213,30 @@ m :> x >>= f = let m' :> y = f x in mappend m m' :> y {-#INLINE (>>=) #-} +{-| Note that 'lazily', 'strictly', 'fst'', and 'mapOf' are all so-called /natural transformations/ on the primitive @Of a@ functor+ If we write + +> type f ~> g = forall x . f x -> g x+ + then we have+ +> mapOf :: (a -> b) -> Of a ~> Of b+> lazily :: Of a -> (,) a+> fst' :: Of a -> Identity a + Manipulation of a @Stream f m r@ by mapping often turns on recognizing natural transformations of @f@,+ thus++> S.map :: (a -> b) -> Stream (Of a) m r -> Stream (Of b) m r+> S.map f = maps (mapOf f)+ + This rests on recognizing that @mapOf@ is a natural transformation; note though+ that it results in such a transformation as well:+ +> S.map :: (a -> b) -> Stream (Of a) m ~> Stream (Of b) m + ++-} lazily :: Of a b -> (a,b) lazily = \(a:>b) -> (a,b) {-# INLINE lazily #-}@@ -215,7 +251,8 @@ snd' :: Of a b -> b snd' (a :> b) = b -+mapOf :: (a -> b) -> Of a r -> Of b r+mapOf f (a:> b) = (f a :> b) {-| Break a sequence when a element falls under a predicate, keeping the rest of the stream as the return value. @@ -1178,10 +1215,11 @@ >>> S.sum $ do {yield 1; yield 2} 3 ->>> S.sum $ do {yield 1; lift $ putStrLn "# 1 was yielded"; yield 2; lift $ putStrLn "# 2 was yielded"}-# 1 was yielded-# 2 was yielded+>>> S.sum $ do {yield 1; lift $ putStrLn "/* 1 was yielded */"; yield 2; lift $ putStrLn "/* 2 was yielded */"}+/* 1 was yielded */+/* 2 was yielded */ 3+ >>> let prompt :: IO Int; prompt = putStrLn "Enter a number:" >> readLn >>> S.sum $ do {lift prompt >>= yield ; lift prompt >>= yield ; lift prompt >>= yield}
streaming.cabal view
@@ -1,10 +1,12 @@ name: streaming-version: 0.1.0.16+version: 0.1.0.17 cabal-version: >=1.10 build-type: Simple synopsis: a free monad transformer optimized for streaming applications -description: @Stream@ can be used wherever +description: __The free stream on a streamable functor__+ .+ @Stream@ can be used wherever <https://hackage.haskell.org/package/free-4.12.1/docs/Control-Monad-Trans-Free.html FreeT> is used. The compiler's standard range of optimizations work better for operations @@ -42,6 +44,57 @@ Somehow, we didn't even need a four-character operator for that, nor advice about best practices! - just ordinary Haskell common sense. .+ __Didn't I hear that free monads are horrible?__+ .+ If @Stream f m r@ is instantiated to @Stream f Identity m r@ then we have+ the standard /free monad construction/. This is subject to certain familiar+ objections from an efficiency perspective; efforts have been made to+ substitute exotic cps-ed implementations and so forth. + .+ In fact, the standard fast talk about /retraversing binds/ and /quadratic explosions/ and+ /costly appends/, and so on become transparent nonsense with @Stream f m r@ + in its streaming use. The insight needed to see this is basically nil: Where @m@ is read as+ @IO@, or some transformed @IO@, then the dreaded /retraversing of the binds/ + in a stream expression would involve repeating all the past actions. Don't worry, to get e.g. the+ second chunk of bytes from a handle, you won't need to start over and get the first+ one again! The first chunk has vanished into an unrepeatable past.+ .+ All of the difficulties a streaming library is attempting to avoid+ are concentrated in the deep irrationality of+ .+ > sequence :: (Monad m, Traversable t) => t (m a) -> m (t a)+ .+ In the streaming context, this becomes + .+ > sequence :: Monad m, Functor f => Stream f m r -> Stream f m r+ > sequence = id+ .+ It is of course easy enough to define+ .+ > accumulate :: Monad m, Functor f => Stream f m r -> m (Stream f Identity r)+ .+ or @reifyBinds@, as you might call it. Small experience will+ teach the user how to avoid or control the sort of accumulation + characteristic of @sequence@ in its various guises e.g. @mapM f = sequence . map f@ and + @traverse f = sequence . fmap f@ and @replicateM n = sequence . replicate n@. + See for example the types of + .+ > Control.Monad.replicateM :: Int -> m a -> m [a]+ > Streaming.Prelude.replicateM :: Int -> m a -> Stream (Of a) m ()+ .+ If you want to tempt fate and replicate the irrationality of @Control.Monad.replicateM@, + then sure, you can write the hermaphroditic, chimerical+ .+ > accumulate . Streaming.Prelude.replicateM :: Int -> m a -> m (Stream (Of a) Identity ())+ .+ but once you know how to operate with a stream directly you will see less and less point+ in what is called /extracting the (structured) value from IO/. With @sequence@ and @traverse@,+ we accumulate a structure holding pure values from a structure holding monadic + values. Why bother when you have intrinsically monadic structures? @Stream f m r@ + gives you an immense body of such structures and a simple discipline for working with them.+ .+ __Interoperation with the streaming-io libraries__+ . The simplest form of interoperation with <http://hackage.haskell.org/package/pipes pipes> is accomplished with this isomorphism:@@ -70,6 +123,8 @@ > Free.iterTM Stream.wrap :: FreeT f m a -> Stream f m a > Stream.iterTM Free.wrap :: Stream f m a -> FreeT f m a .+ __Examples__+ . For some simple ghci examples, see the commentary throughout the Prelude module. For slightly more advanced usage see the commentary in the haddocks of <https://hackage.haskell.org/package/streaming-bytestring streaming-bytestring> and e.g. @@ -77,6 +132,12 @@ the io-streams tutorial. Here's a simple <https://gist.github.com/michaelt/2dcea1ba32562c091357 streaming GET request> with intrinsically streaming byte streams.+ .+ __Problems__+ .+ Questions about this library can be put as issues through the github site or+ on the <https://groups.google.com/forum/#!forum/haskell-pipes pipes mailing list>. + (This library understands itself as part of the pipes "ecosystem.") license: BSD3@@ -86,7 +147,7 @@ stability: Experimental homepage: https://github.com/michaelt/streaming bug-reports: https://github.com/michaelt/streaming/issues-category: Data, Pipes+category: Data, Pipes, Streaming source-repository head type: git location: https://github.com/michaelt/streaming@@ -107,7 +168,7 @@ build-depends: base >=4.6 && <5 , mtl >=2.1 && <2.3 , mmorph >=1.0 && <1.2- , transformers >=0.3 && <0.5+ , transformers >=0.4 && <0.5 default-language: Haskell2010