diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+# 0.4.6.0
+
+- Deprecated Control.Foldl.Transduce.ByteString.IO 
+- Added Control.Foldl.Transduce.ByteString
+- Added "paragraphs" splitter.
+
 # 0.4.5.0
 
 - added split
diff --git a/foldl-transduce.cabal b/foldl-transduce.cabal
--- a/foldl-transduce.cabal
+++ b/foldl-transduce.cabal
@@ -1,5 +1,5 @@
 Name: foldl-transduce
-Version: 0.4.5.0
+Version: 0.4.6.0
 Cabal-Version: >=1.8.0.2
 Build-Type: Simple
 License: BSD3
@@ -30,16 +30,19 @@
         containers                   < 0.6 ,
         bifunctors    == 5.*               ,
         profunctors   == 5.*               ,
+        semigroups    >= 0.18              ,
         semigroupoids >= 5.0               ,
         foldl         >= 1.1      && < 2   ,
         comonad       == 4.*               ,
         free          == 4.*               ,         
         void          >= 0.6               ,
+        split         >= 0.2.2             ,
         monoid-subclasses == 0.4.*         
     Exposed-Modules:
         Control.Foldl.Transduce,
         Control.Foldl.Transduce.Text,
         Control.Foldl.Transduce.Textual,
+        Control.Foldl.Transduce.ByteString,
         Control.Foldl.Transduce.ByteString.IO,
         Control.Foldl.Transduce.Internal
     GHC-Options: -O2 -Wall
@@ -67,6 +70,7 @@
         tasty-hunit >= 0.9.2,
         tasty-quickcheck >= 0.8.3.2, 
         monoid-subclasses == 0.4.*,
+        split         >= 0.2.2,
         foldl               ,
         foldl-transduce
 
diff --git a/src/Control/Foldl/Transduce.hs b/src/Control/Foldl/Transduce.hs
--- a/src/Control/Foldl/Transduce.hs
+++ b/src/Control/Foldl/Transduce.hs
@@ -121,7 +121,7 @@
 import Control.Comonad.Cofree 
 import Control.Foldl (Fold(..),FoldM(..))
 import qualified Control.Foldl as L
-import Control.Foldl.Transduce.Internal (Pair(..),Quartet(..),_1of3)
+import Control.Foldl.Transduce.Internal (Pair(..),Quartet(..),fst3)
 
 {- $setup
 
@@ -168,7 +168,7 @@
 -}
 type Transduction' a b r = forall x. Fold b x -> Fold a (r,x)
 
-{-| Helper for storing a 'ReifiedTransduction'' safely on a container.		
+{-| Helper for storing a 'Transduction'' safely on a container.		
 
 -}
 newtype ReifiedTransduction' a b r = ReifiedTransduction' { getTransduction' :: Transduction' a b r }
@@ -203,7 +203,7 @@
      = forall x. Transducer (x -> i -> (x,[o],[[o]])) x (x -> (r,[o],[[o]]))
 
 instance Comonad (Transducer i o) where
-    extract (Transducer _ begin done) = _1of3 (done begin)
+    extract (Transducer _ begin done) = fst3 (done begin)
     {-# INLINABLE extract #-}
 
     duplicate (Transducer step begin done) = Transducer step begin (\x -> (Transducer step x done,[],[]))
@@ -486,14 +486,14 @@
 -}
 foldify :: Transducer i o s -> Fold i s
 foldify (Transducer step begin done) =
-    Fold (\x i -> _1of3 (step x i)) begin (\x -> _1of3 (done x))
+    Fold (\x i -> fst3 (step x i)) begin (\x -> fst3 (done x))
 
 {-| Monadic version of 'foldify'.		
 
 -}
 foldifyM :: Functor m => TransducerM m i o s -> FoldM m i s
 foldifyM (TransducerM step begin done) =
-    FoldM (\x i -> fmap _1of3 (step x i)) begin (\x -> fmap _1of3 (done x))
+    FoldM (\x i -> fmap fst3 (step x i)) begin (\x -> fmap fst3 (done x))
 
 {-| Transforms a 'Fold' into a 'Transducer' that sends the return value of the
     'Fold' downstream when upstream closes.		
diff --git a/src/Control/Foldl/Transduce/ByteString.hs b/src/Control/Foldl/Transduce/ByteString.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Foldl/Transduce/ByteString.hs
@@ -0,0 +1,88 @@
+﻿{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+-- |
+--
+-- Pour handles into folds,
+-- write to handles using folds. 
+module Control.Foldl.Transduce.ByteString (
+        -- * Reading from handles
+        drainHandle
+    ,   ChunkSize
+    ,   chunkSize
+    ,   chunkSizeDefault
+        -- * Writing to handles
+    ,   toHandle
+    ,   toHandleBuilder  
+    ) where
+
+import qualified Control.Foldl as L
+import Control.Foldl.Transduce 
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Builder as B
+import Control.Monad.IO.Class
+import System.IO
+import Data.ByteString.Lazy.Internal (defaultChunkSize)
+
+{-| Feed a fold with bytes read from a 'Handle'.
+
+-}
+drainHandle 
+    :: (MonadIO m,ToFoldM m f) 
+    => f B.ByteString r 
+    -> ChunkSize 
+    -> Handle 
+    -> m r 
+drainHandle f (ChunkSize csize) h = driveHandle f csize h
+
+{-| Maximum chunk size		
+
+-}
+newtype ChunkSize = ChunkSize Int deriving (Show,Eq,Ord,Num)
+
+chunkSize :: Int -> ChunkSize
+chunkSize = ChunkSize
+
+chunkSizeDefault :: ChunkSize
+chunkSizeDefault = chunkSize defaultChunkSize
+
+driveHandle :: (MonadIO m,ToFoldM m f) 
+            => f B.ByteString r 
+            -> Int -- ^ max chunk size
+            -> Handle 
+            -> m r 
+driveHandle (toFoldM -> f) chunkSize handle = 
+    L.impurely consumeFunc f (B.hGetSome handle chunkSize,hIsEOF handle)
+    where
+        -- adapted from foldM in Pipes.Prelude
+        consumeFunc step begin done (readChunk,checkEOF) = do
+            x0 <- begin
+            loop x0
+              where
+                loop x = do
+                    atEOF <- liftIO checkEOF
+                    if atEOF 
+                       then done x 
+                       else do
+                           chunk <- liftIO readChunk
+                           x' <- step x chunk
+                           loop $! x'
+
+
+toHandle :: (MonadIO m) => Handle -> L.FoldM m B.ByteString ()
+toHandle handle = 
+    L.FoldM 
+    (\_ b -> liftIO (B.hPut handle b))  
+    (return ()) 
+    (\_ -> return ())
+
+
+toHandleBuilder :: (MonadIO m) => Handle -> L.FoldM m B.Builder ()
+toHandleBuilder handle = 
+    L.FoldM
+    (\_ b -> liftIO (B.hPutBuilder handle b)) 
+    (return ()) 
+    (\_ -> return ())
+
+
diff --git a/src/Control/Foldl/Transduce/ByteString/IO.hs b/src/Control/Foldl/Transduce/ByteString/IO.hs
--- a/src/Control/Foldl/Transduce/ByteString/IO.hs
+++ b/src/Control/Foldl/Transduce/ByteString/IO.hs
@@ -5,7 +5,7 @@
 --
 -- Pour handles into folds,
 -- write to handles using folds. 
-module Control.Foldl.Transduce.ByteString.IO (
+module Control.Foldl.Transduce.ByteString.IO {-# DEPRECATED "Use Control.Foldl.Transduce.ByteString instead." #-} (
         driveHandle
     ,   toHandle
     ,   toHandleBuilder  
diff --git a/src/Control/Foldl/Transduce/Internal.hs b/src/Control/Foldl/Transduce/Internal.hs
--- a/src/Control/Foldl/Transduce/Internal.hs
+++ b/src/Control/Foldl/Transduce/Internal.hs
@@ -2,12 +2,12 @@
         -- * Strict datatypes 
         Pair(..)
     ,   Quartet(..)
-    ,   _1of3
+    ,   fst3
     ) where
 
 data Pair a b = Pair !a !b
 
 data Quartet a b c d = Quartet !a !b !c !d
 
-_1of3 :: (a,b,c) -> a
-_1of3 (x,_,_) = x
+fst3 :: (a,b,c) -> a
+fst3 (x,_,_) = x
diff --git a/src/Control/Foldl/Transduce/Text.hs b/src/Control/Foldl/Transduce/Text.hs
--- a/src/Control/Foldl/Transduce/Text.hs
+++ b/src/Control/Foldl/Transduce/Text.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ViewPatterns #-}
 
 -- |
 --
@@ -17,8 +18,9 @@
     ,   stripStart
     ,   stripEnd
         -- * Splitters
-    ,   lines
     ,   words
+    ,   lines
+    ,   paragraphs
         -- * Re-exports
         -- $reexports
     ,   module Control.Foldl.Transduce.Textual
@@ -27,7 +29,7 @@
 import Prelude hiding (lines,words)
 import Data.Char
 import Data.Monoid (mempty)
-import Data.Foldable (foldMap)
+import Data.Foldable (foldMap,foldl')
 import qualified Data.ByteString as B
 import qualified Data.Text 
 import qualified Data.Text as T
@@ -40,6 +42,11 @@
 import qualified Control.Foldl.Transduce as L
 import Control.Foldl.Transduce.Textual
 import Control.Foldl.Transduce.Internal (Pair(..))
+import qualified Data.List
+import Data.List.Split
+import qualified Data.List.Split
+import Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.List.NonEmpty as NonEmpty
 
 {- $setup
 
@@ -55,7 +62,7 @@
 
 {-| Builds a decoding 'Transducer' out of a stream-oriented decoding function
     from "Data.Text.Encoding" and an error handler from
-    "Data.Text.Encoding.Error".		
+    "Data.Text.Encoding.Error".        
 
 -}
 decoder :: (B.ByteString -> T.Decoding) -> T.OnDecodeError -> L.Transducer B.ByteString T.Text ()
@@ -73,7 +80,7 @@
     onLeftovers' = onLeftovers "leftovers" Nothing
 
 {-| Builds a UTF8-decoding 'Transducer'. Takes an error handler from
-    "Data.Text.Encoding.Error".		
+    "Data.Text.Encoding.Error".        
 
 -}
 utf8 :: T.OnDecodeError -> L.Transducer B.ByteString T.Text ()
@@ -112,7 +119,7 @@
 utf8strict = utf8 T.strictDecode
 
 {-| Similar to 'decoder', but catches 'UnicodeException' in 'IO' and uses
-    'Control.Monad.Trans.Except' to communicate the error.		
+    'Control.Monad.Trans.Except' to communicate the error.        
 
 -}
 decoderE :: MonadIO m
@@ -141,7 +148,7 @@
         onLeftovers' = T.strictDecode "leftovers" Nothing
 
 {-| Like 'utf8strict', but catches 'UnicodeException' in 'IO' and uses
-    'Control.Monad.Trans.Except' to communicate the error.		
+    'Control.Monad.Trans.Except' to communicate the error.        
 
 >>> runExceptT $ L.foldM (transduceM utf8E (L.generalize L.list)) (map fromString ["invalid \xc3\x28 sequence"])
 Left Cannot decode byte '\x28': Data.Text.Internal.Encoding.streamDecodeUtf8With: Invalid UTF-8 stream
@@ -152,7 +159,7 @@
 utf8E :: MonadIO m => L.TransducerM (ExceptT T.UnicodeException m) B.ByteString T.Text ()   
 utf8E = decoderE T.streamDecodeUtf8With
 
-{-| Appends a newline at the end of the stream.		
+{-| Appends a newline at the end of the stream.        
 
 >>> L.fold (transduce newline L.list) (map T.pack ["without","newline"])
 ["without","newline","\n"]
@@ -163,7 +170,7 @@
 blank :: T.Text -> Bool
 blank = Data.Text.all isSpace
 
-{-| Remove leading white space from a stream of 'Text'.		
+{-| Remove leading white space from a stream of 'Text'.        
 
 >>> L.fold (transduce stripStart L.list) (map T.pack ["   ","", "   text "])
 ["text "]
@@ -178,7 +185,7 @@
                 else (True, [T.stripStart i],[])
         done _  = ((),[],[])
 
-{-| Remove trailing white space from a stream of 'Text'.		
+{-| Remove trailing white space from a stream of 'Text'.        
 
     __/BEWARE!/__ 
     This function naively accumulates in memory any arriving "blank blocks" of
@@ -274,6 +281,119 @@
                         (_,[]) -> error "never happens, txt not blank"
                 in (nextstate,oldgroup,newgroups)
         done _ = ((),[],[])
+
+
+data ParagraphsState = 
+      SkippingAfterStreamStart
+    | SkippingAfterNewline
+    | SkippingAfterBlankLine
+    | ContinuingNonemptyLine
+
+{-| Splits a stream of text into paragraphs, removing empty lines and trimming
+    newspace from the start of each line.
+
+>>> map mconcat (L.fold (folds paragraphs L.list L.list) (map T.pack [" \n aaa","\naa ", " \n\nbb\n"]))
+["aaa\naa  \n","bb\n"]
+
+    Used with 'L.transduce', it removes empty lines and trims newspace from the
+    start of each line.
+-}
+paragraphs :: L.Transducer T.Text T.Text ()
+paragraphs = L.Transducer step SkippingAfterStreamStart done 
+    where
+        step tstate txt
+            | Data.Text.null txt = 
+                (tstate,[],[])
+            | otherwise = 
+                let (initlines,lastline) = splittedLines txt
+                    (tstate', outputsreversed) =
+                        advanceLast
+                        (foldl' 
+                            advance
+                            (tstate,pure [])
+                            initlines)
+                        lastline          
+                    (xs :| xss) = fmap reverse (NonEmpty.reverse outputsreversed)
+                in (tstate',xs,xss)
+        done _ = 
+            ((),[],[])
+        splittedLines :: T.Text -> ([T.Text],T.Text)
+        splittedLines nonEmptyChunk = 
+            let splitted = 
+                    Data.Text.lines nonEmptyChunk 
+                    ++
+                    if T.last nonEmptyChunk == '\n' then [mempty] else mempty
+            in (init splitted, last splitted) -- unsafe with empty lists!!!
+        advance 
+            :: (ParagraphsState, NonEmpty [T.Text]) 
+            -> T.Text 
+            -> (ParagraphsState, NonEmpty [T.Text])
+        advance (s,outputs) i = 
+            case (s, blank i) of
+                (SkippingAfterStreamStart, True) -> 
+                    (,) 
+                    SkippingAfterStreamStart 
+                    outputs
+                (SkippingAfterStreamStart, False) -> 
+                    (,)
+                    SkippingAfterNewline
+                    (prepend ["\n",T.stripStart i] outputs) 
+                (SkippingAfterNewline, True) -> 
+                    (,) 
+                    SkippingAfterBlankLine 
+                    outputs 
+                (SkippingAfterNewline, False) -> 
+                    (,)
+                    SkippingAfterNewline
+                    (prepend ["\n",T.stripStart i] outputs)
+                (SkippingAfterBlankLine, True) -> 
+                    (,) 
+                    SkippingAfterBlankLine 
+                    outputs 
+                (SkippingAfterBlankLine, False) -> 
+                    (,)
+                    SkippingAfterNewline
+                    (prepend ["\n",T.stripStart i] (NonEmpty.cons [] outputs)) 
+                (ContinuingNonemptyLine, _) -> 
+                    (,)
+                    SkippingAfterNewline
+                    (prepend ["\n",i] outputs)
+        advanceLast 
+                :: (ParagraphsState, NonEmpty [T.Text]) 
+                -> T.Text 
+                -> (ParagraphsState, NonEmpty [T.Text])
+        advanceLast (s,outputs) i = 
+            case (s, blank i) of
+                (SkippingAfterStreamStart, True) -> 
+                    (,) 
+                    SkippingAfterStreamStart 
+                    outputs
+                (SkippingAfterStreamStart, False) -> 
+                    (,)
+                    ContinuingNonemptyLine
+                    (prepend [T.stripStart i] outputs)
+                (SkippingAfterNewline, True) -> 
+                    (,) 
+                    SkippingAfterNewline 
+                    outputs
+                (SkippingAfterNewline, False) -> 
+                    (,)
+                    ContinuingNonemptyLine
+                    (prepend [T.stripStart i] outputs)
+                (SkippingAfterBlankLine, True) -> 
+                    (,)
+                    SkippingAfterBlankLine
+                    outputs 
+                (SkippingAfterBlankLine, False) -> 
+                    (,)
+                    ContinuingNonemptyLine
+                    (prepend [T.stripStart i] (NonEmpty.cons [] outputs))
+                (ContinuingNonemptyLine, _) -> 
+                    (,)
+                    ContinuingNonemptyLine
+                    (prepend [i] outputs)
+        prepend :: [a] -> NonEmpty [a] -> NonEmpty [a]
+        prepend as (as':| rest) = (as ++ as') :| rest
 
 ------------------------------------------------------------------------------
 
diff --git a/tests/tests.hs b/tests/tests.hs
--- a/tests/tests.hs
+++ b/tests/tests.hs
@@ -1,9 +1,12 @@
 module Main where
 
 import Prelude hiding (splitAt,lines,words)
+import Data.Char
 import Data.String hiding (lines,words)
 import Data.Monoid
 import Data.Bifunctor
+import qualified Data.List (intersperse,splitAt)
+import qualified Data.List.Split as Split
 import qualified Data.Monoid.Factorial as SFM
 import Test.Tasty
 import Test.Tasty.HUnit
@@ -17,104 +20,222 @@
 import Control.Foldl.Transduce.Text
 import Control.Foldl.Transduce.Textual
 
+{- $quickcheck
+
+   Notes for quickchecking on the REPL:
+
+cabal repl tests
+:t sample
+sample :: Show a => Gen a -> IO ()
+sample (arbitrary :: Gen WordA)
+
+-}
+
 main :: IO ()
 main = defaultMain tests
 
-newtype WordQC = WordQC { getWordQC :: T.Text } deriving (Show)
+testCaseEq :: (Eq a, Show a) => TestName -> a -> a -> TestTree
+testCaseEq name a1 a2 = testCase name (assertEqual "" a1 a2)
 
-instance Arbitrary WordQC where
+blank :: T.Text -> Bool
+blank = T.all isSpace
+
+nl :: T.Text
+nl = T.pack "\n"
+
+sp :: T.Text
+sp = T.pack " "
+
+c :: T.Text
+c = T.pack "c"
+
+{- $words
+
+-}
+
+newtype WordA = WordA { getWord :: T.Text } deriving (Show)
+
+instance Arbitrary WordA where
     arbitrary = do
         firstChar <- oneof [pure ' ', pure '\n', arbitrary]
         lastChar <- oneof [pure ' ', pure '\n', arbitrary]
         middle <- listOf (frequency [(1,pure ' '),(4,arbitrary)])
-        return (WordQC (T.pack (firstChar : (middle ++ [lastChar]))))
+        return (WordA (T.pack (firstChar : (middle ++ [lastChar]))))
 
+{- $paragraphs
+
+-}
+
+newtype TextChunksA = TextChunksA { getChunks :: [T.Text] } deriving (Show)
+
+instance Arbitrary TextChunksA where
+    arbitrary = flip suchThat (not . blank . mconcat . getChunks) (do
+        TextChunksA <$> partz)
+            where
+                chunkz = frequency [
+                      (20::Int, flip T.replicate sp <$> choose (1,40)) 
+                    , (20, flip T.replicate sp <$> choose (1,3)) 
+                    , (50, pure nl)
+                    , (20, flip T.replicate c <$> choose (1,30))
+                    , (20, flip T.replicate c <$> choose (1,3))
+                    ]
+                combined = mconcat <$> vectorOf 40 chunkz 
+                partitions = infiniteListOf (choose (1::Int,7))
+                partz = partition [] <$> combined <*> partitions
+                partition :: [T.Text] -> T.Text -> [Int] -> [T.Text]
+                partition accum text (x:xs) =
+                    if x >= T.length text    
+                       then reverse (text:accum)
+                       else 
+                           let (point,rest) = T.splitAt x text 
+                           in 
+                           partition (point:accum) rest xs
+                partition _ _ [] = error "never happens"
+    shrink (TextChunksA texts) = 
+        let removeIndex i xs = 
+                let (xs',xs'') = Data.List.splitAt i xs
+                in xs' ++ tail xs'' 
+            l = length texts
+        in 
+        if l == 1 
+           then []
+           else map (\i -> TextChunksA (removeIndex i texts)) [0..l-1] 
+
+paragraphsBaseline 
+    :: T.Text -> [T.Text] 
+paragraphsBaseline =  
+      map (T.unlines . map T.stripStart . T.lines)
+    . map mconcat 
+    . map (`mappend` [nl])
+    . map (Data.List.intersperse nl)
+    . filter (not . null) 
+    . Split.splitWhen blank 
+    . T.lines
+
+ignoreLastNewline :: [T.Text] -> [T.Text]
+ignoreLastNewline ts = 
+    let 
+        lastt = last ts
+        lastt' = if T.last lastt == '\n' then T.init lastt else lastt 
+    in init ts ++ [lastt']
+
+-- (paragraphs,chunks)
+splittedParagraphs :: T.Text -> [Int] -> [([T.Text],[T.Text])]
+splittedParagraphs txt splitsizes =   
+    let 
+        splitted = paragraphsBaseline txt
+    in 
+    zip (repeat splitted) (map (flip T.chunksOf txt) splitsizes)
+
+paragraphsUnderTest
+    :: [T.Text] -> [T.Text] 
+paragraphsUnderTest txt =
+    map mconcat (L.fold (folds paragraphs L.list L.list) txt)
+
+paragraph01 :: T.Text
+paragraph01 = 
+    T.pack 
+    "  \n \n\n \n \n \
+    \a aa aaa \nb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bb \n \
+    \ ccccccccccccccccccccccc cccccccc \n\n  \n \n\n ccc\     
+    \ \n \n \nd\n\n\ne \
+    \\n" 
+    
+paragraph02 :: T.Text
+paragraph02 = T.pack " cc  "
+
 tests :: TestTree
 tests = 
     testGroup "Tests" 
     [
         testGroup "surround" 
         [
-            testCase "surroundempty" $ 
-                assertEqual mempty
-                    "prefixsuffix"
-                    (L.fold (transduce (surround "prefix" "suffix") L.list) "")
-        ]
-        ,
+            testCaseEq "surroundempty" 
+                "prefixsuffix"
+                (L.fold (transduce (surround "prefix" "suffix") L.list) "")
+        ],
         testGroup "chunksOf" 
         [
-            testCase "emptyList3" $ 
-                assertEqual mempty
-                    ([[]]::[[Int]])
-                    (L.fold (folds (chunksOf 3) L.list L.list) [])
+            testCaseEq "emptyList3"
+                ([[]]::[[Int]])
+                (L.fold (folds (chunksOf 3) L.list L.list) [])
             ,
-            testCase "size1" $ 
-                assertEqual mempty
-                    ([[1],[2],[3],[4],[5],[6],[7]]::[[Int]])
-                    (L.fold (folds (chunksOf 1) L.list L.list) [1..7])
+            testCaseEq "size1" 
+                ([[1],[2],[3],[4],[5],[6],[7]]::[[Int]])
+                (L.fold (folds (chunksOf 1) L.list L.list) [1..7])
             ,
-            testCase "size3" $ 
-                assertEqual mempty
-                    ([[1,2,3],[4,5,6],[7]]::[[Int]])
-                    (L.fold (folds (chunksOf 3) L.list L.list) [1..7])
-        ]
-        ,
-        testGroup "textualBreak" $ 
+            testCaseEq "size3" 
+                ([[1,2,3],[4,5,6],[7]]::[[Int]])
+                (L.fold (folds (chunksOf 3) L.list L.list) [1..7])
+        ],
+        testGroup "textualBreak"
         [
-            testCase "beginwithdot" $
-                assertEqual mempty
-                    ".bb"
-                    (L.fold (bisect (textualBreak (=='.')) ignore (reify id) L.mconcat) ["aa",".bb"])
+            testCaseEq "beginwithdot"
+                ".bb"
+                (L.fold (bisect (textualBreak (=='.')) ignore (reify id) L.mconcat) ["aa",".bb"])
             ,
-            testCase "endwithdot" $
-                assertEqual mempty
-                    "."
-                    (L.fold (bisect (textualBreak (=='.')) ignore (reify id) L.mconcat) ["aa","bb."])
-        ]   
-        ,
-        testGroup "newline" $ 
+            testCaseEq "endwithdot"
+                "."
+                (L.fold (bisect (textualBreak (=='.')) ignore (reify id) L.mconcat) ["aa","bb."])
+        ],
+        testGroup "newline"
         [
-            testCase "newlineempty" $
-                assertEqual mempty
+            testCaseEq "newlineempty"
                 (T.pack "\n")
                 (mconcat (L.fold (transduce newline L.list) (map T.pack [])))
             ,
-            testCase "newlinenull" $
-                assertEqual mempty
+            testCaseEq "newlinenull"
                 (T.pack "\n")
                 (mconcat (L.fold (transduce newline L.list) (map T.pack [""])))
-        ]
-        ,
+        ],
         testGroup "words" 
         [ 
             testGroup "quickcheck" 
             [ 
-                testProperty "quickcheck1" (\chunks -> 
-                         let tchunks = fmap getWordQC chunks 
-                         in
-                         (case TL.words (TL.fromChunks tchunks) of
-                            [] -> [mempty]
-                            x -> x) ==
-                         (fmap TL.fromChunks (L.fold (folds words L.list L.list) tchunks)))
+                testProperty "quickcheck1" (\chunks -> -- list of words 
+                    let tchunks = fmap getWord chunks 
+                    in
+                    (case TL.words (TL.fromChunks tchunks) of
+                       [] -> [mempty]
+                       x -> x) ==
+                    (fmap TL.fromChunks (L.fold (folds words L.list L.list) tchunks)))
             ]
-        ]
-        ,
-        testGroup "quiesceWith" $ 
+        ],
+        testGroup "paragraphs" 
         [
-            testCase "collectAfterFailure" $
-                let foldthatfails = 
-                        transduceM utf8E (L.generalize L.list)
-                    inputs = 
-                        map fromString ["invalid \xc3\x28 sequence","xxx","zzz","___"]
-                    fallbackfold =
-                        bisectM (chunkedSplitAt 4) (reifyM id) ignore (L.generalize L.list)
-                in
-                do
-                   r <- L.foldM (quiesceWith fallbackfold foldthatfails) inputs 
-                   assertEqual 
-                       mempty 
-                       (Left ('C',4))
-                       (first (bimap (head . show) (SFM.length . mconcat)) r)
+            testCase "paragraphs01"
+                (mapM_
+                    (\(x,y) -> assertEqual "" (ignoreLastNewline x) (ignoreLastNewline (paragraphsUnderTest y))) 
+                    (splittedParagraphs paragraph01 [1..7])),
+            testCaseEq "newlineAtEnd"
+                (map T.pack ["aa\n"]) 
+                (paragraphsUnderTest (map T.pack ["a","a","\n"])),
+            testCaseEq "noNewlineAtEnd"
+                (map T.pack ["aa"]) 
+                (paragraphsUnderTest (map T.pack ["a","a"])),
+            testGroup "quickcheck" 
+            [ 
+                testProperty "quickcheck1" (\(TextChunksA chunks) ->
+                        ignoreLastNewline (paragraphsUnderTest chunks)
+                        ==
+                        ignoreLastNewline (paragraphsBaseline (mconcat chunks)))
+            ]
+        ],
+        testGroup "quiesceWith"  
+        [
+            testCase "collectAfterFailure" (do
+               let
+                  foldthatfails = 
+                      transduceM utf8E (L.generalize L.list)
+                  inputs = 
+                      map fromString ["invalid \xc3\x28 sequence","xxx","zzz","___"]
+                  fallbackfold =
+                      bisectM (chunkedSplitAt 4) (reifyM id) ignore (L.generalize L.list)
+               r <- L.foldM (quiesceWith fallbackfold foldthatfails) inputs 
+               assertEqual 
+                   mempty 
+                   (Left ('C',4))
+                   (first (bimap (head . show) (SFM.length . mconcat)) r))
         ]   
     ]
 
