diff --git a/library/Potoki/Core/Consume.hs b/library/Potoki/Core/Consume.hs
--- a/library/Potoki/Core/Consume.hs
+++ b/library/Potoki/Core/Consume.hs
@@ -17,6 +17,7 @@
   foldingInIO,
   execState,
   writeBytesToFile,
+  writeBytesToFileWithoutBuffering,
   appendBytesToFile,
   deleteFiles,
   printBytes,
@@ -206,11 +207,32 @@
 -}
 {-# INLINABLE writeBytesToFile #-}
 writeBytesToFile :: FilePath -> Consume ByteString (Either IOException ())
-writeBytesToFile path =
+writeBytesToFile =
+  writeBytesToFileWithBuffering (BlockBuffering Nothing)
+
+{-|
+Overwrite a file.
+
+* Exception-free
+* Automatic resource management
+-}
+{-# INLINABLE writeBytesToFileWithBuffering #-}
+writeBytesToFileWithBuffering :: BufferMode -> FilePath -> Consume ByteString (Either IOException ())
+writeBytesToFileWithBuffering bufferMode path =
   Consume $ \ fetch ->
-  try $ withFile path WriteMode $ \ handleVal ->
+  try $ withFile path WriteMode $ \ handle ->
   do
-    L.fetchAndHandleAll fetch (return ()) (C.hPut handleVal)
+    hSetBuffering handle bufferMode
+    L.fetchAndHandleAll fetch (return ()) (C.hPut handle)
+
+{-|
+A more efficient implementation than just writing to file without buffering.
+It uses an explicit buffer of input chunks and flushes all the chunks that have been so far aggregated at once.
+-}
+{-# INLINABLE writeBytesToFileWithoutBuffering #-}
+writeBytesToFileWithoutBuffering :: FilePath -> Consume ByteString (Either IOException ())
+writeBytesToFileWithoutBuffering =
+  transform (arr mconcat . B.bufferizeFlushing 64) . writeBytesToFileWithBuffering NoBuffering
 
 {-|
 Append to a file.
diff --git a/library/Potoki/Core/Fetch.hs b/library/Potoki/Core/Fetch.hs
--- a/library/Potoki/Core/Fetch.hs
+++ b/library/Potoki/Core/Fetch.hs
@@ -214,36 +214,38 @@
 {-# INLINABLE mapFilter #-}
 mapFilter :: (input -> Maybe output) -> Fetch input -> Fetch output
 mapFilter mapping (Fetch fetchIO) =
-  Fetch $ 
-  fix $ \ doLoop -> do 
-    fetch <- fetchIO
-    case mapping <$> fetch of
-      Just (Just output) -> return (Just output)
-      Just Nothing -> doLoop
-      Nothing -> return Nothing
+  Fetch $ let
+    loop = do 
+      fetch <- fetchIO
+      case mapping <$> fetch of
+        Just (Just output) -> return (Just output)
+        Just Nothing -> loop
+        Nothing -> return Nothing
+    in loop
 
 {-# INLINABLE filter #-}
 filter :: (input -> Bool) -> Fetch input -> Fetch input
 filter predicate (Fetch fetchIO) =
-  Fetch $ 
-  fix $ \ doLoop -> do 
-    fetch <- fetchIO
-    case predicate <$> fetch of
-      Just True -> return fetch
-      Just False -> doLoop
-      Nothing -> return Nothing
-
+  Fetch $ let
+    loop = do 
+      fetch <- fetchIO
+      case predicate <$> fetch of
+        Just True -> return fetch
+        Just False -> loop
+        Nothing -> return Nothing
+    in loop
 
 {-# INLINABLE just #-}
 just :: Fetch (Maybe element) -> Fetch element
 just (Fetch fetchIO) =
-  Fetch $ 
-  fix $ \ doLoop -> do 
-    fetch <- fetchIO
-    case fetch of
-      Just (Just element) -> return (Just element)
-      Just (Nothing) -> doLoop
-      Nothing -> return Nothing
+  Fetch $ let
+    loop = do
+      fetch <- fetchIO
+      case fetch of
+        Just (Just element) -> return (Just element)
+        Just (Nothing) -> loop
+        Nothing -> return Nothing
+    in loop
 
 {-# INLINABLE takeWhile #-}
 takeWhile :: (element -> Bool) -> Fetch element -> Fetch element
diff --git a/library/Potoki/Core/IO.hs b/library/Potoki/Core/IO.hs
--- a/library/Potoki/Core/IO.hs
+++ b/library/Potoki/Core/IO.hs
@@ -23,12 +23,13 @@
 
 produce :: Produce input -> forall x. IO x -> (input -> IO x) -> IO x
 produce (Produce produceAcquire) stop emit =
-  C.acquire produceAcquire $ \ (Fetch fetchIO) ->
-    fix $ \ doLoop -> do
+  C.acquire produceAcquire $ \ (Fetch fetchIO) -> let
+    loop = do
       fetch <- fetchIO
       case fetch of
         Nothing      -> stop
-        Just element -> emit element >> doLoop
+        Just element -> emit element >> loop
+    in loop
 
 consume :: IO (Maybe input) -> Consume input output -> IO output
 consume fetchIO (Consume consumeIO) =
diff --git a/library/Potoki/Core/IO/Fetch.hs b/library/Potoki/Core/IO/Fetch.hs
--- a/library/Potoki/Core/IO/Fetch.hs
+++ b/library/Potoki/Core/IO/Fetch.hs
@@ -6,11 +6,13 @@
 {-| Fetch all the elements running the provided handler on them -}
 fetchAndHandleAll :: Fetch element -> IO () -> (element -> IO ()) -> IO ()
 fetchAndHandleAll (Fetch fetchIO) onEnd onElement =
-  fix $ \ doLoop -> do
-    fetch <- fetchIO
-    case fetch of
-      Nothing      -> onEnd
-      Just element -> onElement element >> doLoop
+  let
+    loop = do
+      fetch <- fetchIO
+      case fetch of
+        Nothing      -> onEnd
+        Just element -> onElement element >> loop
+    in loop
 
 {-| Fetch and handle just one element -}
 fetchAndHandle :: Fetch element -> IO a -> (element -> IO a) -> IO a
diff --git a/library/Potoki/Core/Transform.hs b/library/Potoki/Core/Transform.hs
--- a/library/Potoki/Core/Transform.hs
+++ b/library/Potoki/Core/Transform.hs
@@ -32,6 +32,7 @@
   A.parseNonEmptyLineBytesConcurrently,
   -- * Concurrency
   N.bufferize,
+  N.bufferizeFlushing,
   N.concurrently,
   N.unsafeConcurrently,
   N.async,
diff --git a/library/Potoki/Core/Transform/Basic.hs b/library/Potoki/Core/Transform/Basic.hs
--- a/library/Potoki/Core/Transform/Basic.hs
+++ b/library/Potoki/Core/Transform/Basic.hs
@@ -34,39 +34,39 @@
 drop amount =
   Transform $ \ (A.Fetch fetchIO) -> M.Acquire $ do
     countRef <- newIORef amount
-    return $ (, return ()) $ 
-      A.Fetch $ fix $ \ doLoop -> do
+    return $ (, return ()) $ A.Fetch $ let
+      loop = do
         count <- readIORef countRef
         if count > 0
           then do
             writeIORef countRef $! pred count
-            doLoop
+            loop
           else fetchIO
+      in loop
 
 {-# INLINE list #-}
 list :: Transform [a] a
 list =
   Transform $ \ (A.Fetch fetchListIO) -> M.Acquire $ do
     bufferRef <- newIORef []
-    return $ (, return ()) $ 
-      A.Fetch $ do
-        buffer <- readIORef bufferRef
-        case buffer of
-          headVal : tailVal -> do
-            writeIORef bufferRef tailVal
-            return (Just headVal)
-          _ ->
-            let
-              fetchElementIO = do
-                fetchListIO >>= \case
-                  Nothing -> return Nothing
-                  Just (headVal : tailVal) -> do
-                    writeIORef bufferRef tailVal
-                    return (Just headVal)
-                  _ -> do
-                    writeIORef bufferRef []
-                    return Nothing
-              in fetchElementIO
+    return $ (, return ()) $ A.Fetch $ do
+      buffer <- readIORef bufferRef
+      case buffer of
+        headVal : tailVal -> do
+          writeIORef bufferRef tailVal
+          return (Just headVal)
+        _ ->
+          let
+            fetchElementIO = do
+              fetchListIO >>= \case
+                Nothing -> return Nothing
+                Just (headVal : tailVal) -> do
+                  writeIORef bufferRef tailVal
+                  return (Just headVal)
+                _ -> do
+                  writeIORef bufferRef []
+                  return Nothing
+            in fetchElementIO
 
 {-# INLINABLE vector #-}
 vector :: Transform (Vector a) a
@@ -74,8 +74,8 @@
   Transform $ \ (A.Fetch fetchVectorIO) -> M.Acquire $ do
     indexRef <- newIORef 0
     vectorRef <- newIORef mempty
-    return $ (, return ()) $ 
-      A.Fetch $ fix $ \ doLoop -> do
+    return $ (, return ()) $ A.Fetch $ let
+      loop = do
         vectorVal <- readIORef vectorRef
         indexVal <- readIORef indexRef
         if indexVal < P.length vectorVal
@@ -86,26 +86,27 @@
             Just vectorVal' -> do
               writeIORef vectorRef vectorVal'
               writeIORef indexRef 0
-              doLoop
+              loop
             Nothing -> return Nothing
+      in loop
 
 {-# INLINE distinctBy #-}
 distinctBy :: (Eq comparable, Hashable comparable) => (element -> comparable) -> Transform element element
 distinctBy f =
   Transform $ \ (A.Fetch fetch) -> M.Acquire $ do
     stateRef <- newIORef mempty
-    return $ (, return ()) $ 
-      A.Fetch $ fix $ \ doLoop -> 
-        fetch >>= \case 
-          Nothing -> return Nothing
-          Just input -> do
-            let comparable = f input
-            !set <- readIORef stateRef
-            if C.member comparable set
-              then doLoop
-              else do
-                writeIORef stateRef $! C.insert comparable set
-                return (Just input)
+    return $ (, return ()) $ A.Fetch $ let
+      loop = fetch >>= \case
+        Nothing -> return Nothing
+        Just input -> do
+          let comparable = f input
+          !set <- readIORef stateRef
+          if C.member comparable set
+            then loop
+            else do
+              writeIORef stateRef $! C.insert comparable set
+              return (Just input)
+      in loop
 
 {-# INLINE distinct #-}
 distinct :: (Eq element, Hashable element) => Transform element element
@@ -138,7 +139,7 @@
 mapInIOWithCounter handler =
   ioTransform $ do
     counter <- newIORef 0
-    return $ mapInIO $ \ a -> do
+    return $ mapInIO $ \ !a -> do
       count <- atomicModifyIORef' counter (\ n -> (succ n, n))
       handler count a
 
@@ -191,29 +192,31 @@
 produce inputToProduce =
   Transform $ \ (Fetch inputFetchIO) -> do
     stateRef <- liftIO $ newIORef Nothing
-    return $ Fetch $ fix $ \ doLoop -> do
-      state <- readIORef stateRef
-      case state of
-        Just (Fetch outputFetchIO, kill) ->
-          do
-            outputFetchResult <- outputFetchIO
-            case outputFetchResult of
-              Just x -> return (Just x)
-              Nothing -> do
-                kill
-                writeIORef stateRef Nothing
-                doLoop
-        Nothing ->
-          do
-            inputFetchResult <- inputFetchIO
-            case inputFetchResult of
-              Just input -> do
-                case inputToProduce input of
-                  Produce (Acquire produceIO) -> do
-                    fetchAndKill <- produceIO
-                    writeIORef stateRef (Just fetchAndKill)
-                    doLoop
-              Nothing -> return Nothing
+    return $ Fetch $ let
+      loop = do
+        state <- readIORef stateRef
+        case state of
+          Just (Fetch outputFetchIO, kill) ->
+            do
+              outputFetchResult <- outputFetchIO
+              case outputFetchResult of
+                Just x -> return (Just x)
+                Nothing -> do
+                  kill
+                  writeIORef stateRef Nothing
+                  loop
+          Nothing ->
+            do
+              inputFetchResult <- inputFetchIO
+              case inputFetchResult of
+                Just input -> do
+                  case inputToProduce input of
+                    Produce (Acquire produceIO) -> do
+                      fetchAndKill <- produceIO
+                      writeIORef stateRef (Just fetchAndKill)
+                      loop
+                Nothing -> return Nothing
+      in loop
 
 {-# INLINE mapFetch #-}
 mapFetch :: (Fetch a -> Fetch b) -> Transform a b
diff --git a/library/Potoki/Core/Transform/ByteString.hs b/library/Potoki/Core/Transform/ByteString.hs
--- a/library/Potoki/Core/Transform/ByteString.hs
+++ b/library/Potoki/Core/Transform/ByteString.hs
@@ -65,25 +65,27 @@
     lineList =
       Transform $ \ (A.Fetch fetchIO) -> M.Acquire $ do
         pokingRef <- newIORef mempty
-        return $ (, return ()) $ A.Fetch $ fetchIO >>= \ case
-          Just chunk -> case B.split 10 chunk of
-            head : tail -> do
+        return $ (, return ()) $ A.Fetch $ do
+          fetchResult <- fetchIO
+          case fetchResult of
+            Just chunk -> case B.split 10 chunk of
+              head : tail -> do
+                poking <- readIORef pokingRef
+                let newPoking = poking <> C.bytes head
+                case unsnoc tail of
+                  Just (!init, last) -> do
+                    writeIORef pokingRef (C.bytes last)
+                    let
+                      !bytes = D.poking newPoking
+                      in return (Just (bytes : init))
+                  Nothing -> do
+                    writeIORef pokingRef newPoking
+                    return (Just [])
+              _ -> return (Just [])
+            Nothing -> do
               poking <- readIORef pokingRef
-              let newPoking = poking <> C.bytes head
-              case unsnoc tail of
-                Just (!init, last) -> do
-                  writeIORef pokingRef (C.bytes last)
-                  let
-                    !bytes = D.poking newPoking
-                    in return (Just (bytes : init))
-                Nothing -> do
-                  writeIORef pokingRef newPoking
-                  return (Just [])
-            _ -> return (Just [])
-          Nothing -> do
-            poking <- readIORef pokingRef
-            if C.null poking
-              then return Nothing
-              else let
-                !bytes = D.poking poking
-                in return (Just (bytes : []))
+              if C.null poking
+                then return Nothing
+                else let
+                  !bytes = D.poking poking
+                  in return (Just (bytes : []))
diff --git a/library/Potoki/Core/Transform/Concurrency.hs b/library/Potoki/Core/Transform/Concurrency.hs
--- a/library/Potoki/Core/Transform/Concurrency.hs
+++ b/library/Potoki/Core/Transform/Concurrency.hs
@@ -8,6 +8,32 @@
 import qualified Acquire.Acquire as M
 
 
+bufferizeFlushing :: Int -> Transform input [input]
+bufferizeFlushing maxSize =
+  Transform $ \ (A.Fetch fetchIO) -> liftIO $ do
+    buffer <- newTBQueueIO maxSize
+    activeVar <- newTVarIO True
+
+    forkIO $ let
+      loop = do
+        fetchingResult <- fetchIO
+        case fetchingResult of
+          Just !element -> do
+            atomically $ writeTBQueue buffer element
+            loop
+          Nothing -> atomically $ writeTVar activeVar False
+      in loop
+
+    return $ Fetch $ atomically $ do
+      batch <- flushTBQueue buffer
+      if null batch
+        then do
+          active <- readTVar activeVar
+          if active
+            then retry
+            else return Nothing
+        else return (Just batch)
+
 {-# INLINE bufferize #-}
 bufferize :: Int -> Transform element element
 bufferize size =
@@ -15,13 +41,15 @@
     buffer <- newTBQueueIO size
     activeVar <- newTVarIO True
 
-    forkIO $ fix $ \ loop -> do
-      fetchingResult <- fetchIO
-      case fetchingResult of
-        Just !element -> do
-          atomically $ writeTBQueue buffer element
-          loop
-        Nothing -> atomically $ writeTVar activeVar False
+    forkIO $ let
+      loop = do
+        fetchingResult <- fetchIO
+        case fetchingResult of
+          Just !element -> do
+            atomically $ writeTBQueue buffer element
+            loop
+          Nothing -> atomically $ writeTVar activeVar False
+      in loop
 
     return $ Fetch $ let
       readBuffer = Just <$> readTBQueue buffer
@@ -79,14 +107,15 @@
 
     replicateM_ workersAmount $ forkIO $ do
       (A.Fetch fetchIO, finalize) <- case syncTransformIO fetchIO of M.Acquire io -> io
-      fix $ \ loop -> do
-        fetchResult <- fetchIO
-        case fetchResult of
-          Just !result -> do
-            atomically (writeTBQueue chan result)
-            loop
-          Nothing -> atomically (modifyTVar' workersCounter pred)
-      finalize
+      let
+        loop = do
+          fetchResult <- fetchIO
+          case fetchResult of
+            Just !result -> do
+              atomically (writeTBQueue chan result)
+              loop
+            Nothing -> atomically (modifyTVar' workersCounter pred)
+        in loop *> finalize
 
     return $ A.Fetch $ let
       readChan = Just <$> readTBQueue chan
@@ -106,11 +135,13 @@
     chan <- atomically newEmptyTMVar
     workersCounter <- atomically (newTVar workersAmount)
 
-    replicateM_ workersAmount $ forkIO $ fix $ \ loop -> do
-      fetchResult <- fetchIO
-      case fetchResult of
-        Just input -> atomically (putTMVar chan input) *> loop
-        Nothing -> atomically (modifyTVar' workersCounter pred)
+    replicateM_ workersAmount $ forkIO $ let
+      loop = do
+        fetchResult <- fetchIO
+        case fetchResult of
+          Just input -> atomically (putTMVar chan input) *> loop
+          Nothing -> atomically (modifyTVar' workersCounter pred)
+      in loop
 
     return $ A.Fetch $ let
       readChan = Just <$> takeTMVar chan
diff --git a/potoki-core.cabal b/potoki-core.cabal
--- a/potoki-core.cabal
+++ b/potoki-core.cabal
@@ -1,5 +1,5 @@
 name: potoki-core
-version: 2.2.8
+version: 2.2.8.2
 synopsis: Low-level components of "potoki"
 description:
   Provides everything required for building custom instances of
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -25,9 +25,11 @@
   defaultMain $
   testGroup "All tests" $
   [
-    testCase "extractLines" $ do
+    testCase "extractLinesWithoutTrail" $ do
       assertEqual "" ["ab", "", "cd"] =<<
         C.produceAndConsume (E.transform A.extractLinesWithoutTrail (E.list ["a", "b\n", "\nc", "d\n"])) D.list
+    ,
+    testCase "extractLines" $ do
       assertEqual "" ["ab", "", "cd", ""] =<<
         C.produceAndConsume (E.transform A.extractLines (E.list ["a", "b\n", "\nc", "d\n"])) D.list
     ,
