pipes-text 0.0.0.11 → 0.0.0.12
raw patch · 5 files changed
+456/−513 lines, 5 filesdep −profunctorsdep ~pipes-bytestring
Dependencies removed: profunctors
Dependency ranges changed: pipes-bytestring
Files
- Pipes/Text.hs +148/−501
- Pipes/Text/Encoding.hs +4/−8
- Pipes/Text/Tutorial.hs +295/−0
- changelog +6/−0
- pipes-text.cabal +3/−4
Pipes/Text.hs view
@@ -1,25 +1,11 @@ {-# LANGUAGE RankNTypes, TypeFamilies, BangPatterns, Trustworthy #-} +{-| The module @Pipes.Text@ closely follows @Pipes.ByteString@ from + the @pipes-bytestring@ package. A draft tutorial can be found in+ @Pipes.Text.Tutorial@. +-} module Pipes.Text (- -- * Effectful Text- -- $intro- - -- * Lenses- -- $lenses- - -- ** @view@ \/ @(^.)@- -- $view-- -- ** @over@ \/ @(%~)@- -- $over- - -- ** @zoom@- -- $zoom- - -- * Special types: @Producer Text m (Producer Text m r)@ and @FreeT (Producer Text m) m r@- -- $special- -- * Producers fromLazy @@ -27,17 +13,13 @@ , map , concatMap , take- , drop , takeWhile- , dropWhile , filter- , scan- , pack- , unpack , toCaseFold , toLower , toUpper , stripStart+ , scan -- * Folds , toLazy@@ -53,7 +35,6 @@ , minimum , find , index- , count -- * Primitive Character Parsers , nextChar@@ -62,7 +43,7 @@ , peekChar , isEndOfChars - -- * Parsing Lenses + -- * Parsing Lenses , splitAt , span , break@@ -71,34 +52,34 @@ , word , line - -- * FreeT Splitters+ -- * Transforming Text and Character Streams+ , drop+ , dropWhile+ , pack+ , unpack+ , intersperse++ -- * FreeT Transformations , chunksOf , splitsWith , splits , groupsBy , groups , lines- , words-- -- * Transformations- , intersperse- , packChars- - -- * Joiners- , intercalate , unlines+ , words , unwords+ , intercalate -- * Re-exports -- $reexports , module Data.ByteString , module Data.Text- , module Data.Profunctor , module Pipes.Parse , module Pipes.Group ) where -import Control.Applicative ((<*)) +import Control.Applicative ((<*)) import Control.Monad (liftM, join) import Control.Monad.Trans.State.Strict (StateT(..), modify) import qualified Data.Text as T@@ -107,14 +88,12 @@ import Data.ByteString (ByteString) import Data.Functor.Constant (Constant(Constant, getConstant)) import Data.Functor.Identity (Identity)-import Data.Profunctor (Profunctor)-import qualified Data.Profunctor+ import Pipes-import Pipes.Group (concats, intercalates, FreeT(..), FreeF(..))+import Pipes.Group (folds, maps, concats, intercalates, FreeT(..), FreeF(..)) import qualified Pipes.Group as PG import qualified Pipes.Parse as PP import Pipes.Parse (Parser)-import Pipes.Text.Encoding (Lens'_, Iso'_) import qualified Pipes.Prelude as P import Data.Char (isSpace) import Data.Word (Word8)@@ -149,364 +128,27 @@ words, writeFile ) -{- $intro- This package provides @pipes@ utilities for /text streams/ or /character streams/, - realized as streams of 'Text' chunks. The individual chunks are uniformly /strict/, - and thus you will generally want @Data.Text@ in scope. But the type - @Producer Text m r@ ,as we are using it, is a sort of /pipes/ equivalent of the lazy @Text@ type. - - This particular module provides many functions equivalent in one way or another to - the pure functions in - <https://hackage.haskell.org/package/text-1.1.0.0/docs/Data-Text-Lazy.html Data.Text.Lazy>. - They transform, divide, group and fold text streams. Though @Producer Text m r@ - is the type of \'effectful Text\', the functions in this module are \'pure\' - in the sense that they are uniformly monad-independent.- Simple /IO/ operations are defined in @Pipes.Text.IO@ -- as lazy IO @Text@ - operations are in @Data.Text.Lazy.IO@. Inter-operation with @ByteString@ - is provided in @Pipes.Text.Encoding@, which parallels @Data.Text.Lazy.Encoding@. - The Text type exported by @Data.Text.Lazy@ is basically that of a lazy list of - strict Text: the implementation is arranged so that the individual strict 'Text' - chunks are kept to a reasonable size; the user is not aware of the divisions - between the connected 'Text' chunks. - So also here: the functions in this module are designed to operate on streams that- are insensitive to text boundaries. This means that they may freely split- text into smaller texts and /discard empty texts/. The objective, though, is - that they should /never concatenate texts/ in order to provide strict upper - bounds on memory usage. -- For example, to stream only the first three lines of 'stdin' to 'stdout' you- might write:--> import Pipes-> import qualified Pipes.Text as Text-> import qualified Pipes.Text.IO as Text-> import Pipes.Group (takes')-> import Lens.Family -> -> main = runEffect $ takeLines 3 Text.stdin >-> Text.stdout-> where -> takeLines n = Text.unlines . takes' n . view Text.lines-- The above program will never bring more than one chunk of text (~ 32 KB) into- memory, no matter how long the lines are.---}-{- $lenses- As this example shows, one superficial difference from @Data.Text.Lazy@ - is that many of the operations, like 'lines', are \'lensified\'; this has a - number of advantages (where it is possible); in particular it facilitates their - use with 'Parser's of Text (in the general <http://hackage.haskell.org/package/pipes-parse-3.0.1/docs/Pipes-Parse-Tutorial.html pipes-parse> - sense.) The disadvantage, famously, is that the messages you get for type errors can be- a little alarming. The remarks that follow in this section are for non-lens adepts.-- Each lens exported here, e.g. 'lines', 'chunksOf' or 'splitAt', reduces to the - intuitively corresponding function when used with @view@ or @(^.)@. Instead of- writing:- - > splitAt 17 producer- - as we would with the Prelude or Text functions, we write - - > view (splitAt 17) producer- - or equivalently- - > producer ^. splitAt 17-- This may seem a little indirect, but note that many equivalents of - @Text -> Text@ functions are exported here as 'Pipe's. Here too we recover the intuitively - corresponding functions by prefixing them with @(>->)@. Thus something like--> stripLines = Text.unlines . Group.maps (>-> Text.stripStart) . view Text.lines -- would drop the leading white space from each line. -- The lenses in this library are marked as /improper/; this just means that - they don't admit all the operations of an ideal lens, but only /getting/ and /focusing/. - Just for this reason, though, the magnificent complexities of the lens libraries - are a distraction. The lens combinators to keep in mind, the ones that make sense for - our lenses, are @view@ \/ @(^.)@), @over@ \/ @(%~)@ , and @zoom@. -- One need only keep in mind that if @l@ is a @Lens'_ a b@, then:---}-{- $view- @view l@ is a function @a -> b@ . Thus @view l a@ (also written @a ^. l@ ) - is the corresponding @b@; as was said above, this function will be exactly the - function you think it is, given its name. Thus to uppercase the first n characters - of a Producer, leaving the rest the same, we could write: --- > upper n p = do p' <- p ^. Text.splitAt n >-> Text.toUpper- > p'--}-{- $over- @over l@ is a function @(b -> b) -> a -> a@. Thus, given a function that modifies- @b@s, the lens lets us modify an @a@ by applying @f :: b -> b@ to - the @b@ that we can \"see\" through the lens. So @over l f :: a -> a@ - (it can also be written @l %~ f@). - For any particular @a@, then, @over l f a@ or @(l %~ f) a@ is a revised @a@. - So above we might have written things like these: -- > stripLines = Text.lines %~ maps (>-> Text.stripStart)- > stripLines = over Text.lines (maps (>-> Text.stripStart))- > upper n = Text.splitAt n %~ (>-> Text.toUpper)---}-{- $zoom- @zoom l@, finally, is a function from a @Parser b m r@ - to a @Parser a m r@ (or more generally a @StateT (Producer b m x) m r@). - Its use is easiest to see with an decoding lens like 'utf8', which- \"sees\" a Text producer hidden inside a ByteString producer:- @drawChar@ is a Text parser, returning a @Maybe Char@, @zoom utf8 drawChar@ is - a /ByteString/ parser, returning a @Maybe Char@. @drawAll@ is a Parser that returns - a list of everything produced from a Producer, leaving only the return value; it would - usually be unreasonable to use it. But @zoom (splitAt 17) drawAll@- returns a list of Text chunks containing the first seventeen Chars, and returns the rest of- the Text Producer for further parsing. Suppose that we want, inexplicably, to - modify the casing of a Text Producer according to any instruction it might - contain at the start. Then we might write something like this:--> obey :: Monad m => Producer Text m b -> Producer Text m b-> obey p = do (ts, p') <- lift $ runStateT (zoom (Text.splitAt 7) drawAll) p-> let seven = T.concat ts-> case T.toUpper seven of -> "TOUPPER" -> p' >-> Text.toUpper-> "TOLOWER" -> p' >-> Text.toLower-> _ -> do yield seven-> p'---> >>> let doc = each ["toU","pperTh","is document.\n"]-> >>> runEffect $ obey doc >-> Text.stdout-> THIS DOCUMENT.-- The purpose of exporting lenses is the mental economy achieved with this three-way - applicability. That one expression, e.g. @lines@ or @splitAt 17@ can have these - three uses is no more surprising than that a pipe can act as a function modifying - the output of a producer, namely by using @>->@ to its left: @producer >-> pipe@- -- but can /also/ modify the inputs to a consumer by using @>->@ to its right: - @pipe >-> consumer@-- The three functions, @view@ \/ @(^.)@, @over@ \/ @(%~)@ and @zoom@ are supplied by - both <http://hackage.haskell.org/package/lens lens> and - <http://hackage.haskell.org/package/lens-family lens-family> The use of 'zoom' is explained- in <http://hackage.haskell.org/package/pipes-parse-3.0.1/docs/Pipes-Parse-Tutorial.html Pipes.Parse.Tutorial> - and to some extent in the @Pipes.Text.Encoding@ module here. ---}-{- $special- These simple 'lines' examples reveal a more important difference from @Data.Text.Lazy@ . - This is in the types that are most closely associated with our central text type, - @Producer Text m r@. In @Data.Text@ and @Data.Text.Lazy@ we find functions like--> splitAt :: Int -> Text -> (Text, Text)-> lines :: Text -> [Text]-> chunksOf :: Int -> Text -> [Text]-- which relate a Text with a pair of Texts or a list of Texts. - The corresponding functions here (taking account of \'lensification\') are --> view . splitAt :: (Monad m, Integral n) => n -> Producer Text m r -> Producer Text m (Producer Text m r)-> view lines :: Monad m => Producer Text m r -> FreeT (Producer Text m) m r-> view . chunksOf :: (Monad m, Integral n) => n -> Producer Text m r -> FreeT (Producer Text m) m r-- Some of the types may be more readable if you imagine that we have introduced- our own type synonyms--> type Text m r = Producer T.Text m r-> type Texts m r = FreeT (Producer T.Text m) m r-- Then we would think of the types above as--> view . splitAt :: (Monad m, Integral n) => n -> Text m r -> Text m (Text m r)-> view lines :: (Monad m) => Text m r -> Texts m r-> view . chunksOf :: (Monad m, Integral n) => n -> Text m r -> Texts m r-- which brings one closer to the types of the similar functions in @Data.Text.Lazy@-- In the type @Producer Text m (Producer Text m r)@ the second - element of the \'pair\' of effectful Texts cannot simply be retrieved - with something like 'snd'. This is an \'effectful\' pair, and one must work - through the effects of the first element to arrive at the second Text stream, even- if you are proposing to throw the Text in the first element away. - Note that we use Control.Monad.join to fuse the pair back together, since it specializes to --> join :: Monad m => Producer Text m (Producer m r) -> Producer m r-- The return type of 'lines', 'words', 'chunksOf' and the other /splitter/ functions,- @FreeT (Producer m Text) m r@ -- our @Texts m r@ -- is the type of (effectful)- lists of (effectful) texts. The type @([Text],r)@ might be seen to gather- together things of the forms:--> r-> (Text,r)-> (Text, (Text, r))-> (Text, (Text, (Text, r)))-> (Text, (Text, (Text, (Text, r))))-> ...-- (We might also have identified the sum of those types with @Free ((,) Text) r@ - -- or, more absurdly, @FreeT ((,) Text) Identity r@.) - - Similarly, our type @Texts m r@, or @FreeT (Text m) m r@ -- in fact called - @FreeT (Producer Text m) m r@ here -- encompasses all the members of the sequence:- -> m r-> Text m r-> Text m (Text m r)-> Text m (Text m (Text m r))-> Text m (Text m (Text m (Text m r)))-> ...-- We might have used a more specialized type in place of @FreeT (Producer a m) m r@,- or indeed of @FreeT (Producer Text m) m r@, but it is clear that the correct- result type of 'lines' will be isomorphic to @FreeT (Producer Text m) m r@ . -- One might think that --> lines :: Monad m => Lens'_ (Producer Text m r) (FreeT (Producer Text m) m r)-> view . lines :: Monad m => Producer Text m r -> FreeT (Producer Text m) m r-- should really have the type- -> lines :: Monad m => Pipe Text Text m r-- as e.g. 'toUpper' does. But this would spoil the control we are - attempting to maintain over the size of chunks. It is in fact just - as unreasonable to want such a pipe as to want--> Data.Text.Lazy.lines :: Text -> Text -- to 'rechunk' the strict Text chunks inside the lazy Text to respect - line boundaries. In fact we have --> Data.Text.Lazy.lines :: Text -> [Text]-> Prelude.lines :: String -> [String]-- where the elements of the list are themselves lazy Texts or Strings; the use- of @FreeT (Producer Text m) m r@ is simply the 'effectful' version of this. - - The @Pipes.Group@ module, which can generally be imported without qualification,- provides many functions for working with things of type @FreeT (Producer a m) m r@.- In particular it conveniently exports the constructors for @FreeT@ and the associated- @FreeF@ type -- a fancy form of @Either@, namely - -> data FreeF f a b = Pure a | Free (f b)-- for pattern-matching. Consider the implementation of the 'words' function, or - of the part of the lens that takes us to the words; it is compact but exhibits many - of the points under discussion, including explicit handling of the @FreeT@ and @FreeF@- constuctors. Keep in mind that --> newtype FreeT f m a = FreeT (m (FreeF f a (FreeT f m a)))-> next :: Monad m => Producer a m r -> m (Either r (a, Producer a m r))-- Thus the @do@ block after the @FreeT@ constructor is in the base monad, e.g. 'IO' or 'Identity';- the later subordinate block, opened by the @Free@ constructor, is in the @Producer@ monad:--> words :: Monad m => Producer Text m r -> FreeT (Producer Text m) m r-> words p = FreeT $ do -- With 'next' we will inspect p's first chunk, excluding spaces;-> x <- next (p >-> dropWhile isSpace) -- note that 'dropWhile isSpace' is a pipe, and is thus *applied* with '>->'.-> return $ case x of -- We use 'return' and so need something of type 'FreeF (Text m) r (Texts m r)'-> Left r -> Pure r -- 'Left' means we got no Text chunk, but only the return value; so we are done.-> Right (txt, p') -> Free $ do -- If we get a chunk and the rest of the producer, p', we enter the 'Producer' monad-> p'' <- view (break isSpace) -- When we apply 'break isSpace', we get a Producer that returns a Producer;-> (yield txt >> p') -- so here we yield everything up to the next space, and get the rest back.-> return (words p'') -- We then carry on with the rest, which is likely to begin with space.- --}- -- | Convert a lazy 'TL.Text' into a 'Producer' of strict 'Text's fromLazy :: (Monad m) => TL.Text -> Producer' Text m ()-fromLazy = TL.foldrChunks (\e a -> yield e >> a) (return ()) +fromLazy = TL.foldrChunks (\e a -> yield e >> a) (return ()) {-# INLINE fromLazy #-} - (^.) :: a -> ((b -> Constant b b) -> (a -> Constant b a)) -> b a ^. lens = getConstant (lens Constant a) - -- | Apply a transformation to each 'Char' in the stream map :: (Monad m) => (Char -> Char) -> Pipe Text Text m r map f = P.map (T.map f) {-# INLINABLE map #-} -{-# RULES "p >-> map f" forall p f .- p >-> map f = for p (\txt -> yield (T.map f txt))- #-}- -- | Map a function over the characters of a text stream and concatenate the results concatMap :: (Monad m) => (Char -> Text) -> Pipe Text Text m r concatMap f = P.map (T.concatMap f) {-# INLINABLE concatMap #-} -{-# RULES "p >-> concatMap f" forall p f .- p >-> concatMap f = for p (\txt -> yield (T.concatMap f txt))- #-}----- | Transform a Pipe of 'String's into one of 'Text' chunks-pack :: Monad m => Pipe String Text m r-pack = P.map T.pack-{-# INLINEABLE pack #-}--{-# RULES "p >-> pack" forall p .- p >-> pack = for p (\txt -> yield (T.pack txt))- #-}---- | Transform a Pipes of 'Text' chunks into one of 'String's-unpack :: Monad m => Pipe Text String m r-unpack = for cat (\t -> yield (T.unpack t))-{-# INLINEABLE unpack #-}--{-# RULES "p >-> unpack" forall p .- p >-> unpack = for p (\txt -> yield (T.unpack txt))- #-}---- | @toCaseFold@, @toLower@, @toUpper@ and @stripStart@ are standard 'Text' utilities, --- here acting as 'Text' pipes, rather as they would on a lazy text-toCaseFold :: Monad m => Pipe Text Text m r-toCaseFold = P.map T.toCaseFold-{-# INLINEABLE toCaseFold #-}--{-# RULES "p >-> toCaseFold" forall p .- p >-> toCaseFold = for p (\txt -> yield (T.toCaseFold txt))- #-}----- | lowercase incoming 'Text'-toLower :: Monad m => Pipe Text Text m r-toLower = P.map T.toLower-{-# INLINEABLE toLower #-}--{-# RULES "p >-> toLower" forall p .- p >-> toLower = for p (\txt -> yield (T.toLower txt))- #-}---- | uppercase incoming 'Text'-toUpper :: Monad m => Pipe Text Text m r-toUpper = P.map T.toUpper-{-# INLINEABLE toUpper #-}--{-# RULES "p >-> toUpper" forall p .- p >-> toUpper = for p (\txt -> yield (T.toUpper txt))- #-}---- | Remove leading white space from an incoming succession of 'Text's -stripStart :: Monad m => Pipe Text Text m r-stripStart = do- chunk <- await- let text = T.stripStart chunk- if T.null text- then stripStart- else do yield text - cat-{-# INLINEABLE stripStart #-}---- | @(take n)@ only allows @n@ individual characters to pass; +-- | @(take n)@ only allows @n@ individual characters to pass; -- contrast @Pipes.Prelude.take@ which would let @n@ chunks pass. take :: (Monad m, Integral a) => a -> Pipe Text Text m () take n0 = go n0 where@@ -522,21 +164,6 @@ go (n - len) {-# INLINABLE take #-} --- | @(drop n)@ drops the first @n@ characters-drop :: (Monad m, Integral a) => a -> Pipe Text Text m r-drop n0 = go n0 where- go n- | n <= 0 = cat- | otherwise = do- txt <- await- let len = fromIntegral (T.length txt)- if (len >= n)- then do- yield (T.drop (fromIntegral n) txt)- cat- else go (n - len)-{-# INLINABLE drop #-}- -- | Take characters until they fail the predicate takeWhile :: (Monad m) => (Char -> Bool) -> Pipe Text Text m () takeWhile predicate = go@@ -551,27 +178,11 @@ else yield prefix {-# INLINABLE takeWhile #-} --- | Drop characters until they fail the predicate-dropWhile :: (Monad m) => (Char -> Bool) -> Pipe Text Text m r-dropWhile predicate = go where- go = do- txt <- await- case T.findIndex (not . predicate) txt of- Nothing -> go- Just i -> do- yield (T.drop i txt)- cat-{-# INLINABLE dropWhile #-}- -- | Only allows 'Char's to pass if they satisfy the predicate filter :: (Monad m) => (Char -> Bool) -> Pipe Text Text m r filter predicate = P.map (T.filter predicate) {-# INLINABLE filter #-} -{-# RULES "p >-> filter q" forall p q .- p >-> filter q = for p (\txt -> yield (T.filter q txt))- #-}- -- | Strict left scan over the characters scan :: (Monad m)@@ -588,6 +199,33 @@ go c' {-# INLINABLE scan #-} +-- | @toCaseFold@, @toLower@, @toUpper@ and @stripStart@ are standard 'Text' utilities,+-- here acting as 'Text' pipes, rather as they would on a lazy text+toCaseFold :: Monad m => Pipe Text Text m r+toCaseFold = P.map T.toCaseFold+{-# INLINEABLE toCaseFold #-}++-- | lowercase incoming 'Text'+toLower :: Monad m => Pipe Text Text m r+toLower = P.map T.toLower+{-# INLINEABLE toLower #-}++-- | uppercase incoming 'Text'+toUpper :: Monad m => Pipe Text Text m r+toUpper = P.map T.toUpper+{-# INLINEABLE toUpper #-}++-- | Remove leading white space from an incoming succession of 'Text's+stripStart :: Monad m => Pipe Text Text m r+stripStart = do+ chunk <- await+ let text = T.stripStart chunk+ if T.null text+ then stripStart+ else do yield text+ cat+{-# INLINEABLE stripStart #-}+ {-| Fold a pure 'Producer' of strict 'Text's into a lazy 'TL.Text' -}@@ -613,6 +251,7 @@ foldChars step begin done = P.fold (T.foldl' step) begin done {-# INLINABLE foldChars #-} + -- | Retrieve the first 'Char' head :: (Monad m) => Producer Text m () -> m (Maybe Char) head = go@@ -693,18 +332,13 @@ index :: (Monad m, Integral a) => a-> Producer Text m () -> m (Maybe Char)-index n p = head (p >-> drop n)+index n p = head (drop n p) {-# INLINABLE index #-} --- | Store a tally of how many segments match the given 'Text'-count :: (Monad m, Num n) => Text -> Producer Text m () -> m n-count c p = P.fold (+) 0 id (p >-> P.map (fromIntegral . T.count c))-{-# INLINABLE count #-} - -- | Consume the first character from a stream of 'Text'--- +-- -- 'next' either fails with a 'Left' if the 'Producer' has no more characters or -- succeeds with a 'Right' providing the next character and the remainder of the -- 'Producer'.@@ -780,12 +414,11 @@ Just _-> False ) {-# INLINABLE isEndOfChars #-} - -- | Splits a 'Producer' after the given number of characters splitAt :: (Monad m, Integral n) => n- -> Lens'_ (Producer Text m r)+ -> Lens' (Producer Text m r) (Producer Text m (Producer Text m r)) splitAt n0 k p0 = fmap join (k (go n0 p0)) where@@ -814,7 +447,7 @@ span :: (Monad m) => (Char -> Bool)- -> Lens'_ (Producer Text m r)+ -> Lens' (Producer Text m r) (Producer Text m (Producer Text m r)) span predicate k p0 = fmap join (k (go p0)) where@@ -839,7 +472,7 @@ break :: (Monad m) => (Char -> Bool)- -> Lens'_ (Producer Text m r)+ -> Lens' (Producer Text m r) (Producer Text m (Producer Text m r)) break predicate = span (not . predicate) {-# INLINABLE break #-}@@ -850,7 +483,7 @@ groupBy :: (Monad m) => (Char -> Char -> Bool)- -> Lens'_ (Producer Text m r)+ -> Lens' (Producer Text m r) (Producer Text m (Producer Text m r)) groupBy equals k p0 = fmap join (k ((go p0))) where go p = do@@ -859,22 +492,22 @@ Left r -> return (return r) Right (txt, p') -> case T.uncons txt of Nothing -> go p'- Just (c, _) -> (yield txt >> p') ^. span (equals c) + Just (c, _) -> (yield txt >> p') ^. span (equals c) {-# INLINABLE groupBy #-} -- | Improper lens that splits after the first succession of identical 'Char' s-group :: Monad m - => Lens'_ (Producer Text m r)+group :: Monad m+ => Lens' (Producer Text m r) (Producer Text m (Producer Text m r)) group = groupBy (==) {-# INLINABLE group #-} {-| Improper lens that splits a 'Producer' after the first word - Unlike 'words', this does not drop leading whitespace + Unlike 'words', this does not drop leading whitespace -}-word :: (Monad m) - => Lens'_ (Producer Text m r)+word :: (Monad m)+ => Lens' (Producer Text m r) (Producer Text m (Producer Text m r)) word k p0 = fmap join (k (to p0)) where@@ -883,15 +516,28 @@ p'^.break isSpace {-# INLINABLE word #-} --line :: (Monad m) - => Lens'_ (Producer Text m r)+line :: (Monad m)+ => Lens' (Producer Text m r) (Producer Text m (Producer Text m r)) line = break (== '\n')- {-# INLINABLE line #-} +-- | @(drop n)@ drops the first @n@ characters+drop :: (Monad m, Integral n)+ => n -> Producer Text m r -> Producer Text m r+drop n p = do+ p' <- lift $ runEffect (for (p ^. splitAt n) discard)+ p'+{-# INLINABLE drop #-} +-- | Drop characters until they fail the predicate+dropWhile :: (Monad m)+ => (Char -> Bool) -> Producer Text m r -> Producer Text m r+dropWhile predicate p = do+ p' <- lift $ runEffect (for (p ^. span predicate) discard)+ p'+{-# INLINABLE dropWhile #-}+ -- | Intersperse a 'Char' in between the characters of stream of 'Text' intersperse :: (Monad m) => Char -> Producer Text m r -> Producer Text m r@@ -915,30 +561,36 @@ {-# INLINABLE intersperse #-} +-- | Improper lens from unpacked 'Word8's to packaged 'ByteString's+pack :: Monad m => Lens' (Producer Char m r) (Producer Text m r)+pack k p = fmap _unpack (k (_pack p))+{-# INLINABLE pack #-} --- | Improper isomorphism between a 'Producer' of 'ByteString's and 'Word8's-packChars :: Monad m => Iso'_ (Producer Char m x) (Producer Text m x)-packChars = Data.Profunctor.dimap to (fmap from)- where- -- to :: Monad m => Producer Char m x -> Producer Text m x- to p = PG.folds step id done (p^.PG.chunksOf defaultChunkSize)+-- | Improper lens from packed 'ByteString's to unpacked 'Word8's+unpack :: Monad m => Lens' (Producer Text m r) (Producer Char m r)+unpack k p = fmap _pack (k (_unpack p))+{-# INLINABLE unpack #-} - step diffAs c = diffAs . (c:)+_pack :: Monad m => Producer Char m r -> Producer Text m r+_pack p = folds step id done (p^.PG.chunksOf defaultChunkSize)+ where+ step diffAs w8 = diffAs . (w8:) done diffAs = T.pack (diffAs [])+{-# INLINABLE _pack #-} - -- from :: Monad m => Producer Text m x -> Producer Char m x- from p = for p (each . T.unpack)- -{-# INLINABLE packChars #-}+_unpack :: Monad m => Producer Text m r -> Producer Char m r+_unpack p = for p (each . T.unpack)+{-# INLINABLE _unpack #-} defaultChunkSize :: Int defaultChunkSize = 16384 - (sizeOf (undefined :: Int) `shiftL` 1) + -- | Split a text stream into 'FreeT'-delimited text streams of fixed size chunksOf :: (Monad m, Integral n)- => n -> Lens'_ (Producer Text m r) + => n -> Lens' (Producer Text m r) (FreeT (Producer Text m) m r) chunksOf n k p0 = fmap concats (k (FreeT (go p0))) where@@ -947,7 +599,7 @@ return $ case x of Left r -> Pure r Right (txt, p') -> Free $ do- p'' <- (yield txt >> p') ^. splitAt n + p'' <- (yield txt >> p') ^. splitAt n return $ FreeT (go p'') {-# INLINABLE chunksOf #-} @@ -958,8 +610,7 @@ splitsWith :: (Monad m) => (Char -> Bool)- -> Producer Text m r- -> FreeT (Producer Text m) m r+ -> Producer Text m r -> FreeT (Producer Text m) m r splitsWith predicate p0 = FreeT (go0 p0) where go0 p = do@@ -977,17 +628,17 @@ return $ case x of Left r -> Pure r Right (_, p') -> Free $ do- p'' <- p' ^. span (not . predicate) + p'' <- p' ^. span (not . predicate) return $ FreeT (go1 p'') {-# INLINABLE splitsWith #-} -- | Split a text stream using the given 'Char' as the delimiter splits :: (Monad m) => Char- -> Lens'_ (Producer Text m r)+ -> Lens' (Producer Text m r) (FreeT (Producer Text m) m r) splits c k p =- fmap (PG.intercalates (yield (T.singleton c))) (k (splitsWith (c ==) p))+ fmap (intercalates (yield (T.singleton c))) (k (splitsWith (c ==) p)) {-# INLINABLE splits #-} {-| Isomorphism between a stream of 'Text' and groups of equivalent 'Char's , using the@@ -996,8 +647,8 @@ groupsBy :: Monad m => (Char -> Char -> Bool)- -> Lens'_ (Producer Text m x) (FreeT (Producer Text m) m x)-groupsBy equals k p0 = fmap concats (k (FreeT (go p0))) where + -> Lens' (Producer Text m x) (FreeT (Producer Text m) m x)+groupsBy equals k p0 = fmap concats (k (FreeT (go p0))) where go p = do x <- next p case x of Left r -> return (Pure r) Right (bs, p') -> case T.uncons bs of@@ -1011,7 +662,7 @@ -- | Like 'groupsBy', where the equality predicate is ('==') groups :: Monad m- => Lens'_ (Producer Text m x) (FreeT (Producer Text m) m x)+ => Lens' (Producer Text m x) (FreeT (Producer Text m) m x) groups = groupsBy (==) {-# INLINABLE groups #-} @@ -1020,10 +671,19 @@ {-| Split a text stream into 'FreeT'-delimited lines -} lines- :: (Monad m) => Iso'_ (Producer Text m r) (FreeT (Producer Text m) m r)-lines = Data.Profunctor.dimap _lines (fmap _unlines)- where- _lines p0 = FreeT (go0 p0) + :: (Monad m) => Lens' (Producer Text m r) (FreeT (Producer Text m) m r)+lines k p = fmap _unlines (k (_lines p))+{-# INLINABLE lines #-}++unlines+ :: Monad m+ => Lens' (FreeT (Producer Text m) m r) (Producer Text m r)+unlines k p = fmap _lines (k (_unlines p))+{-# INLINABLE unlines #-}++_lines :: Monad m+ => Producer Text m r -> FreeT (Producer Text m) m r+_lines p0 = FreeT (go0 p0) where go0 p = do x <- next p@@ -1040,40 +700,48 @@ case x of Left r -> return $ Pure r Right (_, p'') -> go0 p''- -- _unlines- -- :: Monad m- -- => FreeT (Producer Text m) m x -> Producer Text m x- _unlines = concats . PG.maps (<* yield (T.singleton '\n'))- --{-# INLINABLE lines #-}+{-# INLINABLE _lines #-} +_unlines :: Monad m+ => FreeT (Producer Text m) m r -> Producer Text m r+_unlines = concats . maps (<* yield (T.singleton '\n'))+{-# INLINABLE _unlines #-} --- | Split a text stream into 'FreeT'-delimited words+-- | Split a text stream into 'FreeT'-delimited words. Note that +-- roundtripping with e.g. @over words id@ eliminates extra space+-- characters as with @Prelude.unwords . Prelude.words@ words- :: (Monad m) => Iso'_ (Producer Text m r) (FreeT (Producer Text m) m r)-words = Data.Profunctor.dimap go (fmap _unwords)- where- go p = FreeT $ do- x <- next (p >-> dropWhile isSpace)+ :: (Monad m) => Lens' (Producer Text m r) (FreeT (Producer Text m) m r)+words k p = fmap _unwords (k (_words p))+{-# INLINABLE words #-}++unwords+ :: Monad m+ => Lens' (FreeT (Producer Text m) m r) (Producer Text m r)+unwords k p = fmap _words (k (_unwords p))+{-# INLINABLE unwords #-}++_words :: (Monad m) => Producer Text m r -> FreeT (Producer Text m) m r+_words p = FreeT $ do+ x <- next (dropWhile isSpace p) return $ case x of Left r -> Pure r Right (bs, p') -> Free $ do p'' <- (yield bs >> p') ^. break isSpace- return (go p'')- _unwords = PG.intercalates (yield $ T.singleton ' ')- -{-# INLINABLE words #-}+ return (_words p'')+{-# INLINABLE _words #-} +_unwords :: (Monad m) => FreeT (Producer Text m) m r -> Producer Text m r+_unwords = intercalates (yield $ T.singleton ' ')+{-# INLINABLE _unwords #-} + {-| 'intercalate' concatenates the 'FreeT'-delimited text streams after interspersing a text stream in between them -} intercalate :: (Monad m)- => Producer Text m ()- -> FreeT (Producer Text m) m r- -> Producer Text m r+ => Producer Text m () -> FreeT (Producer Text m) m r -> Producer Text m r intercalate p0 = go0 where go0 f = do@@ -1093,35 +761,14 @@ go1 f' {-# INLINABLE intercalate #-} -{-| Join 'FreeT'-delimited lines into a text stream--}-unlines- :: (Monad m) => FreeT (Producer Text m) m r -> Producer Text m r-unlines = go- where- go f = do- x <- lift (runFreeT f)- case x of- Pure r -> return r- Free p -> do- f' <- p- yield $ T.singleton '\n'- go f'-{-# INLINABLE unlines #-} -{-| Join 'FreeT'-delimited words into a text stream--}-unwords- :: (Monad m) => FreeT (Producer Text m) m r -> Producer Text m r-unwords = intercalate (yield $ T.singleton ' ')-{-# INLINABLE unwords #-} - {- $reexports- + @Data.Text@ re-exports the 'Text' type. - @Pipes.Parse@ re-exports 'input', 'concat', 'FreeT' (the type) and the 'Parse' synonym. + @Pipes.Parse@ re-exports 'input', 'concat', 'FreeT' (the type) and the 'Parse' synonym. -} +type Lens' a b = forall f . Functor f => (b -> f b) -> (a -> f a)
Pipes/Text/Encoding.hs view
@@ -41,13 +41,10 @@ , decodeAscii , encodeIso8859_1 , decodeIso8859_1- , Lens'_- , Iso'_ ) where import Data.Functor.Constant (Constant(..))-import Data.Profunctor (Profunctor) import Data.Char (ord) import Data.ByteString as B import Data.ByteString (ByteString)@@ -61,16 +58,15 @@ import Data.Word (Word8) import Pipes -type Lens'_ a b = forall f . Functor f => (b -> f b) -> (a -> f a)-type Iso'_ a b = forall f p . (Functor f, Profunctor p) => p b (f b) -> p a (f a)+type Lens' a b = forall f . Functor f => (b -> f b) -> (a -> f a) {- $lenses The 'Codec' type is a simple specializion of - the @Lens'_@ type synonymn used by the standard lens libraries, + the @Lens'@ type synonymn used by the standard lens libraries, <http://hackage.haskell.org/package/lens lens> and <http://hackage.haskell.org/package/lens-family lens-family>. That type, -> type Lens'_ a b = forall f . Functor f => (b -> f b) -> (a -> f a)+> type Lens' a b = forall f . Functor f => (b -> f b) -> (a -> f a) is just an alias for a Prelude type. Thus you use any particular codec with the @view@ / @(^.)@ , @zoom@ and @over@ functions from either of those libraries;@@ -81,7 +77,7 @@ type Codec = forall m r . Monad m- => Lens'_ (Producer ByteString m r)+ => Lens' (Producer ByteString m r) (Producer Text m (Producer ByteString m r)) {- | 'decode' is just the ordinary @view@ or @(^.)@ of the lens libraries;
+ Pipes/Text/Tutorial.hs view
@@ -0,0 +1,295 @@+{-# OPTIONS_GHC -fno-warn-unused-imports #-}++module Pipes.Text.Tutorial (+ -- * Effectful Text+ -- $intro++ -- * Lenses+ -- $lenses++ -- ** @view@ \/ @(^.)@+ -- $view++ -- ** @over@ \/ @(%~)@+ -- $over++ -- ** @zoom@+ -- $zoom++ -- * Special types: @Producer Text m (Producer Text m r)@ and @FreeT (Producer Text m) m r@+ -- $special+ ) where+ +import Pipes+import Pipes.Text+import Pipes.Text.IO+import Pipes.Text.Encoding+ +{- $intro+ This package provides @pipes@ utilities for /text streams/ or /character streams/,+ realized as streams of 'Text' chunks. The individual chunks are uniformly /strict/,+ and thus you will generally want @Data.Text@ in scope. But the type+ @Producer Text m r@ ,as we are using it, is a sort of /pipes/ equivalent of the lazy @Text@ type.++ The main @Pipes.Text@ module provides many functions equivalent in one way or another to+ the pure functions in+ <https://hackage.haskell.org/package/text-1.1.0.0/docs/Data-Text-Lazy.html Data.Text.Lazy>.+ They transform, divide, group and fold text streams. Though @Producer Text m r@+ is the type of \'effectful Text\', the functions in this module are \'pure\'+ in the sense that they are uniformly monad-independent.+ Simple /IO/ operations are defined in @Pipes.Text.IO@ -- as lazy IO @Text@+ operations are in @Data.Text.Lazy.IO@. Inter-operation with @ByteString@+ is provided in @Pipes.Text.Encoding@, which parallels @Data.Text.Lazy.Encoding@.++ The Text type exported by @Data.Text.Lazy@ is basically that of a lazy list of+ strict Text: the implementation is arranged so that the individual strict 'Text'+ chunks are kept to a reasonable size; the user is not aware of the divisions+ between the connected 'Text' chunks.+ So also here: the functions in this module are designed to operate on streams that+ are insensitive to text boundaries. This means that they may freely split+ text into smaller texts and /discard empty texts/. The objective, though, is+ that they should /never concatenate texts/ in order to provide strict upper+ bounds on memory usage.++ For example, to stream only the first three lines of 'stdin' to 'stdout' you+ might write:++> import Pipes+> import qualified Pipes.Text as Text+> import qualified Pipes.Text.IO as Text+> import Pipes.Group (takes')+> import Lens.Family+>+> main = runEffect $ takeLines 3 Text.stdin >-> Text.stdout+> where+> takeLines n = Text.unlines . takes' n . view Text.lines++ The above program will never bring more than one chunk of text (~ 32 KB) into+ memory, no matter how long the lines are.++-}+{- $lenses+ As this example shows, one superficial difference from @Data.Text.Lazy@+ is that many of the operations, like 'lines', are \'lensified\'; this has a+ number of advantages (where it is possible); in particular it facilitates their+ use with 'Parser's of Text (in the general <http://hackage.haskell.org/package/pipes-parse-3.0.1/docs/Pipes-Parse-Tutorial.html pipes-parse>+ sense.) The disadvantage, famously, is that the messages you get for type errors can be+ a little alarming. The remarks that follow in this section are for non-lens adepts.++ Each lens exported here, e.g. 'lines', 'chunksOf' or 'splitAt', reduces to the+ intuitively corresponding function when used with @view@ or @(^.)@. Instead of+ writing:++ > splitAt 17 producer++ as we would with the Prelude or Text functions, we write++ > view (splitAt 17) producer++ or equivalently++ > producer ^. splitAt 17++ This may seem a little indirect, but note that many equivalents of+ @Text -> Text@ functions are exported here as 'Pipe's. Here too we recover the intuitively+ corresponding functions by prefixing them with @(>->)@. Thus something like++> stripLines = Text.unlines . Group.maps (>-> Text.stripStart) . view Text.lines++ would drop the leading white space from each line.++ The lenses in this library are marked as /improper/; this just means that+ they don't admit all the operations of an ideal lens, but only /getting/ and /focusing/.+ Just for this reason, though, the magnificent complexities of the lens libraries+ are a distraction. The lens combinators to keep in mind, the ones that make sense for+ our lenses, are @view@ \/ @(^.)@), @over@ \/ @(%~)@ , and @zoom@.++ One need only keep in mind that if @l@ is a @Lens' a b@, then:++-}+{- $view+ @view l@ is a function @a -> b@ . Thus @view l a@ (also written @a ^. l@ )+ is the corresponding @b@; as was said above, this function will be exactly the+ function you think it is, given its name. Thus to uppercase the first n characters+ of a Producer, leaving the rest the same, we could write:+++ > upper n p = do p' <- p ^. Text.splitAt n >-> Text.toUpper+ > p'+-}+{- $over+ @over l@ is a function @(b -> b) -> a -> a@. Thus, given a function that modifies+ @b@s, the lens lets us modify an @a@ by applying @f :: b -> b@ to+ the @b@ that we can \"see\" through the lens. So @over l f :: a -> a@+ (it can also be written @l %~ f@).+ For any particular @a@, then, @over l f a@ or @(l %~ f) a@ is a revised @a@.+ So above we might have written things like these:++ > stripLines = Text.lines %~ maps (>-> Text.stripStart)+ > stripLines = over Text.lines (maps (>-> Text.stripStart))+ > upper n = Text.splitAt n %~ (>-> Text.toUpper)++-}+{- $zoom+ @zoom l@, finally, is a function from a @Parser b m r@+ to a @Parser a m r@ (or more generally a @StateT (Producer b m x) m r@).+ Its use is easiest to see with an decoding lens like 'utf8', which+ \"sees\" a Text producer hidden inside a ByteString producer:+ @drawChar@ is a Text parser, returning a @Maybe Char@, @zoom utf8 drawChar@ is+ a /ByteString/ parser, returning a @Maybe Char@. @drawAll@ is a Parser that returns+ a list of everything produced from a Producer, leaving only the return value; it would+ usually be unreasonable to use it. But @zoom (splitAt 17) drawAll@+ returns a list of Text chunks containing the first seventeen Chars, and returns the rest of+ the Text Producer for further parsing. Suppose that we want, inexplicably, to+ modify the casing of a Text Producer according to any instruction it might+ contain at the start. Then we might write something like this:++> obey :: Monad m => Producer Text m b -> Producer Text m b+> obey p = do (ts, p') <- lift $ runStateT (zoom (Text.splitAt 7) drawAll) p+> let seven = T.concat ts+> case T.toUpper seven of+> "TOUPPER" -> p' >-> Text.toUpper+> "TOLOWER" -> p' >-> Text.toLower+> _ -> do yield seven+> p'+++> >>> let doc = each ["toU","pperTh","is document.\n"]+> >>> runEffect $ obey doc >-> Text.stdout+> THIS DOCUMENT.++ The purpose of exporting lenses is the mental economy achieved with this three-way+ applicability. That one expression, e.g. @lines@ or @splitAt 17@ can have these+ three uses is no more surprising than that a pipe can act as a function modifying+ the output of a producer, namely by using @>->@ to its left: @producer >-> pipe@+ -- but can /also/ modify the inputs to a consumer by using @>->@ to its right:+ @pipe >-> consumer@++ The three functions, @view@ \/ @(^.)@, @over@ \/ @(%~)@ and @zoom@ are supplied by+ both <http://hackage.haskell.org/package/lens lens> and+ <http://hackage.haskell.org/package/lens-family lens-family> The use of 'zoom' is explained+ in <http://hackage.haskell.org/package/pipes-parse-3.0.1/docs/Pipes-Parse-Tutorial.html Pipes.Parse.Tutorial>+ and to some extent in the @Pipes.Text.Encoding@ module here.++-}+{- $special+ These simple 'lines' examples reveal a more important difference from @Data.Text.Lazy@ .+ This is in the types that are most closely associated with our central text type,+ @Producer Text m r@. In @Data.Text@ and @Data.Text.Lazy@ we find functions like++> splitAt :: Int -> Text -> (Text, Text)+> lines :: Text -> [Text]+> chunksOf :: Int -> Text -> [Text]++ which relate a Text with a pair of Texts or a list of Texts.+ The corresponding functions here (taking account of \'lensification\') are++> view . splitAt :: (Monad m, Integral n) => n -> Producer Text m r -> Producer Text m (Producer Text m r)+> view lines :: Monad m => Producer Text m r -> FreeT (Producer Text m) m r+> view . chunksOf :: (Monad m, Integral n) => n -> Producer Text m r -> FreeT (Producer Text m) m r++ Some of the types may be more readable if you imagine that we have introduced+ our own type synonyms++> type Text m r = Producer T.Text m r+> type Texts m r = FreeT (Producer T.Text m) m r++ Then we would think of the types above as++> view . splitAt :: (Monad m, Integral n) => n -> Text m r -> Text m (Text m r)+> view lines :: (Monad m) => Text m r -> Texts m r+> view . chunksOf :: (Monad m, Integral n) => n -> Text m r -> Texts m r++ which brings one closer to the types of the similar functions in @Data.Text.Lazy@++ In the type @Producer Text m (Producer Text m r)@ the second+ element of the \'pair\' of effectful Texts cannot simply be retrieved+ with something like 'snd'. This is an \'effectful\' pair, and one must work+ through the effects of the first element to arrive at the second Text stream, even+ if you are proposing to throw the Text in the first element away.+ Note that we use Control.Monad.join to fuse the pair back together, since it specializes to++> join :: Monad m => Producer Text m (Producer m r) -> Producer m r++ The return type of 'lines', 'words', 'chunksOf' and the other /splitter/ functions,+ @FreeT (Producer m Text) m r@ -- our @Texts m r@ -- is the type of (effectful)+ lists of (effectful) texts. The type @([Text],r)@ might be seen to gather+ together things of the forms:++> r+> (Text,r)+> (Text, (Text, r))+> (Text, (Text, (Text, r)))+> (Text, (Text, (Text, (Text, r))))+> ...++ (We might also have identified the sum of those types with @Free ((,) Text) r@+ -- or, more absurdly, @FreeT ((,) Text) Identity r@.)++ Similarly, our type @Texts m r@, or @FreeT (Text m) m r@ -- in fact called+ @FreeT (Producer Text m) m r@ here -- encompasses all the members of the sequence:++> m r+> Text m r+> Text m (Text m r)+> Text m (Text m (Text m r))+> Text m (Text m (Text m (Text m r)))+> ...++ We might have used a more specialized type in place of @FreeT (Producer a m) m r@,+ or indeed of @FreeT (Producer Text m) m r@, but it is clear that the correct+ result type of 'lines' will be isomorphic to @FreeT (Producer Text m) m r@ .++ One might think that++> lines :: Monad m => Lens' (Producer Text m r) (FreeT (Producer Text m) m r)+> view . lines :: Monad m => Producer Text m r -> FreeT (Producer Text m) m r++ should really have the type++> lines :: Monad m => Pipe Text Text m r++ as e.g. 'toUpper' does. But this would spoil the control we are+ attempting to maintain over the size of chunks. It is in fact just+ as unreasonable to want such a pipe as to want++> Data.Text.Lazy.lines :: Text -> Text++ to 'rechunk' the strict Text chunks inside the lazy Text to respect+ line boundaries. In fact we have++> Data.Text.Lazy.lines :: Text -> [Text]+> Prelude.lines :: String -> [String]++ where the elements of the list are themselves lazy Texts or Strings; the use+ of @FreeT (Producer Text m) m r@ is simply the 'effectful' version of this.++ The @Pipes.Group@ module, which can generally be imported without qualification,+ provides many functions for working with things of type @FreeT (Producer a m) m r@.+ In particular it conveniently exports the constructors for @FreeT@ and the associated+ @FreeF@ type -- a fancy form of @Either@, namely++> data FreeF f a b = Pure a | Free (f b)++ for pattern-matching. Consider the implementation of the 'words' function, or+ of the part of the lens that takes us to the words; it is compact but exhibits many+ of the points under discussion, including explicit handling of the @FreeT@ and @FreeF@+ constuctors. Keep in mind that++> newtype FreeT f m a = FreeT (m (FreeF f a (FreeT f m a)))+> next :: Monad m => Producer a m r -> m (Either r (a, Producer a m r))++ Thus the @do@ block after the @FreeT@ constructor is in the base monad, e.g. 'IO' or 'Identity';+ the later subordinate block, opened by the @Free@ constructor, is in the @Producer@ monad:++> words :: Monad m => Producer Text m r -> FreeT (Producer Text m) m r+> words p = FreeT $ do -- With 'next' we will inspect p's first chunk, excluding spaces;+> x <- next (p >-> dropWhile isSpace) -- note that 'dropWhile isSpace' is a pipe, and is thus *applied* with '>->'.+> return $ case x of -- We use 'return' and so need something of type 'FreeF (Text m) r (Texts m r)'+> Left r -> Pure r -- 'Left' means we got no Text chunk, but only the return value; so we are done.+> Right (txt, p') -> Free $ do -- If we get a chunk and the rest of the producer, p', we enter the 'Producer' monad+> p'' <- view (break isSpace) -- When we apply 'break isSpace', we get a Producer that returns a Producer;+> (yield txt >> p') -- so here we yield everything up to the next space, and get the rest back.+> return (words p'') -- We then carry on with the rest, which is likely to begin with space.++-}
changelog view
@@ -1,3 +1,9 @@+# Version 0.0.0.12++* Opposing lenses for `lines` and `unlines` and `words` and `unwords`. + Brought closer in line with `pipes-bytestring` again. Removed `count`, which+ was wrong. Scrapped `Iso` and the `profunctors` dependency. + # Version 0.0.0.11 * Updated to use streaming-commons in place of text-stream-decoding.
pipes-text.cabal view
@@ -1,5 +1,5 @@ name: pipes-text-version: 0.0.0.11+version: 0.0.0.12 synopsis: Text pipes. description: * This package will be in a draft, or testing, phase until version 0.0.1. Please report any installation difficulties, or any wisdom about the api, on the github page or the <https://groups.google.com/forum/#!forum/haskell-pipes pipes list> .@@ -38,12 +38,11 @@ bytestring >= 0.9.2.1 && < 0.11, text >= 0.11.2 && < 1.2 , streaming-commons >= 0.1 && < 0.2 , - profunctors >= 3.1.1 && < 4.1 , pipes >= 4.0 && < 4.2 , pipes-group >= 1.0.0 && < 1.1 , pipes-parse >= 3.0.0 && < 3.1 , pipes-safe >= 2.1 && < 2.3 , - pipes-bytestring >= 1.0 && < 2.1 ,+ pipes-bytestring >= 1.0 && < 2.2 , transformers >= 0.2.0.0 && < 0.5 other-extensions: RankNTypes@@ -51,6 +50,6 @@ ghc-options: -O2 if !flag(noio)- exposed-modules: Pipes.Text.IO+ exposed-modules: Pipes.Text.IO, Pipes.Text.Tutorial build-depends: text >=0.11.3 && < 1.2