diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for construct
 
+## 0.3.2 -- 2024-12-15
+
+* Added combinators `manyTill` and `sequence`
+* Slightly improved documentation
+
 ## 0.3.1.2 -- 2023-12-19
 
 * Fixed Cabal warnings
diff --git a/construct.cabal b/construct.cabal
--- a/construct.cabal
+++ b/construct.cabal
@@ -1,6 +1,6 @@
 cabal-version:       >=1.10
 name:                construct
-version:             0.3.1.2
+version:             0.3.2
 synopsis:            Haskell version of the Construct library for easy specification of file formats
 description:
    A Haskell version of the <https://construct.readthedocs.io/en/latest/intro.html Construct> library for Python. A
diff --git a/src/Construct.hs b/src/Construct.hs
--- a/src/Construct.hs
+++ b/src/Construct.hs
@@ -1,5 +1,7 @@
 {-# LANGUAGE FlexibleContexts, FlexibleInstances, GADTs, RankNTypes, ScopedTypeVariables, TypeOperators #-}
 
+-- | Declarative and symmetrical specification of binary and textual data formats.
+
 module Construct
 (
   -- * The type
@@ -7,7 +9,7 @@
 
   -- * Combinators
   (Construct.<$), (Construct.*>), (Construct.<*), (Construct.<|>), (<+>), (<?>),
-  empty, optional, optionWithDefault, pair, deppair, many, some, sepBy, count,
+  empty, optional, optionWithDefault, pair, deppair, many, some, manyTill, sepBy, Construct.sequence, count,
   -- ** Self-referential record support
   mfix, record, recordWith,
   -- ** Mapping over a 'Format'
@@ -306,9 +308,18 @@
                   go (Done r _) = pure r
                   go (Partial cont) = Input.anyToken >>= go . cont
 
+-- | Sequence a list of formats into a list format.
+sequence :: forall m n s a. (Monad m, AlternativeFail n, Monoid s, Eq a, Show a) => [Format m n s a] -> Format m n s [a]
+sequence fs = Format{
+  parse = traverse parse fs,
+  serialize = \xs-> if length fs == length xs then foldr (liftA2 (<>)) (pure mempty) $ zipWith serialize fs xs
+                    else failure $ "Expected list of length " <> shows (length fs) (", not " <> show (length xs))}
+
 count :: (Applicative m, AlternativeFail n, Show a, Monoid s) => Int -> Format m n s a -> Format m n s [a]
 -- | Repeats the argument format the given number of times.
 --
+-- The property @count n f == Construct.sequence (replicate n f)@ holds.
+--
 -- >>> testParse (count 4 byte) (ByteString.pack [1,2,3,4,5])
 -- Right [([1,2,3,4],"\ENQ")]
 -- >>> testSerialize (count 4 byte) [1,2,3,4,5]
@@ -396,6 +407,14 @@
 some f = Format{
    parse = Applicative.some (parse f),
    serialize = maybe (failure "[]") (fmap sconcat . traverse (serialize f)) . nonEmpty}
+
+manyTill :: (Alternative m, Applicative n, Monoid s) => Format m n s a -> Format m n s () -> Format m n s [a]
+-- | In the parsing direction, the same as the regular 'Parser.manyTill': parses the item zero or more times until
+-- the terminator succeeds. When serializing, makes sure to append the terminator. Beware, for performance reasons
+-- the function does /not/ verify that no item's serialization can be parsed via the terminator.
+manyTill item end = Format{
+   parse = Parser.manyTill (parse item) (parse end),
+   serialize = \xs-> liftA2 ((<>) . mconcat) (traverse (serialize item) xs) (serialize end ())}
 
 sepBy :: (Alternative m, Applicative n, Monoid s) => Format m n s a -> Format m n s () -> Format m n s [a]
 -- | Represents any number of values formatted using the first argument, separated by the second format argumewnt in
diff --git a/src/Construct/Internal.hs b/src/Construct/Internal.hs
--- a/src/Construct/Internal.hs
+++ b/src/Construct/Internal.hs
@@ -2,11 +2,11 @@
 
 -- | The central type. The four type parameters are:
 --
---   * @m@ is the type of the parser for the format
---   * @n@ is the container type for the serialized form of the value, typically 'Identity' unless something
+--   * @m@, type of the parser for the format
+--   * @n@, container type for the serialized form of the value, typically 'Identity' unless something
 --     'Alternative' is called for.
---   * @s@ is the type of the serialized value, typically 'ByteString'
---   * @a@ is the type of the value in the program
+--   * @s@, type of the serialized value, typically 'ByteString'
+--   * @a@, type of the parsed value in memory
 --
 -- The @parse@ and @serialize@ fields can be used to perform the two sides of the conversion between the in-memory and
 -- serialized form of the value.
