diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,11 @@
+## 0.2.0
+
+* Updated for Streamly 0.9.0.
+* Added `groupByHeader` convenience function.
+
 ## 0.1.0
 
-* Updated for streamly 0.8.0.
+* Updated for Streamly 0.8.0.
 
 ## 0.0.2
 
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Shlok Datye (c) 2020
+Copyright Shlok Datye (c) 2023
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -15,54 +15,68 @@
 ## Quick start
 
 ```haskell
-{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
 
 module Main where
 
 import Crypto.Hash (hashFinalize, hashInit, hashUpdate)
 import Crypto.Hash.Algorithms (SHA256)
 import Data.ByteString (ByteString)
-import Data.Either (isRight)
 import Data.Function ((&))
 import Data.Maybe (fromJust, fromMaybe)
 import Data.Void (Void)
-import Streamly.External.Archive (Header, headerPathName, readArchive)
-import Streamly.Internal.Data.Fold.Types (Fold (..))
-import Streamly.Internal.Data.Unfold.Types (Unfold)
-import qualified Streamly.Prelude as S
+import qualified Streamly.Data.Fold as F
+import qualified Streamly.Data.Stream.Prelude as S
+import Streamly.External.Archive
+  ( Header,
+    groupByHeader,
+    headerPathName,
+    readArchive,
+  )
+import Streamly.Internal.Data.Fold.Type (Fold (Fold), Step (Partial))
+import Streamly.Internal.Data.Unfold.Type (Unfold)
 
 main :: IO ()
 main = do
-    -- Obtain an unfold for the archive.
-    -- For each entry in the archive, we will get a Header followed
-    -- by zero or more ByteStrings containing chunks of file data.
-    let unf :: Unfold IO Void (Either Header ByteString)
-            = readArchive "/path/to/archive.tar.gz"
+  -- Obtain an unfold for the archive.
+  -- For each entry in the archive, we will get a Header followed
+  -- by zero or more ByteStrings containing chunks of file data.
+  let unf :: Unfold IO Void (Either Header ByteString) =
+        readArchive "/path/to/archive.tar.gz"
 
-    -- Create a fold for converting each entry (which, as we saw
-    -- above, is a Left followed by zero or more Rights) into a
-    -- path and corresponding SHA-256 hash (Nothing for no data).
-    let entryFold :: Fold IO (Either Header ByteString) (String, Maybe String)
-            = Fold
-                (\(mpath, mctx) e ->
-                    case e of
-                        Left h -> do
-                            mpath' <- headerPathName h
-                            return (mpath', mctx)
-                        Right bs ->
-                            return (mpath,
-                                Just . (`hashUpdate` bs) $
-                                    fromMaybe (hashInit @SHA256) mctx))
-                (return (Nothing, Nothing))
-                (\(mpath, mctx) ->
-                    return (show $ fromJust mpath,
-                                show . hashFinalize <$> mctx))
+  -- Create a fold for converting each entry (which, as we saw
+  -- above, is a Left followed by zero or more Rights) into a
+  -- path and corresponding SHA-256 hash (Nothing for no data).
+  let entryFold :: Fold IO (Either Header ByteString) (String, Maybe String) =
+        Fold
+          ( \(mpath, mctx) e ->
+              case e of
+                Left h -> do
+                  mpath' <- headerPathName h
+                  return $ Partial (mpath', mctx)
+                Right bs ->
+                  return $
+                    Partial
+                      ( mpath,
+                        Just . (`hashUpdate` bs) $
+                          fromMaybe (hashInit @SHA256) mctx
+                      )
+          )
+          (return $ Partial (Nothing, Nothing))
+          ( \(mpath, mctx) ->
+              return
+                ( show $ fromJust mpath,
+                  show . hashFinalize <$> mctx
+                )
+          )
 
-    -- Execute the stream, grouping at the headers (the Lefts) using the
-    -- above fold, and output the paths and SHA-256 hashes along the way.
-    S.unfold unf undefined
-        & S.groupsBy (\e _ -> isRight e) entryFold
-        & S.mapM_ print
+  -- Execute the stream, grouping at the headers (the Lefts) using the
+  -- above fold, and output the paths and SHA-256 hashes along the way.
+  S.unfold unf undefined
+    & groupByHeader entryFold
+    & S.mapM print
+    & S.fold F.drain
 ```
 
 ## Benchmarks
@@ -71,4 +85,4 @@
 
 The former fulfills the promise of [streamly](https://hackage.haskell.org/package/streamly) and stream fusion. The differences to C are presumably explained by the marshalling of data into the Haskell world and are currently small enough for our purposes.
 
-<sup>†</sup> [Linode](https://linode.com); Debian 10, Dedicated 32GB: 16 CPU, 640GB Storage, 32GB RAM.
+<sup>†</sup> April 2023; [Linode](https://linode.com); Debian 11, Dedicated 32GB: 16 CPU, 640GB Storage, 32GB RAM.
diff --git a/src/Streamly/External/Archive.hs b/src/Streamly/External/Archive.hs
--- a/src/Streamly/External/Archive.hs
+++ b/src/Streamly/External/Archive.hs
@@ -1,13 +1,11 @@
---------------------------------------------------------------------------------
-
-{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
-
---------------------------------------------------------------------------------
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 module Streamly.External.Archive
-    (
-    -- ** Read
+  ( -- ** Read
     readArchive,
+    groupByHeader,
 
     -- ** Header
     Header,
@@ -15,31 +13,44 @@
     headerFileType,
     headerPathName,
     headerPathNameUtf8,
-    headerSize) where
-
---------------------------------------------------------------------------------
+    headerSize,
+  )
+where
 
 import Control.Exception (mask_)
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Data.Either
+import Data.Function
 import Data.Int (Int64)
 import Data.Void (Void)
 import Foreign (Ptr, free, malloc)
 import Foreign.C.Types (CChar, CSize)
+import Streamly.Data.Fold (Fold)
+import qualified Streamly.Data.Parser as P
+import Streamly.Data.Stream.Prelude (Stream)
+import qualified Streamly.Data.Stream.Prelude as S
+import Streamly.Data.Unfold (lmap)
+import Streamly.External.Archive.Internal.Foreign
+  ( Entry,
+    FileType (..),
+    archive_entry_filetype,
+    archive_entry_pathname,
+    archive_entry_pathname_utf8,
+    archive_entry_size,
+    archive_read_data_block,
+    archive_read_free,
+    archive_read_new,
+    archive_read_next_header,
+    archive_read_open_filename,
+    archive_read_support_filter_all,
+    archive_read_support_format_all,
+  )
 import Streamly.Internal.Data.IOFinalizer (newIOFinalizer, runIOFinalizer)
 import Streamly.Internal.Data.Stream.StreamD.Type (Step (..))
-import Streamly.Internal.Data.Unfold (supply)
 import Streamly.Internal.Data.Unfold.Type (Unfold (..))
 
-import qualified Data.ByteString as B
-
-import Streamly.External.Archive.Internal.Foreign (Entry, FileType (..),
-    archive_entry_filetype, archive_entry_pathname, archive_entry_pathname_utf8, archive_entry_size,
-    archive_read_data_block, archive_read_free, archive_read_new, archive_read_next_header,
-    archive_read_open_filename, archive_read_support_filter_all, archive_read_support_format_all)
-
---------------------------------------------------------------------------------
-
 -- | Header information for an entry in the archive.
 newtype Header = Header Entry
 
@@ -59,38 +70,62 @@
 headerSize :: Header -> IO (Maybe Int)
 headerSize (Header e) = archive_entry_size e
 
+-- | A convenience function for grouping @Either Header ByteString@s, usually obtained with
+-- 'readArchive', by the headers. The input @Fold@ processes a single entry (a 'Header' followed by
+-- zero or more @ByteString@s).
+{-# INLINE groupByHeader #-}
+groupByHeader ::
+  (Monad m) =>
+  Fold m (Either Header ByteString) b ->
+  Stream m (Either Header ByteString) ->
+  Stream m b
+groupByHeader itemFold str =
+  str
+    & S.parseMany (P.groupBy (\_ e -> isRight e) itemFold)
+    & fmap
+      ( \case
+          Left _ ->
+            -- groupBy is documented to never fail.
+            error "unexpected parseMany/groupBy error"
+          Right b -> b
+      )
+
 -- | Creates an unfold with which we can stream data out of the given archive.
 {-# INLINE readArchive #-}
 readArchive :: (MonadIO m) => FilePath -> Unfold m Void (Either Header ByteString)
-readArchive fp = supply () $
+readArchive fp =
+  (lmap . const) () $
     Unfold
-        (\(arch, buf, sz, offs, pos, ref, readHeader) ->
-            if readHeader then do
-                me <- liftIO $ archive_read_next_header arch
-                case me of
-                    Nothing -> do
-                        liftIO $ runIOFinalizer ref
-                        return Stop
-                    Just e -> do
-                        return $ Yield (Left $ Header e) (arch, buf, sz, offs, 0, ref, False)
+      ( \(arch, buf, sz, offs, pos, ref, readHeader) ->
+          if readHeader
+            then do
+              me <- liftIO $ archive_read_next_header arch
+              case me of
+                Nothing -> do
+                  liftIO $ runIOFinalizer ref
+                  return Stop
+                Just e -> do
+                  return $ Yield (Left $ Header e) (arch, buf, sz, offs, 0, ref, False)
             else do
-                (bs, done) <- liftIO $ archive_read_data_block arch buf sz offs pos
-                return $
-                    if B.length bs > 0 then
-                        Yield (Right bs) (arch, buf, sz, offs, pos + fromIntegral (B.length bs), ref, done)
-                    else
-                        Skip (arch, buf, sz, offs, pos, ref, done))
-        (\() -> do
-            (arch, buf, sz, offs, ref) <- liftIO . mask_ $ do
-                arch <- liftIO archive_read_new
-                buf :: Ptr (Ptr CChar) <- liftIO malloc
-                sz :: Ptr CSize <- liftIO malloc
-                offs :: Ptr Int64 <- liftIO malloc
-                ref <- newIOFinalizer $ archive_read_free arch >> free buf >> free sz >> free offs
-                return (arch, buf, sz, offs, ref)
-            liftIO $ archive_read_support_filter_all arch
-            liftIO $ archive_read_support_format_all arch
-            liftIO $ archive_read_open_filename arch fp
-            return (arch, buf, sz, offs, 0, ref, True))
-
---------------------------------------------------------------------------------
+              (bs, done) <- liftIO $ archive_read_data_block arch buf sz offs pos
+              return $
+                if B.length bs > 0
+                  then
+                    Yield
+                      (Right bs)
+                      (arch, buf, sz, offs, pos + fromIntegral (B.length bs), ref, done)
+                  else Skip (arch, buf, sz, offs, pos, ref, done)
+      )
+      ( \() -> do
+          (arch, buf, sz, offs, ref) <- liftIO . mask_ $ do
+            arch <- liftIO archive_read_new
+            buf :: Ptr (Ptr CChar) <- liftIO malloc
+            sz :: Ptr CSize <- liftIO malloc
+            offs :: Ptr Int64 <- liftIO malloc
+            ref <- newIOFinalizer $ archive_read_free arch >> free buf >> free sz >> free offs
+            return (arch, buf, sz, offs, ref)
+          liftIO $ archive_read_support_filter_all arch
+          liftIO $ archive_read_support_format_all arch
+          liftIO $ archive_read_open_filename arch fp
+          return (arch, buf, sz, offs, 0, ref, True)
+      )
diff --git a/src/Streamly/External/Archive/Internal/Foreign.hs b/src/Streamly/External/Archive/Internal/Foreign.hs
--- a/src/Streamly/External/Archive/Internal/Foreign.hs
+++ b/src/Streamly/External/Archive/Internal/Foreign.hs
@@ -1,186 +1,186 @@
---------------------------------------------------------------------------------
-
 module Streamly.External.Archive.Internal.Foreign
-    ( Archive
-    , Entry
-    , FileType (..)
-    , archive_read_new
-    , archive_read_support_filter_all
-    , archive_read_support_format_all
-    , archive_read_support_format_gnutar
-    , blockSize
-    , archive_read_open_filename
-    , archive_read_next_header
-    , archive_entry_filetype
-    , archive_entry_pathname
-    , archive_entry_pathname_utf8
-    , archive_entry_size
-    , alloc_archive_read_data_buffer
-    , archive_read_data
-    , archive_read_data_block
-    , archive_read_free ) where
-
---------------------------------------------------------------------------------
+  ( Archive,
+    Entry,
+    FileType (..),
+    archive_read_new,
+    archive_read_support_filter_all,
+    archive_read_support_format_all,
+    archive_read_support_format_gnutar,
+    blockSize,
+    archive_read_open_filename,
+    archive_read_next_header,
+    archive_entry_filetype,
+    archive_entry_pathname,
+    archive_entry_pathname_utf8,
+    archive_entry_size,
+    alloc_archive_read_data_buffer,
+    archive_read_data,
+    archive_read_data_block,
+    archive_read_free,
+  )
+where
 
 import Control.Exception (Exception, mask_, throw)
 import Control.Monad (when)
 import Data.Bits ((.&.))
 import Data.ByteString (ByteString, packCString, packCStringLen)
+import qualified Data.ByteString as B
 import Data.Int (Int64)
 import Foreign (FunPtr, Ptr, nullPtr, peek)
 import Foreign.C.String (CString, peekCString, withCString)
 import Foreign.C.Types (CChar, CInt (CInt), CSize (CSize))
 import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr)
 import Foreign.Marshal.Alloc (mallocBytes)
-import System.Posix.Types (CSsize (CSsize), CMode (CMode))
-
-import qualified Data.ByteString as B
-
---------------------------------------------------------------------------------
+import System.Posix.Types (CMode (CMode), CSsize (CSsize))
 
 data CArchive
+
 data CEntry
 
 foreign import ccall unsafe "archive.h archive_errno"
-    c_archive_errno :: Ptr CArchive -> IO CInt
+  c_archive_errno :: Ptr CArchive -> IO CInt
 
 foreign import ccall unsafe "archive.h archive_error_string"
-    c_archive_error_string :: Ptr CArchive -> IO CString
+  c_archive_error_string :: Ptr CArchive -> IO CString
 
 foreign import ccall unsafe "archive.h archive_read_new"
-    c_archive_read_new :: IO (Ptr CArchive)
+  c_archive_read_new :: IO (Ptr CArchive)
 
 foreign import ccall unsafe "archive.h archive_read_support_filter_all"
-    c_archive_read_support_filter_all :: Ptr CArchive -> IO CInt
+  c_archive_read_support_filter_all :: Ptr CArchive -> IO CInt
 
 foreign import ccall unsafe "archive.h archive_read_support_format_all"
-    c_archive_read_support_format_all :: Ptr CArchive -> IO CInt
+  c_archive_read_support_format_all :: Ptr CArchive -> IO CInt
 
 foreign import ccall unsafe "archive.h archive_read_support_format_gnutar"
-    c_archive_read_support_format_gnutar :: Ptr CArchive -> IO CInt
+  c_archive_read_support_format_gnutar :: Ptr CArchive -> IO CInt
 
 foreign import ccall unsafe "archive.h archive_read_open_filename"
-    c_archive_read_open_filename :: Ptr CArchive -> CString -> CSize -> IO CInt
+  c_archive_read_open_filename :: Ptr CArchive -> CString -> CSize -> IO CInt
 
 foreign import ccall unsafe "archive.h archive_read_next_header2"
-    c_archive_read_next_header2 :: Ptr CArchive -> Ptr CEntry -> IO CInt
+  c_archive_read_next_header2 :: Ptr CArchive -> Ptr CEntry -> IO CInt
 
 foreign import ccall unsafe "archive.h archive_read_data"
-    c_archive_read_data :: Ptr CArchive -> Ptr CChar -> CSize -> IO CSsize -- Todo: Think about la_ssize_t on non-POSIX.
+  -- Todo: Think about la_ssize_t on non-POSIX.
+  c_archive_read_data :: Ptr CArchive -> Ptr CChar -> CSize -> IO CSsize
 
 foreign import ccall unsafe "archive.h archive_read_data_block"
-    c_archive_read_data_block :: Ptr CArchive -> Ptr (Ptr CChar) -> Ptr CSize -> Ptr Int64 -> IO CInt
+  c_archive_read_data_block :: Ptr CArchive -> Ptr (Ptr CChar) -> Ptr CSize -> Ptr Int64 -> IO CInt
 
 foreign import ccall unsafe "archive.h archive_read_free"
-    c_archive_read_free :: Ptr CArchive -> IO CInt
+  c_archive_read_free :: Ptr CArchive -> IO CInt
 
 foreign import ccall unsafe "archive_entry.h archive_entry_filetype"
-    c_archive_entry_filetype :: Ptr CEntry -> IO CMode -- Todo: Think about type on non-POSIX.
+  c_archive_entry_filetype :: Ptr CEntry -> IO CMode -- Todo: Think about type on non-POSIX.
 
 foreign import ccall unsafe "archive_entry.h archive_entry_new"
-    c_archive_entry_new :: IO (Ptr CEntry)
+  c_archive_entry_new :: IO (Ptr CEntry)
 
 -- Similar to c_free_finalizer from ByteString.
 foreign import ccall unsafe "static archive_entry.h &archive_entry_free"
-    c_archive_entry_free_finalizer :: FunPtr (Ptr CEntry -> IO ())
+  c_archive_entry_free_finalizer :: FunPtr (Ptr CEntry -> IO ())
 
 foreign import ccall unsafe "archive_entry.h archive_entry_pathname"
-    c_archive_entry_pathname :: Ptr CEntry -> IO CString
+  c_archive_entry_pathname :: Ptr CEntry -> IO CString
 
 foreign import ccall unsafe "archive_entry.h archive_entry_pathname_utf8"
-    c_archive_entry_pathname_utf8 :: Ptr CEntry -> IO CString
+  c_archive_entry_pathname_utf8 :: Ptr CEntry -> IO CString
 
 foreign import ccall unsafe "archive_entry.h archive_entry_size"
-    c_archive_entry_size :: Ptr CEntry -> IO Int64
+  c_archive_entry_size :: Ptr CEntry -> IO Int64
 
 foreign import ccall unsafe "archive_entry.h archive_entry_size_is_set"
-    c_archive_entry_size_is_set :: Ptr CEntry -> IO CInt
-
---------------------------------------------------------------------------------
+  c_archive_entry_size_is_set :: Ptr CEntry -> IO CInt
 
 -- Documented libarchive return codes.
 data RetCode
-    = RetCodeEOF
-    | RetCodeOK
-    | RetCodeRETRY
-    | RetCodeWARN
-    | RetCodeFAILED
-    | RetCodeFATAL
-    deriving (Show)
+  = RetCodeEOF
+  | RetCodeOK
+  | RetCodeRETRY
+  | RetCodeWARN
+  | RetCodeFAILED
+  | RetCodeFATAL
+  deriving (Show)
 
 retCodes :: [(CInt, RetCode)]
 retCodes =
-    [ (1, RetCodeEOF)
-    , (0, RetCodeOK)
-    , (-10, RetCodeRETRY)
-    , (-20, RetCodeWARN)
-    , (-25, RetCodeFAILED)
-    , (-30, RetCodeFATAL) ]
+  [ (1, RetCodeEOF),
+    (0, RetCodeOK),
+    (-10, RetCodeRETRY),
+    (-20, RetCodeWARN),
+    (-25, RetCodeFAILED),
+    (-30, RetCodeFATAL)
+  ]
 
-data ArchiveError =
-     ArchiveError { err_function :: !String
-                  , err_retcode  :: !(Either CInt RetCode)
-                  , err_number   :: !Int
-                  , err_string   :: !String }
-                  deriving (Show)
+data ArchiveError = ArchiveError
+  { err_function :: !String,
+    err_retcode :: !(Either CInt RetCode),
+    err_number :: !Int,
+    err_string :: !String
+  }
+  deriving (Show)
+
 instance Exception ArchiveError
 
 newtype ErrorString = ErrorString String deriving (Show)
+
 instance Exception ErrorString
 
 archive_error_string :: Ptr CArchive -> IO String
 archive_error_string aptr = do
-    cstr <- c_archive_error_string aptr
-    if cstr == nullPtr
-        then return "archive_error_string returned NULL"
-        else peekCString cstr
+  cstr <- c_archive_error_string aptr
+  if cstr == nullPtr
+    then return "archive_error_string returned NULL"
+    else peekCString cstr
 
 throwArchiveError :: String -> CInt -> Ptr CArchive -> IO noReturn
 throwArchiveError fn rc aptr = do
-    num <- fromIntegral <$> c_archive_errno aptr
-    str <-  archive_error_string aptr
-    throw $ ArchiveError
-        { err_function = fn
-        , err_retcode = maybe (Left rc) Right (lookup rc retCodes)
-        , err_number = num
-        , err_string = str }
-
---------------------------------------------------------------------------------
+  num <- fromIntegral <$> c_archive_errno aptr
+  str <- archive_error_string aptr
+  throw $
+    ArchiveError
+      { err_function = fn,
+        err_retcode = maybe (Left rc) Right (lookup rc retCodes),
+        err_number = num,
+        err_string = str
+      }
 
 newtype Archive = Archive (Ptr CArchive)
 
 newtype Entry = Entry (ForeignPtr CEntry)
 
-data FileType = FileTypeRegular
-              | FileTypeSymlink
-              | FileTypeSocket
-              | FileTypeCharDevice
-              | FileTypeBlockDevice
-              | FileTypeDirectory
-              | FileTypeNamedPipe
-              deriving (Show, Eq)
+data FileType
+  = FileTypeRegular
+  | FileTypeSymlink
+  | FileTypeSocket
+  | FileTypeCharDevice
+  | FileTypeBlockDevice
+  | FileTypeDirectory
+  | FileTypeNamedPipe
+  deriving (Show, Eq)
 
 archive_read_new :: IO Archive
 archive_read_new = do
-    aptr <- c_archive_read_new
-    if aptr == nullPtr
-        then throw $ ErrorString "archive_read_new returned NULL"
-        else return $ Archive aptr
+  aptr <- c_archive_read_new
+  if aptr == nullPtr
+    then throw $ ErrorString "archive_read_new returned NULL"
+    else return $ Archive aptr
 
 archive_read_support_filter_all :: Archive -> IO ()
 archive_read_support_filter_all (Archive aptr) = do
-    rc <- c_archive_read_support_filter_all aptr
-    when (rc /= 0) $ throwArchiveError "archive_read_support_filter_all" rc aptr
+  rc <- c_archive_read_support_filter_all aptr
+  when (rc /= 0) $ throwArchiveError "archive_read_support_filter_all" rc aptr
 
 archive_read_support_format_all :: Archive -> IO ()
 archive_read_support_format_all (Archive aptr) = do
-    rc <- c_archive_read_support_format_all aptr
-    when (rc /= 0) $ throwArchiveError "archive_read_support_format_all" rc aptr
+  rc <- c_archive_read_support_format_all aptr
+  when (rc /= 0) $ throwArchiveError "archive_read_support_format_all" rc aptr
 
 archive_read_support_format_gnutar :: Archive -> IO ()
 archive_read_support_format_gnutar (Archive aptr) = do
-    rc <- c_archive_read_support_format_gnutar aptr
-    when (rc /= 0) $ throwArchiveError "archive_read_support_format_gnutar" rc aptr
+  rc <- c_archive_read_support_format_gnutar aptr
+  when (rc /= 0) $ throwArchiveError "archive_read_support_format_gnutar" rc aptr
 
 -- Fixed block size for now.
 {-# INLINE blockSize #-}
@@ -189,22 +189,22 @@
 
 archive_read_open_filename :: Archive -> FilePath -> IO ()
 archive_read_open_filename (Archive aptr) fp =
-    withCString fp $ \cstr -> do
-        rc <- c_archive_read_open_filename aptr cstr blockSize
-        when (rc /= 0) $ throwArchiveError "archive_read_open_filename" rc aptr
+  withCString fp $ \cstr -> do
+    rc <- c_archive_read_open_filename aptr cstr blockSize
+    when (rc /= 0) $ throwArchiveError "archive_read_open_filename" rc aptr
 
 -- | Returns 'Nothing' if we have reached the end of the archive.
 {-# INLINE archive_read_next_header #-}
 archive_read_next_header :: Archive -> IO (Maybe Entry)
 archive_read_next_header (Archive aptr) = do
-    fpe <- mask_ $ c_archive_entry_new >>= newForeignPtr c_archive_entry_free_finalizer
-    rc <- withForeignPtr fpe $ c_archive_read_next_header2 aptr
-    if rc == 1 then  -- EOF.
-        return Nothing
-    else if rc < 0 then
-        throwArchiveError "archive_read_next_header" rc aptr
+  fpe <- mask_ $ c_archive_entry_new >>= newForeignPtr c_archive_entry_free_finalizer
+  rc <- withForeignPtr fpe $ c_archive_read_next_header2 aptr
+  if rc == 1 -- EOF.
+    then return Nothing
     else
-        return . Just . Entry $ fpe
+      if rc < 0
+        then throwArchiveError "archive_read_next_header" rc aptr
+        else return . Just . Entry $ fpe
 
 {-# INLINE fileTypeAeIFMT #-}
 fileTypeAeIFMT :: CMode
@@ -213,44 +213,44 @@
 {-# INLINE fileTypes #-}
 fileTypes :: [(CMode, FileType)]
 fileTypes =
-    [ (0o0100000, FileTypeRegular)
-    , (0o0120000, FileTypeSymlink)
-    , (0o0140000, FileTypeSocket)
-    , (0o0020000, FileTypeCharDevice)
-    , (0o0060000, FileTypeBlockDevice)
-    , (0o0040000, FileTypeDirectory)
-    , (0o0010000, FileTypeNamedPipe) ]
+  [ (0o0100000, FileTypeRegular),
+    (0o0120000, FileTypeSymlink),
+    (0o0140000, FileTypeSocket),
+    (0o0020000, FileTypeCharDevice),
+    (0o0060000, FileTypeBlockDevice),
+    (0o0040000, FileTypeDirectory),
+    (0o0010000, FileTypeNamedPipe)
+  ]
 
 {-# INLINE archive_entry_filetype #-}
 archive_entry_filetype :: Entry -> IO (Maybe FileType)
 archive_entry_filetype (Entry feptr) = withForeignPtr feptr $ \eptr -> do
-    i <- c_archive_entry_filetype eptr
-    return $ lookup (i .&. fileTypeAeIFMT) fileTypes
+  i <- c_archive_entry_filetype eptr
+  return $ lookup (i .&. fileTypeAeIFMT) fileTypes
 
 {-# INLINE archive_entry_pathname #-}
 archive_entry_pathname :: Entry -> IO (Maybe ByteString)
 archive_entry_pathname (Entry feptr) = withForeignPtr feptr $ \eptr -> do
-    cstr <- c_archive_entry_pathname eptr
-    if cstr == nullPtr
-        then return Nothing
-        else Just <$> packCString cstr
+  cstr <- c_archive_entry_pathname eptr
+  if cstr == nullPtr
+    then return Nothing
+    else Just <$> packCString cstr
 
 {-# INLINE archive_entry_pathname_utf8 #-}
 archive_entry_pathname_utf8 :: Entry -> IO (Maybe ByteString)
 archive_entry_pathname_utf8 (Entry feptr) = withForeignPtr feptr $ \eptr -> do
-    cstr <- c_archive_entry_pathname_utf8 eptr
-    if cstr == nullPtr
-        then return Nothing
-        else Just <$> packCString cstr
+  cstr <- c_archive_entry_pathname_utf8 eptr
+  if cstr == nullPtr
+    then return Nothing
+    else Just <$> packCString cstr
 
 {-# INLINE archive_entry_size #-}
 archive_entry_size :: Entry -> IO (Maybe Int)
 archive_entry_size (Entry feptr) = withForeignPtr feptr $ \eptr -> do
-    size_is_set <- (/= 0) <$> c_archive_entry_size_is_set eptr
-    if size_is_set then
-        Just . fromIntegral <$> c_archive_entry_size eptr
-    else
-        return Nothing
+  size_is_set <- (/= 0) <$> c_archive_entry_size_is_set eptr
+  if size_is_set
+    then Just . fromIntegral <$> c_archive_entry_size eptr
+    else return Nothing
 
 -- | Please free after use.
 alloc_archive_read_data_buffer :: IO (Ptr CChar)
@@ -261,40 +261,47 @@
 {-# INLINE archive_read_data #-}
 archive_read_data :: Archive -> Ptr CChar -> IO (Maybe ByteString)
 archive_read_data (Archive aptr) buf = do
-    rb <- c_archive_read_data aptr buf blockSize
-    if rb == 0 then
-        return Nothing
-    else if rb < 0 then
-        throwArchiveError "archive_read_data" (fromIntegral rb) aptr
+  rb <- c_archive_read_data aptr buf blockSize
+  if rb == 0
+    then return Nothing
     else
-        Just <$> packCStringLen (buf, fromIntegral rb)
+      if rb < 0
+        then throwArchiveError "archive_read_data" (fromIntegral rb) aptr
+        else Just <$> packCStringLen (buf, fromIntegral rb)
 
 {-# INLINE archive_read_data_block #-}
-archive_read_data_block :: Archive -> Ptr (Ptr CChar) -> Ptr CSize -> Ptr Int64 -> Int64 -> IO (ByteString, Bool)
+archive_read_data_block ::
+  Archive ->
+  Ptr (Ptr CChar) ->
+  Ptr CSize ->
+  Ptr Int64 ->
+  Int64 ->
+  IO (ByteString, Bool)
 archive_read_data_block (Archive aptr) buf sz offs pos = do
-    rc <- c_archive_read_data_block aptr buf sz offs
-    if rc < 0 then
-        throwArchiveError "archive_read_data_block" (fromIntegral rc) aptr
-    else if rc == 0 || rc == 1 then do -- OK or EOF.
-        bs <- peek buf >>= \buf' -> peek sz >>= \sz' -> packCStringLen (buf', fromIntegral sz')
-        offs' <- peek offs
-        -- pos: Where we are currently located and where the data goes normally (for non-sparse files).
-        -- offs': Where libarchive is asking us to position the data.
-        if offs' == pos then
-            return (bs, rc == 1)
-        else if offs' > pos then do
-            -- For a sparse file, we need to prepend zeroes to the normal data.
-            let diff = offs' - pos
-            let bs' = B.replicate (fromIntegral diff) 0 `B.append` bs
-            return (bs', rc == 1)
-        else
-            throw $ ErrorString "archive_read_data_block: unexpected offset"
+  rc <- c_archive_read_data_block aptr buf sz offs
+  if rc < 0
+    then throwArchiveError "archive_read_data_block" (fromIntegral rc) aptr
     else
-        throw $ ErrorString "archive_read_data_block: unexpected return code"
+      if rc == 0 || rc == 1
+        then do
+          -- OK or EOF.
+          bs <- peek buf >>= \buf' -> peek sz >>= \sz' -> packCStringLen (buf', fromIntegral sz')
+          offs' <- peek offs
+          -- pos: Where we are currently located and where the data goes normally (for non-sparse
+          -- files). offs': Where libarchive is asking us to position the data.
+          if offs' == pos
+            then return (bs, rc == 1)
+            else
+              if offs' > pos
+                then do
+                  -- For a sparse file, we need to prepend zeroes to the normal data.
+                  let diff = offs' - pos
+                  let bs' = B.replicate (fromIntegral diff) 0 `B.append` bs
+                  return (bs', rc == 1)
+                else throw $ ErrorString "archive_read_data_block: unexpected offset"
+        else throw $ ErrorString "archive_read_data_block: unexpected return code"
 
 archive_read_free :: Archive -> IO ()
 archive_read_free (Archive aptr) = do
-    rc <- c_archive_read_free aptr
-    when (rc /= 0) $ throwArchiveError "archive_read_free" rc aptr
-
---------------------------------------------------------------------------------
+  rc <- c_archive_read_free aptr
+  when (rc /= 0) $ throwArchiveError "archive_read_free" rc aptr
diff --git a/streamly-archive.cabal b/streamly-archive.cabal
--- a/streamly-archive.cabal
+++ b/streamly-archive.cabal
@@ -1,13 +1,6 @@
-cabal-version: 1.12
-
--- This file has been generated from package.yaml by hpack version 0.34.4.
---
--- see: https://github.com/sol/hpack
---
--- hash: 3fcf39b51710099491d88d24c7309b32d8799b3ab5c85856dddb8b74361e25be
-
+cabal-version:  3.0
 name:           streamly-archive
-version:        0.1.0
+version:        0.2.0
 synopsis:       Stream data from archives using the streamly library.
 description:    Please see the README on GitHub at <https://github.com/shlok/streamly-archive#readme>
 category:       Archive, Codec, Streaming, Streamly
@@ -15,8 +8,8 @@
 bug-reports:    https://github.com/shlok/streamly-archive/issues
 author:         Shlok Datye
 maintainer:     sd-haskell@quant.is
-copyright:      2021 Shlok Datye
-license:        BSD3
+copyright:      2023 Shlok Datye
+license:        BSD-3-Clause
 license-file:   LICENSE
 build-type:     Simple
 extra-source-files:
@@ -35,6 +28,8 @@
       Streamly.External.Archive.Internal.Foreign
   other-modules:
       Paths_streamly_archive
+  autogen-modules:
+      Paths_streamly_archive
   hs-source-dirs:
       src
   ghc-options: -Wall
@@ -42,8 +37,9 @@
       archive
   build-depends:
       base >=4.7 && <5
-    , bytestring ==0.10.*
-    , streamly ==0.8.*
+    , bytestring ==0.11.*
+    , streamly ==0.9.*
+    , streamly-core ==0.1.*
   default-language: Haskell2010
 
 test-suite streamly-archive-test
@@ -52,6 +48,8 @@
   other-modules:
       Streamly.External.Archive.Tests
       Paths_streamly_archive
+  autogen-modules:
+      Paths_streamly_archive
   hs-source-dirs:
       test
   ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
@@ -60,11 +58,12 @@
   build-depends:
       QuickCheck >=2.13.2 && <2.15
     , base >=4.7 && <5
-    , bytestring ==0.10.*
+    , bytestring ==0.11.*
     , cryptonite >=0.26
     , directory >=1.3.6.0 && <1.4
     , filepath >=1.4.2.1 && <1.5
-    , streamly ==0.8.*
+    , streamly ==0.9.*
+    , streamly-core ==0.1.*
     , streamly-archive
     , tar >=0.5.1.1 && <0.6
     , tasty >=1.2.3 && <1.5
diff --git a/test/Streamly/External/Archive/Tests.hs b/test/Streamly/External/Archive/Tests.hs
--- a/test/Streamly/External/Archive/Tests.hs
+++ b/test/Streamly/External/Archive/Tests.hs
@@ -1,24 +1,24 @@
---------------------------------------------------------------------------------
-
-{-# LANGUAGE NumericUnderscores, OverloadedStrings #-}
-
---------------------------------------------------------------------------------
+{-# LANGUAGE NumericUnderscores #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 module Streamly.External.Archive.Tests (tests) where
 
---------------------------------------------------------------------------------
-
+import qualified Codec.Archive.Tar as Tar
 import Codec.Compression.GZip (compress)
 import Control.Monad (forM)
 import Crypto.Random.Entropy (getEntropy)
 import Data.Bifunctor (first)
 import Data.ByteString (ByteString, append)
+import qualified Data.ByteString as B
 import Data.ByteString.Char8 (unpack)
+import qualified Data.ByteString.Lazy as LB
 import Data.Char (chr, ord)
-import Data.Either (isRight)
 import Data.Function ((&))
 import Data.List (nub, sort)
 import Data.Maybe (fromJust)
+import qualified Streamly.Data.Stream.Prelude as S
+import Streamly.External.Archive
+import Streamly.External.Archive.Internal.Foreign (blockSize)
 import Streamly.Internal.Data.Fold.Type (Fold (Fold), Step (Partial))
 import System.Directory (createDirectoryIfMissing)
 import System.FilePath (addTrailingPathSeparator, hasTrailingPathSeparator, joinPath, takeDirectory)
@@ -29,134 +29,151 @@
 import Test.Tasty.HUnit (assertBool, assertEqual, testCase)
 import Test.Tasty.QuickCheck (testProperty)
 
-import qualified Codec.Archive.Tar as Tar
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as LB
-import qualified Streamly.Prelude as S
-
-import Streamly.External.Archive (FileType (..), headerFileType, headerPathName, headerSize, readArchive)
-import Streamly.External.Archive.Internal.Foreign (blockSize)
-
---------------------------------------------------------------------------------
-
 tests :: [TestTree]
 tests =
-    [ testTar False
-    , testTar True
-    , testSparse ]
-
---------------------------------------------------------------------------------
+  [ testTar False,
+    testTar True,
+    testSparse
+  ]
 
 -- | Use other libraries to create a tar (or tar.gz) file containing random data,
 -- read the file back using our library, and check if the results are as expected.
 testTar :: Bool -> TestTree
-testTar gz = testProperty ("tar (" ++ (if gz then "gz" else "no gz") ++ ")") $ monadicIO $  do
-    -- Generate a random file system hierarchy.
-    hierarchy <- pick $ randomHierarchy "" 4 4 5
+testTar gz = testProperty ("tar (" ++ (if gz then "gz" else "no gz") ++ ")") $ monadicIO $ do
+  -- Generate a random file system hierarchy.
+  hierarchy <- pick $ randomHierarchy "" 4 4 5
 
-    -- Create a new temporary directory, write our hierarchy into
-    -- a "files" subdirectory of the temporary directory, use other
-    -- libraries to create files.tar (or files.tar.gz), read the file
-    -- back using our library, and check if the results are as expected.
-    run . withSystemTempDirectory "archive-streaming-testZip" $ \tmpDir -> do
-        let filesDir = joinPath [tmpDir, "files"]
-        createDirectoryIfMissing True filesDir
-        pathsAndByteStrings <- writeHierarchy filesDir hierarchy
+  -- Create a new temporary directory, write our hierarchy into
+  -- a "files" subdirectory of the temporary directory, use other
+  -- libraries to create files.tar (or files.tar.gz), read the file
+  -- back using our library, and check if the results are as expected.
+  run . withSystemTempDirectory "archive-streaming-testZip" $ \tmpDir -> do
+    let filesDir = joinPath [tmpDir, "files"]
+    createDirectoryIfMissing True filesDir
+    pathsAndByteStrings <- writeHierarchy filesDir hierarchy
 
-        let archFile = joinPath [tmpDir, "files.tar" ++ (if gz then ".gz" else "")]
-        LB.writeFile archFile . (if gz then compress else id) . Tar.write =<< Tar.pack tmpDir ["files"]
+    let archFile = joinPath [tmpDir, "files.tar" ++ (if gz then ".gz" else "")]
+    LB.writeFile archFile . (if gz then compress else id) . Tar.write =<< Tar.pack tmpDir ["files"]
 
-        let fileFold = Fold
-                (\(mfp, mtyp, msz, mbs) e ->
-                    case e of
-                        Left h -> do
-                            mfp_ <- headerPathName h
-                            mtyp_ <- headerFileType h
-                            msz_ <- headerSize h
-                            return $ Partial (unpack <$> mfp_, mtyp_, msz_, mbs)
-                        Right bs -> return $ Partial (mfp, mtyp, msz, case mbs of
+    let fileFold =
+          Fold
+            ( \(mfp, mtyp, msz, mbs) e ->
+                case e of
+                  Left h -> do
+                    mfp_ <- headerPathName h
+                    mtyp_ <- headerFileType h
+                    msz_ <- headerSize h
+                    return $ Partial (unpack <$> mfp_, mtyp_, msz_, mbs)
+                  Right bs ->
+                    return $
+                      Partial
+                        ( mfp,
+                          mtyp,
+                          msz,
+                          case mbs of
                             Nothing -> Just bs
-                            Just bs' -> Just $ bs' `append` bs))
-                (return $ Partial (Nothing, Nothing, Nothing, Nothing))
-                return
+                            Just bs' -> Just $ bs' `append` bs
+                        )
+            )
+            (return $ Partial (Nothing, Nothing, Nothing, Nothing))
+            return
 
-        pathsFileTypesSizesAndByteStrings <-
-            S.unfold (readArchive archFile) undefined
-            & S.groupsBy (\a _ -> isRight a) fileFold
-            & S.map (\(mfp, mtyp, msz, mbs) -> (fromJust mfp, fromJust mtyp, msz, mbs))
-            & S.toList
+    pathsFileTypesSizesAndByteStrings <-
+      S.unfold (readArchive archFile) undefined
+        & groupByHeader fileFold
+        & fmap (\(mfp, mtyp, msz, mbs) -> (fromJust mfp, fromJust mtyp, msz, mbs))
+        & S.toList
 
-        -- Make the file paths and ByteStrings comparable and compare them.
-        let pathsAndByteStrings_ = sort $ map (first ("files/"++)) (("", Nothing) : pathsAndByteStrings)
-        let pathAndByteStrings2_ = sort . map (\(x, _, _, y) -> (x, y)) $ pathsFileTypesSizesAndByteStrings
-        let samePathsAndByteStrings = pathsAndByteStrings_ == pathAndByteStrings2_
+    -- Make the file paths and ByteStrings comparable and compare them.
+    let pathsAndByteStrings_ =
+          sort $ map (first ("files/" ++)) (("", Nothing) : pathsAndByteStrings)
+    let pathAndByteStrings2_ =
+          sort . map (\(x, _, _, y) -> (x, y)) $ pathsFileTypesSizesAndByteStrings
+    let samePathsAndByteStrings = pathsAndByteStrings_ == pathAndByteStrings2_
 
-        -- Check FileType.
-        let fileTypesCorrect = all (\(fp, typ, _, _) ->
+    -- Check FileType.
+    let fileTypesCorrect =
+          all
+            ( \(fp, typ, _, _) ->
                 if hasTrailingPathSeparator fp
-                 then typ == FileTypeDirectory
-                 else typ == FileTypeRegular) pathsFileTypesSizesAndByteStrings
+                  then typ == FileTypeDirectory
+                  else typ == FileTypeRegular
+            )
+            pathsFileTypesSizesAndByteStrings
 
-        -- Check header file size.
-        let fileSizeCorrect = all (\(_, _, msz, mbs) ->
+    -- Check header file size.
+    let fileSizeCorrect =
+          all
+            ( \(_, _, msz, mbs) ->
                 case (msz, mbs) of
-                    (Nothing, _) -> False -- The size is always available.
-                    (Just sz, Nothing) -> sz == 0 -- File or directory.
-                    (Just sz, Just bs) -> fromIntegral sz == B.length bs) pathsFileTypesSizesAndByteStrings
+                  (Nothing, _) -> False -- The size is always available.
+                  (Just sz, Nothing) -> sz == 0 -- File or directory.
+                  (Just sz, Just bs) -> fromIntegral sz == B.length bs
+            )
+            pathsFileTypesSizesAndByteStrings
 
-        return $ samePathsAndByteStrings && fileTypesCorrect && fileSizeCorrect
+    return $ samePathsAndByteStrings && fileTypesCorrect && fileSizeCorrect
 
--- | Read a fixed sparse file (sparse.tar) using our library and make sure the results are as expected.
--- (The file was created manually on Linux with "cp --sparse=always" to create the sparse files and
--- "tar -Scvf" to create the archive. We were unable to do the equivalent thing on macOS Mojave / APFS.)
+-- | Read a fixed sparse file (sparse.tar) using our library and make sure the results are as
+-- expected. (The file was created manually on Linux with "cp --sparse=always" to create the sparse
+-- files and "tar -Scvf" to create the archive. We were unable to do the equivalent thing on macOS
+-- Mojave / APFS.)
 testSparse :: TestTree
 testSparse = testCase "sparse" $ do
-    let fileFold = Fold
-            (\(mfp, mbs) e ->
-                case e of
-                    Left h -> do
-                        mfp_ <- headerPathName h
-                        return $ Partial (unpack <$> mfp_, mbs)
-                    Right bs -> return $ Partial (mfp, case mbs of
-                        Nothing -> Just bs
-                        Just bs' -> Just $ bs' `append` bs))
-            (return $ Partial (Nothing, Nothing))
-            return
-
-    archive <-
-        S.unfold (readArchive "test/data/sparse.tar") undefined
-        & S.groupsBy (\a _ -> isRight a) fileFold
-        & S.map (\(mfp, mbs) -> (fromJust mfp, fromJust mbs))
-        & S.toList
+  let fileFold =
+        Fold
+          ( \(mfp, mbs) e ->
+              case e of
+                Left h -> do
+                  mfp_ <- headerPathName h
+                  return $ Partial (unpack <$> mfp_, mbs)
+                Right bs ->
+                  return $
+                    Partial
+                      ( mfp,
+                        case mbs of
+                          Nothing -> Just bs
+                          Just bs' -> Just $ bs' `append` bs
+                      )
+          )
+          (return $ Partial (Nothing, Nothing))
+          return
 
-    assertEqual "" (map fst archive) ["zero", "zeroZero", "zeroAsdf", "asdfZero"]
+  archive <-
+    S.unfold (readArchive "test/data/sparse.tar") undefined
+      & groupByHeader fileFold
+      & fmap (\(mfp, mbs) -> (fromJust mfp, fromJust mbs))
+      & S.toList
 
-    let tenMb = 10_000_000
-    let zero = B.replicate tenMb 0
-    let asdf = "asdf"
+  assertEqual "" (map fst archive) ["zero", "zeroZero", "zeroAsdf", "asdfZero"]
 
-    assertBool "unexpected bytestring (1)" $ snd (head archive) == zero
-    assertBool "unexpected bytestring (2)" $ snd (archive !! 1) == zero `B.append` zero
-    assertBool "unexpected bytestring (3)" $ snd (archive !! 2) == zero `B.append` asdf
-    assertBool "unexpected bytestring (4)" $ snd (archive !! 3) == asdf `B.append` zero
+  let tenMb = 10_000_000
+  let zero = B.replicate tenMb 0
+  let asdf = "asdf"
 
---------------------------------------------------------------------------------
+  assertBool "unexpected bytestring (1)" $ snd (head archive) == zero
+  assertBool "unexpected bytestring (2)" $ snd (archive !! 1) == zero `B.append` zero
+  assertBool "unexpected bytestring (3)" $ snd (archive !! 2) == zero `B.append` asdf
+  assertBool "unexpected bytestring (4)" $ snd (archive !! 3) == asdf `B.append` zero
 
 -- | Writes a given hierarchy of relative paths (created with 'randomHierarchy') to disk
 -- in the specified directory and returns the same hierarchy except with actual ByteStrings
 -- instead of lengths. Note: The original relative paths are returned back unaltered.
-writeHierarchy :: FilePath -> [(FilePath, Maybe Int)]  -> IO [(FilePath, Maybe ByteString)]
+writeHierarchy :: FilePath -> [(FilePath, Maybe Int)] -> IO [(FilePath, Maybe ByteString)]
 writeHierarchy writeDir = mapM $ \(p, mBsLen) ->
-    let fullp = joinPath [writeDir, p]
-     in case mBsLen of
-            Just bsLen -> do
-                createDirectoryIfMissing True (takeDirectory fullp)
-                bs <- getEntropy (fromIntegral bsLen)
-                B.writeFile fullp bs
-                return (p, if bsLen == 0
-                            then Nothing -- Our library yields no ByteString at all for empty files.
-                            else Just bs)
-            Nothing -> createDirectoryIfMissing True fullp >> return (p, Nothing)
+  let fullp = joinPath [writeDir, p]
+   in case mBsLen of
+        Just bsLen -> do
+          createDirectoryIfMissing True (takeDirectory fullp)
+          bs <- getEntropy (fromIntegral bsLen)
+          B.writeFile fullp bs
+          return
+            ( p,
+              if bsLen == 0
+                then Nothing -- Our library yields no ByteString at all for empty files.
+                else Just bs
+            )
+        Nothing -> createDirectoryIfMissing True fullp >> return (p, Nothing)
 
 -- | Recursively generates a random hierarchy of relative paths to files and
 -- directories. (Nothing is written to disk; only the paths are returned.)
@@ -164,31 +181,45 @@
 -- provided in case of a file; 'Nothing' in the case of a directory.
 randomHierarchy :: FilePath -> Int -> Int -> Int -> Gen [(FilePath, Maybe Int)]
 randomHierarchy dirPath maxFiles maxDirs maxDepth = do
-    numFiles <- choose (0, maxFiles)
-    fileComps <- nub <$> vectorOf numFiles pathComponent
-    let filePaths = map (\c -> joinPath [dirPath, c]) fileComps
-    bsLengths <- map Just <$> vectorOf (length filePaths)
-                                    ( frequency [ (1, choose (0, 5))
-                                                , (1, choose (blockSize - 5, blockSize + 5))
-                                                , (1, choose (0, 3 * blockSize)) ] )
+  numFiles <- choose (0, maxFiles)
+  fileComps <- nub <$> vectorOf numFiles pathComponent
+  let filePaths = map (\c -> joinPath [dirPath, c]) fileComps
+  bsLengths <-
+    map Just
+      <$> vectorOf
+        (length filePaths)
+        ( frequency
+            [ (1, choose (0, 5)),
+              (1, choose (blockSize - 5, blockSize + 5)),
+              (1, choose (0, 3 * blockSize))
+            ]
+        )
 
-    numDirs <- choose (0, maxDirs)
-    dirComps <- nub . filter (not . (`elem` fileComps))
-                    <$> vectorOf (if maxDepth <= 0 then 0 else numDirs) pathComponent
-    -- libarchive reads back directory paths with a trailing separator.
-    let dirPaths = map (\c -> addTrailingPathSeparator $ joinPath [dirPath, c]) dirComps
+  numDirs <- choose (0, maxDirs)
+  dirComps <-
+    nub . filter (not . (`elem` fileComps))
+      <$> vectorOf (if maxDepth <= 0 then 0 else numDirs) pathComponent
+  -- libarchive reads back directory paths with a trailing separator.
+  let dirPaths = map (\c -> addTrailingPathSeparator $ joinPath [dirPath, c]) dirComps
 
-    recursion <- concat <$> forM dirPaths (\dirPath' ->
-                                randomHierarchy dirPath' (maxFiles `div` 2) (maxDirs `div` 2) (maxDepth - 1))
+  recursion <-
+    concat
+      <$> forM
+        dirPaths
+        ( \dirPath' ->
+            randomHierarchy dirPath' (maxFiles `div` 2) (maxDirs `div` 2) (maxDepth - 1)
+        )
 
-    return $ zip filePaths bsLengths ++ zip dirPaths (repeat Nothing) ++ recursion
+  return $ zip filePaths bsLengths ++ zip dirPaths (repeat Nothing) ++ recursion
 
 -- | Generates a random path component of length between 1 and 10, e.g., "HO53UVKQ".
 -- For compatibility with case-insensitive file systems, uses only one case.
 pathComponent :: Gen String
 pathComponent = do
-    len <- choose (1, 10)
-    vectorOf len $ chr <$> frequency [ (1, choose (ord 'A', ord 'Z'))
-                                     , (1, choose (ord '0', ord '9')) ]
-
---------------------------------------------------------------------------------
+  len <- choose (1, 10)
+  vectorOf len $
+    chr
+      <$> frequency
+        [ (1, choose (ord 'A', ord 'Z')),
+          (1, choose (ord '0', ord '9'))
+        ]
diff --git a/test/TestSuite.hs b/test/TestSuite.hs
--- a/test/TestSuite.hs
+++ b/test/TestSuite.hs
@@ -1,22 +1,16 @@
---------------------------------------------------------------------------------
-
 module Main where
 
---------------------------------------------------------------------------------
-
-import Test.Tasty (TestTree, defaultMain, testGroup)
-
 import qualified Streamly.External.Archive.Tests
-
---------------------------------------------------------------------------------
+import Test.Tasty (TestTree, defaultMain, testGroup)
 
 main :: IO ()
 main = defaultMain tests
 
 tests :: TestTree
 tests =
-    testGroup "Tests"
-        [ testGroup "Streamly.External.Archive.Tests"
-                     Streamly.External.Archive.Tests.tests ]
-
---------------------------------------------------------------------------------
+  testGroup
+    "Tests"
+    [ testGroup
+        "Streamly.External.Archive.Tests"
+        Streamly.External.Archive.Tests.tests
+    ]
