quiver-enumerator (empty) → 0.0.0.1
raw patch · 4 files changed
+241/−0 lines, 4 filesdep +basedep +enumeratordep +quiversetup-changed
Dependencies added: base, enumerator, quiver
Files
- LICENSE +28/−0
- Setup.hs +3/−0
- quiver-enumerator.cabal +51/−0
- src/Control/Quiver/Enumerator.lhs +159/−0
+ LICENSE view
@@ -0,0 +1,28 @@++ Bridge between Quiver and Iteratee paradigms+ ============================================++ Copyright © 2015 Patryk Zadarnowski «pat@jantar.org».+ All rights reserved.++ Redistribution and use in source and binary forms, with or without+ modification, are permitted provided that the following conditions+ are met: ➀ Redistributions of source code must retain the above+ copyright notice, this list of conditions and the following disclaimer.+ ➁ Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution. ➂ The name of any+ author may not be used to endorse or promote products derived from this+ software without their specific prior written permission.++ THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY+ AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL+ THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS;+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Setup.hs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ quiver-enumerator.cabal view
@@ -0,0 +1,51 @@+name: quiver-enumerator+version: 0.0.0.1+synopsis: Bridge between Quiver and Iteratee paradigms+homepage: https://github.com/zadarnowski/quiver-enumerator+category: Control+stability: alpha++author: Patryk Zadarnowski+maintainer: Patryk Zadarnowski <pat@jantar.org>++copyright: Copyright (c) 2015 Patryk Zadarnowski++description:++ This library defines a set of functions that convert between+ the "Quiver" and "Data.Enumerator" paradigms. The conversion+ is bidirectional: an appropriately-typed stream processor+ can be converted into an 'Data.Enumerator.Iteratee' and back+ into a stream processor. In addition, a stream processor can+ be fed into an iteratee (or 'Data.Enumerator.Step'),+ resulting in an 'Data.Enumerator.Enumerator'.++ The library has been designed specifically for use with Snap,+ but I'm sure that many other interesting uses of it exist.++cabal-version: >= 1.18+build-type: Simple+license: BSD3+license-file: LICENSE++source-repository head+ type: git+ location: https://github.com/zadarnowski/quiver-enumerator.git++source-repository this+ type: git+ location: https://github.com/zadarnowski/quiver-enumerator.git+ tag: 0.0.0.1++library+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-missing-signatures++ exposed-modules:+ Control.Quiver.Enumerator++ build-depends:+ base >= 4.8 && < 5,+ enumerator >= 0.4.20,+ quiver >= 0.0.0.11
+ src/Control/Quiver/Enumerator.lhs view
@@ -0,0 +1,159 @@+> -- | Module: Control.Quiver.Enumerator+> -- Description: A bidirectional bridge between pipes and iteratees+> -- Copyright: © 2015 Patryk Zadarnowski <pat@jantar.org>+> -- License: BSD3+> -- Maintainer: pat@jantar.org+> -- Stability: experimental+> -- Portability: portable+> --+> -- This library defines a set of functions that convert between+> -- the "Quiver" and "Data.Enumerator" paradigms. The conversion+> -- is bidirectional: an appropriately-typed stream processor+> -- can be converted into an 'Data.Enumerator.Iteratee' and back+> -- into a stream processor. In addition, a stream processor can+> -- be fed into an iteratee (or 'Data.Enumerator.Step'),+> -- resulting in 'Data.Enumerator.Enumerator'.+> --+> -- The library has been designed specifically for use with Snap,+> -- but I'm sure that many other interesting uses of it exist.++> {-# LANGUAGE RankNTypes #-}++> module Control.Quiver.Enumerator (+> fromStream, toStream, toSingletonStream,+> iterateeToConsumer,+> stepToConsumer,+> consumerToIteratee,+> processorToEnumerator,+> ) where++> import Control.Exception (SomeException)+> import qualified Control.Quiver.Internal as Q+> import qualified Data.Enumerator as E+++ Data Type Reference+ ===================++ Definitions of the relevant Quiver and Iteratee types, for reference:++ data Q.P a a' b b' f r =+ Consume a (a' -> Q.P a a' b b' f r) (Q.Producer b b' f r)+ | Produce b (b' -> Q.P a a' b b' f r) (Q.Consumer a a' f r)+ | Enclose (f (Q.P a a' b b' f r))+ | Deliver r++ type E.Enumerator a m b = E.Step a m b -> E.Iteratee a m b++ data E.Step a m b =+ E.Continue (E.Stream a -> E.Iteratee a m b)+ | E.Yield b (E.Stream a)+ | E.Error SomeException++ newtype E.Iteratee a m b = E.Iteratee { E.runIteratee :: m (E.Step a m b) }++ data E.Stream a =+ E.Chunks [a]+ | E.EOF++> -- | Converts a 'E.Stream' to an optional list.+> fromStream :: E.Stream a -> Maybe [a]+> fromStream (E.Chunks cs) = Just cs+> fromStream (E.EOF) = Nothing++> -- | Converts an optional list to a 'E.Stream'.+> toStream :: Maybe [a] -> E.Stream a+> toStream (Just cs) = E.Chunks cs+> toStream (Nothing) = E.EOF++> -- | Converts an optional value to a singleton 'E.Stream'.+> toSingletonStream :: Maybe a -> E.Stream a+> toSingletonStream (Just c) = E.Chunks [c]+> toSingletonStream (Nothing) = E.EOF++> -- | Converts an 'E.Iteratee' into a 'Q.Consumer'.+> iterateeToConsumer :: Functor m => E.Iteratee a m r -> Q.Consumer () a m (Either SomeException (r, Maybe [a]))+> iterateeToConsumer = Q.Enclose . fmap stepToConsumer . E.runIteratee++> -- | Converts a 'E.Step' into a 'Q.Consumer'.+> stepToConsumer :: Functor m => E.Step a m r -> Q.Consumer () a m (Either SomeException (r, Maybe [a]))+> stepToConsumer (E.Continue ik) = Q.consume () (iterateeToConsumer . ik . toSingletonStream . Just)+> (Q.decouple $ iterateeToConsumer $ ik $ E.EOF)+> stepToConsumer (E.Yield r xs) = Q.deliver (Right (r, fromStream xs))+> stepToConsumer (E.Error e) = Q.deliver (Left e)++> -- | Converts a 'Q.Consumer' into an 'E.Iteratee'.+> consumerToIteratee :: Monad m => Q.Consumer a a' m r -> E.Iteratee a' m r+> consumerToIteratee = convert1 []+> where+> convert1 cs (Q.Consume _ k q) = convert2 k q cs+> convert1 cs (Q.Produce _ _ q) = convert1 cs q+> convert1 cs (Q.Enclose m) = E.Iteratee (fmap (convert1 cs) m >>= E.runIteratee)+> convert1 cs (Q.Deliver r) = E.yield r (E.Chunks cs)+> convert2 k _ (c:cs') = convert1 cs' (k c)+> convert2 k q [] = E.continue (convert3 k q)+> convert3 k q (E.Chunks cs) = convert2 k q cs+> convert3 _ q (E.EOF) = convert4 q+> convert4 (Q.Consume _ _ q) = convert4 q+> convert4 (Q.Produce _ _ q) = convert4 q+> convert4 (Q.Enclose m) = E.Iteratee (fmap convert4 m >>= E.runIteratee)+> convert4 (Q.Deliver r) = E.yield r E.EOF++> -- | Feed the output of a stream processor to a 'E.Step', effectively converting it into+> -- an 'E.Enumerator', generalised slightly to allow distinct input and output types.+> -- The chunks of the input stream are fed into the stream processor one element at+> -- the time, and its output is fed to the iteratee one element per chunk.+> processorToEnumerator :: Monad m => Q.P a a' b () m r1 -> E.Step b m r2 -> E.Iteratee a' m (r1, r2)+> processorToEnumerator = convert1 []+> where++ Advance the iteratee until it blocks requesting more input:++> convert1 cs p (E.Continue ks) = convert2 cs ks p+> convert1 cs p s = convert3 cs s p++ Advance the stream processor until it produces an input element for a blocked iteratee:++> convert2 cs ks (Q.Consume _ k q) = convert2c ks k q cs+> convert2 cs ks (Q.Produce y k _) = ks (E.Chunks [y]) E.>>== convert1 cs (k ())+> convert2 cs ks (Q.Enclose m) = E.Iteratee (fmap (convert2 cs ks) m >>= E.runIteratee)+> convert2 cs ks (Q.Deliver r1) = ks (E.EOF) E.>>== finish r1 (E.Chunks cs)++> convert2c ks k _ (c:cs') = convert2 cs' ks (k c)+> convert2c ks k q [] = E.continue (maybe (convert2' ks q) (convert2c ks k q) . fromStream)++ Advance the stream processor, once the iteratee has decoupled:++> convert3 cs s (Q.Consume _ k q) = convert3c s k q cs+> convert3 cs s (Q.Produce _ _ q) = convert3 cs s q+> convert3 cs s (Q.Enclose m) = E.Iteratee (fmap (convert3 cs s) m >>= E.runIteratee)+> convert3 cs s (Q.Deliver r1) = finish r1 (E.Chunks cs) s++> convert3c s k _ (c:cs') = convert3 cs' s (k c)+> convert3c s k q [] = E.continue (maybe (convert3' s q) (convert3c s k q) . fromStream)++ Same as @convert1@, but for a decoupled stream processor:++> convert1' p (E.Continue ks) = convert2' ks p+> convert1' p s = convert3' s p++ Same as @convert2@, but for a decoupled stream processor:++> convert2' ks (Q.Consume _ _ q) = convert2' ks q+> convert2' ks (Q.Produce y k _) = ks (E.Chunks [y]) E.>>== convert1' (k ())+> convert2' ks (Q.Enclose m) = E.Iteratee (fmap (convert2' ks) m >>= E.runIteratee)+> convert2' ks (Q.Deliver r1) = ks (E.EOF) E.>>== finish r1 E.EOF++ Same as @convert3@, but for a decoupled stream processor:++> convert3' s (Q.Consume _ _ q) = convert3' s q+> convert3' s (Q.Produce _ _ q) = convert3' s q+> convert3' s (Q.Enclose m) = E.Iteratee (fmap (convert3' s) m >>= E.runIteratee)+> convert3' s (Q.Deliver r1) = finish r1 E.EOF s++ Convert the result of a final step; it must be 'Yield' or 'Error',+ as iteratees aren't allowed to block after receiving an EOF:++> finish r1 xs (E.Yield r2 _) = E.yield (r1, r2) xs+> finish _ _ (E.Error e) = E.throwError e+> finish _ _ (E.Continue _) = error "divergent iteratee"