diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for json-query
 
+## 0.2.3.0 -- 2024-01-18
+
+* Add `Json.Parser.word32`
+
 ## 0.2.2.0 -- 2023-08-09
 
 * Add `Json.Errors.hPut` to make it less verbose to print
diff --git a/json-query.cabal b/json-query.cabal
--- a/json-query.cabal
+++ b/json-query.cabal
@@ -1,19 +1,20 @@
-cabal-version: 2.4
-name: json-query
-version: 0.2.2.0
-synopsis: Kitchen sink for querying JSON
+cabal-version:      2.4
+name:               json-query
+version:            0.2.3.0
+synopsis:           Kitchen sink for querying JSON
 description:
   The library complements json-syntax by making available several
   common access patterns. The utilities provided by this library
   only query JSON. They do not update it.
-bug-reports: https://github.com/andrewthad/json-query
-license: BSD-3-Clause
-license-file: LICENSE
-author: Andrew Martin
-maintainer: andrew.thaddeus@gmail.com
-copyright: 2020 Andrew Martin
-category: Data
-build-type: Simple
+
+bug-reports:        https://github.com/andrewthad/json-query
+license:            BSD-3-Clause
+license-file:       LICENSE
+author:             Andrew Martin
+maintainer:         andrew.thaddeus@gmail.com
+copyright:          2020 Andrew Martin
+category:           Data
+build-type:         Simple
 extra-source-files: CHANGELOG.md
 
 library
@@ -24,44 +25,50 @@
     Json.Errors
     Json.Parser
     Json.Path
+
   build-depends:
-    , array-chunks >=0.1.2 && <0.2
-    , base >=4.12 && <5
-    , bytebuild >=0.3.5 && <0.4
-    , bytestring >=0.10 && <0.12
-    , contiguous >=0.6.1
-    , json-syntax >=0.2.2 && <0.3
-    , primitive >=0.7 && <0.10
-    , primitive-unlifted >=0.1.3 && <2.2
-    , profunctors >=5.6
-    , scientific-notation >=0.1.5 && <0.2
-    , text-short >=0.1.3 && <0.2
-    , transformers >=0.5.6 && <0.6
-  hs-source-dirs: src
+    , array-chunks         >=0.1.2 && <0.2
+    , base                 >=4.12  && <5
+    , bytebuild            >=0.3.5 && <0.4
+    , bytestring           >=0.10  && <0.12
+    , contiguous           >=0.6.1
+    , json-syntax          >=0.2.2 && <0.3
+    , primitive            >=0.7   && <0.10
+    , primitive-unlifted   >=0.1.3 && <2.2
+    , profunctors          >=5.6
+    , scientific-notation  >=0.1.5 && <0.2
+    , text-short           >=0.1.3 && <0.2
+    , transformers         >=0.5.6 && <0.7
+
+  hs-source-dirs:   src
   default-language: Haskell2010
-  ghc-options: -Wall -O2
+  ghc-options:      -Wall -O2
 
 test-suite test
   default-language: Haskell2010
-  type: exitcode-stdio-1.0
-  hs-source-dirs: test
-  main-is: Main.hs
+  type:             exitcode-stdio-1.0
+  hs-source-dirs:   test
+  main-is:          Main.hs
   other-modules:
     Arrowy
     DogHouse
     Monadic
-  ghc-options: -Wall -O2
+
+  ghc-options:      -Wall -O2
   build-depends:
     , array-chunks
-    , base >=4.12.0.0 && <5
-    , byteslice >=0.1.3
+    , base                 >=4.12.0.0 && <5
+    , bytebuild
+    , byteslice            >=0.1.3
     , bytestring
+    , hspec                >=2.11.7
     , json-query
     , json-syntax
+    , neat-interpolation   >=0.5.1
     , primitive
-    , bytebuild
-    , tasty >=1.2.3
-    , tasty-hunit >=0.10.0.2
+    , scientific-notation  ==0.1.6.0
+    , tasty                >=1.2.3
+    , tasty-hspec          >=1.2.0.4
+    , tasty-hunit          >=0.10.0.2
+    , text                 >=1.2
     , text-short
-    , neat-interpolation >=0.5.1
-    , text >=1.2
diff --git a/src/Json/Arrow.hs b/src/Json/Arrow.hs
--- a/src/Json/Arrow.hs
+++ b/src/Json/Arrow.hs
@@ -8,9 +8,11 @@
 module Json.Arrow
   ( Parser
   , type (~>)
-  -- * Run Parser
+
+    -- * Run Parser
   , run
-  -- * Primitive Parsers
+
+    -- * Primitive Parsers
   , object
   , array
   , string
@@ -18,48 +20,51 @@
   , number
   , boolean
   , null
-  -- ** Object Members
-  , Members(..)
+
+    -- ** Object Members
+  , Members (..)
   , member
   , memberOpt
   , foldMembers
-  -- ** Array Elements
+
+    -- ** Array Elements
   , Elements
   , foldl'
   , map
-  -- * Primitive Combinators
+
+    -- * Primitive Combinators
   , fail
   , failZero
-  -- * Trivial Combinators
+
+    -- * Trivial Combinators
   , withObject
   , withArray
   , fromNull
   , int
   , word16
   , word64
-  -- * Conversion
+
+    -- * Conversion
   , liftMaybe
   ) where
 
-import Prelude hiding (id, (.), fail, map, null)
+import Prelude hiding (fail, id, map, null, (.))
 
-import Control.Arrow ((>>>))
-import Control.Arrow (Arrow(..))
-import Control.Arrow (ArrowZero(..),ArrowPlus(..),ArrowChoice(..),ArrowApply(..))
-import Control.Category (Category(..))
+import Control.Arrow (Arrow (..), ArrowApply (..), ArrowChoice (..), ArrowPlus (..), ArrowZero (..), (>>>))
+import Control.Category (Category (..))
 import Control.Monad.ST (runST)
-import Control.Monad.Trans.Except (ExceptT(ExceptT),runExceptT)
+import Control.Monad.Trans.Except (ExceptT (ExceptT), runExceptT)
 import Data.List (find)
 import Data.Number.Scientific (Scientific)
 import Data.Primitive (SmallArray)
 import Data.Primitive.Unlifted.Array (UnliftedArray)
-import Data.Profunctor (Profunctor(..))
+import Data.Profunctor (Profunctor (..))
 import Data.Text.Short (ShortText)
-import Data.Word (Word16,Word64)
-import Json (Value(Object,Array,String,Number), Member(Member))
-import Json.Context (Context(..))
+import Data.Word (Word16, Word64)
+import Json (Member (Member), Value (Array, Number, Object, String))
+import Json.Context (Context (..))
+import Json.Error (Error (..))
 import Json.Errors (Errors)
-import Json.Error (Error(..))
 
 import qualified Data.Number.Scientific as SCI
 import qualified Data.Primitive.Contiguous as Arr
@@ -67,9 +72,12 @@
 import qualified Json.Errors as Errors
 
 newtype Parser a b = P
-  { unParser :: Context -- ^ reverse list of json keys & indices that have been entered
-             -> a -- ^ value to parse
-             -> Either Errors (Context, b)
+  { unParser ::
+      Context ->
+      -- \^ reverse list of json keys & indices that have been entered
+      a ->
+      -- \^ value to parse
+      Either Errors (Context, b)
   }
 type a ~> b = Parser a b
 
@@ -91,20 +99,23 @@
   String str -> Right (ctx, str)
   _ -> Left (Errors.singleton (Error "expected string" ctx))
 
--- | Parse an array of strings. For example:
---
--- > ["hello","world"]
---
--- Failure context includes the index of non-string value if any values in
--- the array are not strings.
+{- | Parse an array of strings. For example:
+
+> ["hello","world"]
+
+Failure context includes the index of non-string value if any values in
+the array are not strings.
+-}
 strings :: Value ~> UnliftedArray ShortText
 strings = P $ \ctx v -> case v of
   Array membs -> runST $ runExceptT $ do
-    xs <- Arr.itraverseP
-      (\ix e -> case e of
-        String s -> pure s
-        _ -> ExceptT (pure (Left (Errors.singleton (Error "expected string" (Index ix ctx)))))
-      ) membs
+    xs <-
+      Arr.itraverseP
+        ( \ix e -> case e of
+            String s -> pure s
+            _ -> ExceptT (pure (Left (Errors.singleton (Error "expected string" (Index ix ctx)))))
+        )
+        membs
     pure (ctx, xs)
   _ -> Left (Errors.singleton (Error "expected array" ctx))
 
@@ -117,67 +128,68 @@
 boolean = P $ \ctx v -> case v of
   Json.True -> Right (ctx, True)
   Json.False -> Right (ctx, False)
-  _  -> Left (Errors.singleton (Error "expected boolean" ctx))
+  _ -> Left (Errors.singleton (Error "expected boolean" ctx))
 
 null :: Value ~> ()
 null = P $ \ctx v -> case v of
   Json.Null -> Right (ctx, ())
   _ -> Left (Errors.singleton (Error "expected null" ctx))
 
-newtype Members = Members { unMembers :: SmallArray Member }
+newtype Members = Members {unMembers :: SmallArray Member}
 
 member :: ShortText -> Members ~> Value
 member k = P $ \ctx xs -> case find keyEq (unMembers xs) of
-  Just Member{value} -> Right (Key k ctx, value)
+  Just Member {value} -> Right (Key k ctx, value)
   Nothing -> Left (Errors.singleton (Error ("key not found: " <> k) ctx))
-  where
-  keyEq Member{key} = k == key
+ where
+  keyEq Member {key} = k == key
 
 -- | An optional member. Returns Nothing if the value is missing.
 memberOpt :: ShortText -> Members ~> Maybe Value
 memberOpt k = P $ \ctx xs -> case find keyEq (unMembers xs) of
-  Just Member{value} -> Right (Key k ctx, Just value)
+  Just Member {value} -> Right (Key k ctx, Just value)
   Nothing -> Right (ctx, Nothing)
-  where
-  keyEq Member{key} = k == key
+ where
+  keyEq Member {key} = k == key
 
 foldMembers :: a -> (a -> Member ~> a) -> Members ~> a
 foldMembers z0 f = P $ \ctx membs ->
   let xs = unMembers membs
       loop !z !i =
         if i < Arr.size xs
-        then
-          let x@Member{key} = Arr.index xs i
-           in case unParser (f z) (Key key ctx) x of
-                Right (_, z') -> loop z' (i + 1)
-                Left err -> Left err
-        else Right (ctx, z)
+          then
+            let x@Member {key} = Arr.index xs i
+             in case unParser (f z) (Key key ctx) x of
+                  Right (_, z') -> loop z' (i + 1)
+                  Left err -> Left err
+          else Right (ctx, z)
    in loop z0 0
 
-newtype Elements = Elements { unElements :: SmallArray Value }
+newtype Elements = Elements {unElements :: SmallArray Value}
 
 foldl' :: a -> (a -> Value ~> a) -> Elements ~> a
 foldl' z0 f = P $ \ctx elems ->
   let xs = unElements elems
       loop !z !i =
         if i < Arr.size xs
-        then case unParser (f z) (Index i ctx) (Arr.index xs i) of
-          Right (_, z') -> loop z' (i + 1)
-          Left err -> Left err
-        else Right (ctx, z)
+          then case unParser (f z) (Index i ctx) (Arr.index xs i) of
+            Right (_, z') -> loop z' (i + 1)
+            Left err -> Left err
+          else Right (ctx, z)
    in loop z0 0
 
 map :: (Value ~> a) -> Elements ~> SmallArray a
 map (P p) = P $ \ctx (Elements xs) -> runST $ do
   let !len = length xs
   dst <- Arr.new len
-  let loop !i = if i < len
-        then case p (Index i ctx) (Arr.index xs i) of
-          Right (_, y) -> do
-            Arr.write dst i y
-            loop (i + 1)
-          Left err -> pure $ Left err
-        else pure $ Right ()
+  let loop !i =
+        if i < len
+          then case p (Index i ctx) (Arr.index xs i) of
+            Right (_, y) -> do
+              Arr.write dst i y
+              loop (i + 1)
+            Left err -> pure $ Left err
+          else pure $ Right ()
   loop 0 >>= \case
     Right _ -> do
       ys <- Arr.unsafeFreeze dst
@@ -243,9 +255,11 @@
 failZero = P $ \ctx _ -> Left (Errors.singleton (Error "" ctx))
 
 liftMaybe ::
-     ShortText -- ^ Message to display on decode error
-  -> (a -> Maybe b) -- ^ Decode function
-  -> a ~> b
+  -- | Message to display on decode error
+  ShortText ->
+  -- | Decode function
+  (a -> Maybe b) ->
+  a ~> b
 liftMaybe msg f = P $ \ctx x -> case f x of
   Just y -> Right (ctx, y)
   Nothing -> Left (Errors.singleton (Error msg ctx))
diff --git a/src/Json/Context.hs b/src/Json/Context.hs
--- a/src/Json/Context.hs
+++ b/src/Json/Context.hs
@@ -1,43 +1,49 @@
-{-# language BangPatterns #-}
+{-# LANGUAGE BangPatterns #-}
 
 module Json.Context
-  ( Context(..)
+  ( Context (..)
+
     -- * Encoding
   , builderUtf8
+
     -- * Conversion
   , toPath
   ) where
 
-import Json.Path (Path)
-import Data.Text.Short (ShortText)
 import Data.Bytes.Builder (Builder)
+import Data.Text.Short (ShortText)
+import Json.Path (Path)
 
 import qualified Json.Path as Path
 
--- | A finger into a json value indicating where a parser is
--- currently operating. When a parser focuses on a key-value
--- pair in a map, it adds 'Key' constructor to the context, and
--- when it focuses on an element of an array, it adds an 'Index'
--- constructor. Like all zipper-like data structures, it is, in
--- a sense, reversed, which makes it cheap to construct while
--- parsing.
+{- | A finger into a json value indicating where a parser is
+currently operating. When a parser focuses on a key-value
+pair in a map, it adds 'Key' constructor to the context, and
+when it focuses on an element of an array, it adds an 'Index'
+constructor. Like all zipper-like data structures, it is, in
+a sense, reversed, which makes it cheap to construct while
+parsing.
+-}
 data Context
   = Top
   | Key !ShortText !Context
   | Index !Int !Context
-  deriving (Eq,Show)
+  deriving (Eq, Show)
 
--- | Reverse the context, converting it to a 'Path'.
--- For example, toPath performs this conversion:
---
--- > 12.bar.foo.Top ==> foo.bar.12.Nil
+{- | Reverse the context, converting it to a 'Path'.
+For example, toPath performs this conversion:
+
+> 12.bar.foo.Top ==> foo.bar.12.Nil
+-}
 toPath :: Context -> Path
-toPath = go Path.Nil where
+toPath = go Path.Nil
+ where
   go !acc Top = acc
   go !acc (Key k xs) = go (Path.Key k acc) xs
   go !acc (Index i xs) = go (Path.Index i acc) xs
 
--- | Convert 'Context' to textual representation using UTF-8 as the encoding
--- scheme. This reverses the context to present it in the expected order.
+{- | Convert 'Context' to textual representation using UTF-8 as the encoding
+scheme. This reverses the context to present it in the expected order.
+-}
 builderUtf8 :: Context -> Builder
 builderUtf8 ctx0 = Path.builderUtf8 (toPath ctx0)
diff --git a/src/Json/Error.hs b/src/Json/Error.hs
--- a/src/Json/Error.hs
+++ b/src/Json/Error.hs
@@ -1,21 +1,19 @@
-{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE NamedFieldPuns #-}
-{-# LANGUAGE OverloadedStrings #-}
 
 module Json.Error
   ( -- * Types
-    Error(..)
+    Error (..)
+
     -- * Encoding
   , encode
   , builderUtf8
   ) where
 
+import Data.ByteString.Short.Internal (ShortByteString (SBS))
 import Data.Bytes.Builder (Builder)
 import Data.Text.Short (ShortText)
-import Json.Context (Context(..))
-import Data.ByteString.Short.Internal (ShortByteString(SBS))
+import Json.Context (Context (..))
 
 import qualified Data.Bytes.Builder as Builder
 import qualified Data.Bytes.Chunks as ByteChunks
@@ -27,19 +25,18 @@
 data Error = Error
   { message :: !ShortText
   , context :: !Context
-  } deriving (Eq,Show)
+  }
+  deriving (Eq, Show)
 
 ba2st :: PM.ByteArray -> ShortText
-{-# inline ba2st #-}
+{-# INLINE ba2st #-}
 ba2st (PM.ByteArray x) = TS.fromShortByteStringUnsafe (SBS x)
 
 encode :: Error -> ShortText
 encode p = ba2st (ByteChunks.concatU (Builder.run 128 (builderUtf8 p)))
 
 builderUtf8 :: Error -> Builder
-builderUtf8 Error{message,context}
-  =  Context.builderUtf8 context
-  <> Builder.ascii2 ':' ' '
-  <> Builder.shortTextUtf8 message
-
-
+builderUtf8 Error {message, context} =
+  Context.builderUtf8 context
+    <> Builder.ascii2 ':' ' '
+    <> Builder.shortTextUtf8 message
diff --git a/src/Json/Errors.hs b/src/Json/Errors.hs
--- a/src/Json/Errors.hs
+++ b/src/Json/Errors.hs
@@ -1,24 +1,25 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Json.Errors
   ( -- * Types
     Errors
+
     -- * Encoding
   , encode
   , builderUtf8
   , hPut
+
     -- * Create
   , singleton
+
     -- * Conversion
   , toSmallArray
   ) where
 
 import Control.Monad.ST (runST)
-import Data.ByteString.Short.Internal (ShortByteString(SBS))
+import Data.ByteString.Short.Internal (ShortByteString (SBS))
 import Data.Bytes.Builder (Builder)
 import Data.Primitive (SmallArray)
 import Data.Text.Short (ShortText)
@@ -50,30 +51,32 @@
 singleton :: Error -> Errors
 singleton = ErrorsOne
 
--- | Convert errors to builder. The errors are separated by
--- a pair of characters: comma and space.
+{- | Convert errors to builder. The errors are separated by
+a pair of characters: comma and space.
+-}
 builderUtf8 :: Errors -> Builder
 builderUtf8 errs =
   let len = countErrors errs
       errArr = makeErrorArray len errs
    in Error.builderUtf8 (PM.indexSmallArray errArr 0)
-      <>
-      Arr.foldMap
-        (\e -> Builder.ascii2 ',' ' ' <> Error.builderUtf8 e)
-        (Arr.slice errArr 1 (len - 1))
+        <> Arr.foldMap
+          (\e -> Builder.ascii2 ',' ' ' <> Error.builderUtf8 e)
+          (Arr.slice errArr 1 (len - 1))
 
--- | Print errors to the provided handle. Typically, @System.IO.stderr@
--- is provided as the handle. Each encoded error is suffixed with a newline.
---
--- This is a convenience function for the common case where, after a
--- failed parse, an application prints out all parse errors and then exits.
+{- | Print errors to the provided handle. Typically, @System.IO.stderr@
+is provided as the handle. Each encoded error is suffixed with a newline.
+
+This is a convenience function for the common case where, after a
+failed parse, an application prints out all parse errors and then exits.
+-}
 hPut :: Handle -> Errors -> IO ()
 hPut h errs = do
   let len = countErrors errs
       errArr = makeErrorArray len errs
-      bldr = Arr.foldMap
-        (\e -> Error.builderUtf8 e <> Builder.ascii '\n')
-        errArr
+      bldr =
+        Arr.foldMap
+          (\e -> Error.builderUtf8 e <> Builder.ascii '\n')
+          errArr
    in ByteChunks.hPut h (Builder.run 128 bldr)
 
 -- | Convert errors to array.
@@ -81,7 +84,7 @@
 toSmallArray e = makeErrorArray (countErrors e) e
 
 makeErrorArrayErrorThunk :: a
-{-# noinline makeErrorArrayErrorThunk #-}
+{-# NOINLINE makeErrorArrayErrorThunk #-}
 makeErrorArrayErrorThunk =
   errorWithoutStackTrace "Json.Arrow.makeErrorArray: implementation mistake"
 
@@ -102,12 +105,13 @@
 
 -- postcondition: results is greater than 0.
 countErrors :: Errors -> Int
-countErrors = go where
-  go ErrorsOne{} = 1
+countErrors = go
+ where
+  go ErrorsOne {} = 1
   go (ErrorsPlus a b) = go a + go b
 
 ba2st :: PM.ByteArray -> ShortText
-{-# inline ba2st #-}
+{-# INLINE ba2st #-}
 ba2st (PM.ByteArray x) = TS.fromShortByteStringUnsafe (SBS x)
 
 encode :: Errors -> ShortText
diff --git a/src/Json/Parser.hs b/src/Json/Parser.hs
--- a/src/Json/Parser.hs
+++ b/src/Json/Parser.hs
@@ -1,62 +1,71 @@
-{-# language BangPatterns #-}
-{-# language BlockArguments #-}
-{-# language DeriveFunctor #-}
-{-# language DerivingStrategies #-}
-{-# language DuplicateRecordFields #-}
-{-# language KindSignatures #-}
-{-# language LambdaCase #-}
-{-# language NamedFieldPuns #-}
-{-# language RankNTypes #-}
-{-# language OverloadedStrings #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes #-}
 
 module Json.Parser
-  ( Parser(..)
-  , MemberParser(..)
+  ( Parser (..)
+  , MemberParser (..)
+
     -- * Run
   , run
+
     -- * Object Parsing
   , key
   , keyOptNull
   , members
+
     -- * Arrays
   , smallArray
   , foldSmallArray
   , traverseMembers
+
     -- * Specific Data Constructors
   , object
   , array
   , number
   , boolean
   , string
+
     -- * Trivial Combinators
   , int
   , int32
   , word16
+  , word32
   , word64
+
     -- * Failing
   , fail
-    -- * Modified Context 
+
+    -- * Modified Context
   , contextually
   ) where
 
 import Prelude hiding (fail)
 
-import Control.Applicative (Alternative(..))
+import Control.Applicative (Alternative (..))
 import Control.Monad.ST (runST)
 import Control.Monad.Trans.Class (lift)
-import Control.Monad.Trans.Except (ExceptT(ExceptT),runExceptT)
+import Control.Monad.Trans.Except (ExceptT (ExceptT), runExceptT)
 import Data.Foldable (foldlM)
 import Data.Int (Int32)
 import Data.List (find)
 import Data.Number.Scientific (Scientific)
 import Data.Primitive (SmallArray)
 import Data.Text.Short (ShortText)
-import Data.Word (Word16,Word64)
-import Json (Value(Object,Array,Number),Member(Member))
+import Data.Word (Word16, Word32, Word64)
+import Json (Member (Member), Value (Array, Number, Object))
+
 -- import Json.Path (Path(Nil,Key,Index))
-import Json.Context (Context(Top,Key,Index))
+import Json.Context (Context (Index, Key, Top))
+import Json.Error (Error (..))
 import Json.Errors (Errors)
-import Json.Error (Error(..))
 
 import qualified Data.Number.Scientific as SCI
 import qualified Data.Primitive as PM
@@ -64,8 +73,8 @@
 import qualified Json.Errors as Errors
 
 newtype Parser a = Parser
-  { runParser :: Context -> Either Errors a }
-  deriving stock Functor
+  {runParser :: Context -> Either Errors a}
+  deriving stock (Functor)
 
 instance Applicative Parser where
   pure a = Parser (\_ -> Right a)
@@ -88,8 +97,8 @@
     runParser (g x) p
 
 newtype MemberParser a = MemberParser
-  { runMemberParser :: Context -> SmallArray Member -> Either Errors a }
-  deriving stock Functor
+  {runMemberParser :: Context -> SmallArray Member -> Either Errors a}
+  deriving stock (Functor)
 
 instance Applicative MemberParser where
   pure a = MemberParser (\_ _ -> Right a)
@@ -99,7 +108,7 @@
     pure (h y)
 
 instance Alternative MemberParser where
-  empty = MemberParser $ \p _ -> Left (Errors.singleton Error{message=mempty,context=p})
+  empty = MemberParser $ \p _ -> Left (Errors.singleton Error {message = mempty, context = p})
   a <|> b = MemberParser $ \p mbrs ->
     case runMemberParser a p mbrs of
       Right x -> Right x
@@ -119,7 +128,7 @@
   Left e -> Left e
 
 fail :: ShortText -> Parser a
-fail !msg = Parser (\e -> Left (Errors.singleton Error{context=e,message=msg}))
+fail !msg = Parser (\e -> Left (Errors.singleton Error {context = e, message = msg}))
 
 object :: Value -> Parser (SmallArray Member)
 object = \case
@@ -159,6 +168,11 @@
   Just n -> pure n
   _ -> fail "expected number in range [0,2^16)"
 
+word32 :: Scientific -> Parser Word32
+word32 m = case SCI.toWord32 m of
+  Just n -> pure n
+  _ -> fail "expected number in range [0,2^32)"
+
 word64 :: Scientific -> Parser Word64
 word64 m = case SCI.toWord64 m of
   Just n -> pure n
@@ -175,21 +189,22 @@
 
 key :: ShortText -> (Value -> Parser a) -> MemberParser a
 key !name f = MemberParser $ \p mbrs ->
-  let !p' = Key name p in
-  case find (\Member{key=k} -> k == name) mbrs of
-    Nothing -> Left (Errors.singleton (Error{context=p',message="key not found: " <> name}))
-    Just Member{value} -> runParser (f value) p'
+  let !p' = Key name p
+   in case find (\Member {key = k} -> k == name) mbrs of
+        Nothing -> Left (Errors.singleton (Error {context = p', message = "key not found: " <> name}))
+        Just Member {value} -> runParser (f value) p'
 
--- | Variant of 'key' that supplies the JSON value @null@ to the
--- callback if the key is not found. Using this parser combinators implies
--- that there is no distinction between @null@ and an absent value in
--- the encoding scheme.
+{- | Variant of 'key' that supplies the JSON value @null@ to the
+callback if the key is not found. Using this parser combinators implies
+that there is no distinction between @null@ and an absent value in
+the encoding scheme.
+-}
 keyOptNull :: ShortText -> (Value -> Parser a) -> MemberParser a
 keyOptNull !name f = MemberParser $ \p mbrs ->
   let !p' = Key name p
-      val = case find (\Member{key=k} -> k == name) mbrs of
+      val = case find (\Member {key = k} -> k == name) mbrs of
         Nothing -> Json.Null
-        Just Member{value} -> value
+        Just Member {value} -> value
    in runParser (f val) p'
 
 -- object2 ::
@@ -197,39 +212,47 @@
 --   -> ShortText -> Parser a
 --   -> ShortText -> Parser b
 --   -> Parser c
--- object2 f ka pa kb pb = Parser $ \p v -> case v 
+-- object2 f ka pa kb pb = Parser $ \p v -> case v
 
 -- elements :: Parser Value (Chunks Value)
 -- elements = _
 
--- | Run the same parser against every element in a 'SmallArray'. This adjusts
--- the context at each element.
+{- | Run the same parser against every element in a 'SmallArray'. This adjusts
+the context at each element.
+-}
 smallArray :: (Value -> Parser a) -> SmallArray Value -> Parser (SmallArray a)
 smallArray f xs = Parser $ \ !p -> runST do
   let !len = length xs
   dst <- PM.newSmallArray len errorThunk
   runExceptT $ do
-    _ <- foldlM
-      (\ix x -> do
-        !y <- ExceptT (pure (runParser (f x) (Index ix p)))
-        lift (PM.writeSmallArray dst ix y)
-        pure (ix + 1)
-      ) 0 xs
+    _ <-
+      foldlM
+        ( \ix x -> do
+            !y <- ExceptT (pure (runParser (f x) (Index ix p)))
+            lift (PM.writeSmallArray dst ix y)
+            pure (ix + 1)
+        )
+        0
+        xs
     lift (PM.unsafeFreezeSmallArray dst)
 
--- | Run the parser against every element in a 'SmallArray', updating an
--- accumulator at each step. Folds from left to right. This adjusts
--- the context at each element. Typically, type @a@ is @Value@.
+{- | Run the parser against every element in a 'SmallArray', updating an
+accumulator at each step. Folds from left to right. This adjusts
+the context at each element. Typically, type @a@ is @Value@.
+-}
 foldSmallArray :: (b -> a -> Parser b) -> b -> SmallArray a -> Parser b
-{-# inline foldSmallArray #-}
+{-# INLINE foldSmallArray #-}
 foldSmallArray f acc0 xs = Parser $ \ !p -> runST do
   -- TODO: make sure tuples get elided
   runExceptT $ do
-    (!_,r) <- foldlM
-      (\( !ix, !acc) x -> do
-        !acc' <- ExceptT (pure (runParser (f acc x) (Index ix p)))
-        pure (ix + 1, acc')
-      ) (0,acc0) xs
+    (!_, r) <-
+      foldlM
+        ( \(!ix, !acc) x -> do
+            !acc' <- ExceptT (pure (runParser (f acc x) (Index ix p)))
+            pure (ix + 1, acc')
+        )
+        (0, acc0)
+        xs
     pure r
 
 -- | Traverse the members. The adjusts the context at each member.
@@ -238,23 +261,27 @@
   let !len = length xs
   dst <- PM.newSmallArray len errorThunk
   runExceptT $ do
-    !_ <- foldlM
-      (\ !ix x@Member{key=k} -> do
-        !y <- ExceptT (pure (runParser (f x) (Key k p)))
-        lift (PM.writeSmallArray dst ix y)
-        pure (ix + 1)
-      ) 0 xs
+    !_ <-
+      foldlM
+        ( \ !ix x@Member {key = k} -> do
+            !y <- ExceptT (pure (runParser (f x) (Key k p)))
+            lift (PM.writeSmallArray dst ix y)
+            pure (ix + 1)
+        )
+        0
+        xs
     lift (PM.unsafeFreezeSmallArray dst)
 
 errorThunk :: a
-{-# noinline errorThunk #-}
+{-# NOINLINE errorThunk #-}
 errorThunk = errorWithoutStackTrace "Json.Parser: implementation mistake"
 
 -- | Run a parser in a modified context.
 contextually :: (Context -> Context) -> Parser a -> Parser a
-{-# inline contextually #-}
-contextually f (Parser g) = Parser
-  (\p ->
-    let !p' = f p
-     in g p'
-  )
+{-# INLINE contextually #-}
+contextually f (Parser g) =
+  Parser
+    ( \p ->
+        let !p' = f p
+         in g p'
+    )
diff --git a/src/Json/Path.hs b/src/Json/Path.hs
--- a/src/Json/Path.hs
+++ b/src/Json/Path.hs
@@ -1,98 +1,119 @@
-{-# language BangPatterns #-}
-{-# language MagicHash #-}
-{-# language UnboxedTuples #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
 
 module Json.Path
-  ( Path(..)
+  ( Path (..)
+
     -- * Encoding
   , encode
   , builderUtf8
+
     -- * Lookup
   , query
   , query'
+
     -- * Reverse
   , reverse
   ) where
 
 import Prelude hiding (reverse)
 
-import Json (Value(Object,Array,Null),Member(Member))
-import Data.Primitive (ByteArray(ByteArray))
-import Data.Text.Short (ShortText)
+import Data.ByteString.Short.Internal (ShortByteString (SBS))
 import Data.Bytes.Builder (Builder)
-import Data.ByteString.Short.Internal (ShortByteString(SBS))
+import Data.Primitive (ByteArray (ByteArray))
+import Data.Text.Short (ShortText)
+import Json (Member (Member), Value (Array, Null, Object))
 
-import qualified Data.Bytes.Chunks as ByteChunks
 import qualified Data.Bytes.Builder as Builder
+import qualified Data.Bytes.Chunks as ByteChunks
 import qualified Data.Primitive as PM
 import qualified Data.Text.Short.Unsafe as TS
 
 -- | A path to an object.
 data Path
-  = Key {-# UNPACK #-} !ShortText !Path
-    -- ^ JSON path element of a key into an object, \"object.key\".
-  | Index {-# UNPACK #-} !Int !Path
-    -- ^ JSON path element of an index into an array, \"array[index]\".
+  = -- | JSON path element of a key into an object, \"object.key\".
+    Key {-# UNPACK #-} !ShortText !Path
+  | -- | JSON path element of an index into an array, \"array[index]\".
     -- Negative numbers result in undefined behavior.
+    Index {-# UNPACK #-} !Int !Path
   | Nil
-  deriving (Eq,Show)
+  deriving (Eq, Show)
 
--- | Encode a path.
---
--- >>> encode (Key "foo" $ Index 5 $ Key "bar" $ Nil)
--- $.foo[5].bar
+{- | Encode a path.
+
+>>> encode (Key "foo" $ Index 5 $ Key "bar" $ Nil)
+-}
 encode :: Path -> ShortText
 encode p = ba2st (ByteChunks.concatU (Builder.run 128 (builderUtf8 p)))
 
 builderUtf8 :: Path -> Builder
-builderUtf8 p0 = Builder.ascii '$' <> go p0 where
+builderUtf8 p0 = Builder.ascii '$' <> go p0
+ where
   go Nil = mempty
   go (Key k p) = Builder.ascii '.' <> Builder.shortTextUtf8 k <> go p
   go (Index i p) =
-       Builder.ascii '['
-    <> Builder.wordDec (fromIntegral i)
-    <> Builder.ascii ']'
-    <> go p
+    Builder.ascii '['
+      <> Builder.wordDec (fromIntegral i)
+      <> Builder.ascii ']'
+      <> go p
 
--- | Search for an element at the given path. Returns 'Nothing' if
--- anything in the path is missing.
+{- | Search for an element at the given path. Returns 'Nothing' if
+anything in the path is missing.
+-}
 query :: Path -> Value -> Maybe Value
-query = go where
+query = go
+ where
   go Nil v = Just v
-  go (Key k p) (Object mbrs) = foldr
-    (\(Member key val) other -> if key == k
-      then Just val
-      else other
-    ) Nothing mbrs >>= go p
-  go (Index i p) (Array vs) = if i < PM.sizeofSmallArray vs
-    then
-      let !(# e #) = PM.indexSmallArray## vs i
-       in go p e
-    else Nothing
+  go (Key k p) (Object mbrs) =
+    foldr
+      ( \(Member key val) other ->
+          if key == k
+            then Just val
+            else other
+      )
+      Nothing
+      mbrs
+      >>= go p
+  go (Index i p) (Array vs) =
+    if i < PM.sizeofSmallArray vs
+      then
+        let !(# e #) = PM.indexSmallArray## vs i
+         in go p e
+      else Nothing
   go _ _ = Nothing
 
--- | Variant of 'query' that returns 'Null' if anything in the path
--- is missing.
+{- | Variant of 'query' that returns 'Null' if anything in the path
+is missing.
+-}
 query' :: Path -> Value -> Value
-query' = go where
+query' = go
+ where
   go Nil v = v
-  go (Key k p) (Object mbrs) = go p $ foldr
-    (\(Member key val) other -> if key == k
-      then val
-      else other
-    ) Null mbrs
-  go (Index i p) (Array vs) = if i < PM.sizeofSmallArray vs
-    then
-      let !(# e #) = PM.indexSmallArray## vs i
-       in go p e
-    else Null
+  go (Key k p) (Object mbrs) =
+    go p $
+      foldr
+        ( \(Member key val) other ->
+            if key == k
+              then val
+              else other
+        )
+        Null
+        mbrs
+  go (Index i p) (Array vs) =
+    if i < PM.sizeofSmallArray vs
+      then
+        let !(# e #) = PM.indexSmallArray## vs i
+         in go p e
+      else Null
   go _ _ = Null
 
 ba2st :: ByteArray -> ShortText
 ba2st (ByteArray x) = TS.fromShortByteStringUnsafe (SBS x)
 
 reverse :: Path -> Path
-reverse = go Nil where
+reverse = go Nil
+ where
   go !acc Nil = acc
   go !acc (Key k xs) = go (Key k acc) xs
   go !acc (Index i xs) = go (Index i acc) xs
diff --git a/test/Arrowy.hs b/test/Arrowy.hs
--- a/test/Arrowy.hs
+++ b/test/Arrowy.hs
@@ -1,8 +1,6 @@
 {-# LANGUAGE ApplicativeDo #-}
-{-# LANGUAGE Arrows #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TypeOperators #-}
 
 module Arrowy
@@ -12,15 +10,13 @@
 
 import Prelude hiding (id)
 
-import Control.Arrow ((>>>),(<+>))
-import DogHouse(Dog(..),House(..))
-import Json.Arrow (type (~>),Members)
+import Control.Arrow ((<+>), (>>>))
+import DogHouse (Dog (..), House (..))
+import Json.Arrow (Members, type (~>))
+import Json.Context (Context (Index, Key, Top))
+import Json.Error (Error (..))
 import Json.Errors (Errors)
-import Json.Error (Error(..))
-import Json.Context (Context(Key,Index,Top))
-import Data.Primitive (SmallArray)
 
-import qualified GHC.Exts as Exts
 import qualified Json
 import qualified Json.Arrow as P
 import qualified Json.Errors as Errors
@@ -32,17 +28,16 @@
 houseMemberParser = do
   address <- P.member "address" >>> P.string
   dogs <- P.member "dogs" >>> P.withArray (P.withObject dogMemberParser)
-  pure House{address,dogs}
+  pure House {address, dogs}
 
 dogMemberParser :: Members ~> Dog
 dogMemberParser = do
   name <- P.member "name" >>> P.string
   age <- P.member "age" >>> (P.int <+> P.fromNull 0)
   alive <- P.member "alive" >>> P.boolean
-  pure Dog{name,age,alive}
+  pure Dog {name, age, alive}
 
 badErrors :: Errors
 badErrors =
   Errors.singleton (Error "expected number" $ Key "age" $ Index 1 $ Key "dogs" Top)
-  <>
-  Errors.singleton (Error "expected null" $ Key "age" $ Index 1 $ Key "dogs" Top)
+    <> Errors.singleton (Error "expected null" $ Key "age" $ Index 1 $ Key "dogs" Top)
diff --git a/test/DogHouse.hs b/test/DogHouse.hs
--- a/test/DogHouse.hs
+++ b/test/DogHouse.hs
@@ -1,20 +1,19 @@
-{-# language ApplicativeDo #-}
-{-# language NamedFieldPuns #-}
-{-# language OverloadedStrings #-}
-{-# language QuasiQuotes #-}
+{-# LANGUAGE ApplicativeDo #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
 
 module DogHouse
-  ( House(..)
-  , Dog(..)
+  ( House (..)
+  , Dog (..)
   , sampleGood
   , sampleBad
   , expectationGood
   ) where
 
-import Data.ByteString.Short.Internal (ShortByteString(SBS))
+import Data.ByteString.Short.Internal (ShortByteString (SBS))
 import Data.Bytes (Bytes)
-import Data.Primitive (ByteArray)
-import Data.Primitive (SmallArray)
+import Data.Primitive (ByteArray, SmallArray)
 import Data.Text.Encoding (encodeUtf8)
 import Data.Text.Short (ShortText)
 import NeatInterpolation (text)
@@ -27,41 +26,45 @@
 data House = House
   { address :: !ShortText
   , dogs :: !(SmallArray Dog)
-  } deriving (Eq,Show)
+  }
+  deriving (Eq, Show)
 
 data Dog = Dog
   { name :: !ShortText
   , age :: !Int
   , alive :: !Bool
-  } deriving (Eq,Show)
+  }
+  deriving (Eq, Show)
 
-shortByteStringToByteArray :: ShortByteString -> ByteArray 
+shortByteStringToByteArray :: ShortByteString -> ByteArray
 shortByteStringToByteArray (SBS x) = PM.ByteArray x
 
 expectationGood :: House
-expectationGood = House
-  { address = "123 Walsh Street"
-  , dogs = Exts.fromList
-    [ Dog
-      { name = "Fluff"
-      , age = 52
-      , alive = True
-      }
-    , Dog
-      { name = "McDeath"
-      , age = 98
-      , alive = False
-      }
-    ]
-  }
+expectationGood =
+  House
+    { address = "123 Walsh Street"
+    , dogs =
+        Exts.fromList
+          [ Dog
+              { name = "Fluff"
+              , age = 52
+              , alive = True
+              }
+          , Dog
+              { name = "McDeath"
+              , age = 98
+              , alive = False
+              }
+          ]
+    }
 
 sampleGood :: Bytes
-sampleGood = id
-  $ Bytes.fromByteArray
-  $ shortByteStringToByteArray
-  $ SBS.toShort
-  $ encodeUtf8
-  [text|
+sampleGood =
+  Bytes.fromByteArray $
+    shortByteStringToByteArray $
+      SBS.toShort $
+        encodeUtf8
+          [text|
   {
     "address": "123 Walsh Street",
     "dogs": [
@@ -78,12 +81,12 @@
   |]
 
 sampleBad :: Bytes
-sampleBad = id
-  $ Bytes.fromByteArray
-  $ shortByteStringToByteArray
-  $ SBS.toShort
-  $ encodeUtf8
-  [text|
+sampleBad =
+  Bytes.fromByteArray $
+    shortByteStringToByteArray $
+      SBS.toShort $
+        encodeUtf8
+          [text|
   {
     "address": "123 Walsh Street",
     "dogs": [
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,88 +1,123 @@
-{-# language LambdaCase #-}
-{-# language DuplicateRecordFields #-}
-{-# language OverloadedStrings #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 import Control.Arrow ((>>>))
 import Data.Bifunctor (first)
 import Data.Bytes (Bytes)
-import Json.Error (Error(Error))
-import Json.Path (Path(Key,Index,Nil))
-import Test.Tasty (defaultMain,testGroup,TestTree)
+import Data.Word (Word32, Word64)
+import Json.Error (Error (Error))
+import Json.Path (Path (Index, Key, Nil))
+import Test.Hspec
+import Test.Tasty (TestTree, defaultMain, testGroup)
 import Test.Tasty.HUnit ((@=?))
+import Test.Tasty.Hspec
 
+import qualified Data.Bytes.Text.Ascii as Ascii
+import qualified Data.Number.Scientific as SCI
 import qualified GHC.Exts as Exts
-import qualified Data.Bytes as Bytes
 import qualified Json
+import qualified Json.Arrow as A
 import qualified Json.Context as Ctx
+import qualified Json.Error as JE
+import qualified Json.Errors as JES
+import qualified Json.Parser as P
 import qualified Json.Path as Path
-import qualified Json.Arrow as A
-import qualified Json.Errors as Errors
 import qualified Test.Tasty.HUnit as THU
 
 import qualified Arrowy
 import qualified DogHouse
 import qualified Monadic
-import qualified Json.Error
 
 main :: IO ()
-main = defaultMain tests
+main = do
+  hSpecGroup <- testSpec "Hspec Tests" hSpecTests
+  defaultMain $ testGroup "All Tests" [hSpecGroup, hUnitTests]
 
-tests :: TestTree
-tests = testGroup "Tests"
-  [ THU.testCase "Json.Path.query-A" $
-      case Json.decode (Bytes.fromAsciiString "{\"bla\": true, \"foo\" : [ 55 , { \"bar\": false } ] }") of
-        Left _ -> fail "failed to parse into syntax tree" 
-        Right v -> 
-          Just Json.False
-          @=?
-          Path.query (Key "foo" $ Index 1 $ Key "bar" $ Nil) v
-  , THU.testCase "Json.Path.encode-A" $
-      "$.foo[1].bar"
-      @=?
-      Path.encode (Key "foo" $ Index 1 $ Key "bar" $ Nil)
-  , THU.testCase "DogHouse-Monadic-A" $ case Json.decode DogHouse.sampleGood of
-      Left _ -> fail "failed to parse into syntax tree" 
-      Right val ->
-        Right DogHouse.expectationGood
-        @=?
-        Monadic.decode val
-  , THU.testCase "DogHouse-Monadic-B" $ case Json.decode DogHouse.sampleBad of
-      Left _ -> fail "failed to parse into syntax tree" 
-      Right val ->
-        Monadic.expectationBad
-        @=?
-        Monadic.decode val
-  , THU.testCase "DogHouse-Arrowy-A" $ case Json.decode DogHouse.sampleGood of
-      Left _ -> fail "failed to parse into syntax tree" 
-      Right val ->
-        Right DogHouse.expectationGood
-        @=?
-        Arrowy.decode val
-  , THU.testCase "DogHouse-Arrowy-B" $ case Json.decode DogHouse.sampleBad of
-      Left _ -> fail "failed to parse into syntax tree" 
-      Right val ->
-        Left Arrowy.badErrors
-        @=?
-        Arrowy.decode val
-  , THU.testCase "Arrowy-strings-A" $ case Json.decode sampleArrowyStrings of
-      Left _ -> fail "failed to parse into syntax tree" 
-      Right val ->
-        Left
-          ( Exts.fromList
-            [ Error
-              { context = Ctx.Index 2 (Ctx.Key "foo" Ctx.Top)
-              , message = "expected string"
-              }
-            ]
-          )
-        @=?
-        first Errors.toSmallArray (A.run (A.object >>> A.member "foo" >>> A.strings) val)
-  , THU.testCase "Arrowy-encode-errors" $
-      "$.dogs[1].age: expected number, $.dogs[1].age: expected null"
-      @=?
-      Errors.encode Arrowy.badErrors
-  ]
+hSpecTests :: Spec
+hSpecTests =
+  describe "Parser" $ do
+    describe "word32" $ do
+      context "when value is negative" $ do
+        it "should fail" $ do
+          let input = SCI.small (-1) 0
+          let actual = P.runParser (P.word32 input) Ctx.Top
+          let expected = Left (JES.singleton JE.Error {JE.message = "expected number in range [0,2^32)", JE.context = Ctx.Top})
+          actual `shouldBe` expected
 
+      context "when value is zero" $ do
+        it "should succeed" $ do
+          let input = SCI.small 0 0
+          let actual = P.runParser (P.word32 input) Ctx.Top
+          let expected = Right 0
+          actual `shouldBe` expected
+
+      context "when value is upper bound" $ do
+        it "should succeed" $ do
+          let input = SCI.fromWord32 (maxBound :: Word32)
+          let actual = P.runParser (P.word32 input) Ctx.Top
+          let expected = Right (maxBound :: Word32)
+          actual `shouldBe` expected
+
+      context "when value is greater than upper bound" $ do
+        it "should fail" $ do
+          let large :: Word64 = fromIntegral (maxBound :: Word32) + 1
+          let input = SCI.fromWord64 large
+          let actual = P.runParser (P.word32 input) Ctx.Top
+          let expected = Left (JES.singleton JE.Error {JE.message = "expected number in range [0,2^32)", JE.context = Ctx.Top})
+          actual `shouldBe` expected
+
+hUnitTests :: TestTree
+hUnitTests =
+  testGroup
+    "HUnit Tests"
+    [ THU.testCase "Json.Path.query-A" $
+        case Json.decode (Ascii.fromString "{\"bla\": true, \"foo\" : [ 55 , { \"bar\": false } ] }") of
+          Left _ -> fail "failed to parse into syntax tree"
+          Right v ->
+            Just Json.False
+              @=? Path.query (Key "foo" $ Index 1 $ Key "bar" Nil) v
+    , THU.testCase "Json.Path.encode-A" $
+        "$.foo[1].bar"
+          @=? Path.encode (Key "foo" $ Index 1 $ Key "bar" Nil)
+    , THU.testCase "DogHouse-Monadic-A" $ case Json.decode DogHouse.sampleGood of
+        Left _ -> fail "failed to parse into syntax tree"
+        Right val ->
+          Right DogHouse.expectationGood
+            @=? Monadic.decode val
+    , THU.testCase "DogHouse-Monadic-B" $ case Json.decode DogHouse.sampleBad of
+        Left _ -> fail "failed to parse into syntax tree"
+        Right val ->
+          Monadic.expectationBad
+            @=? Monadic.decode val
+    , THU.testCase "DogHouse-Arrowy-A" $ case Json.decode DogHouse.sampleGood of
+        Left _ -> fail "failed to parse into syntax tree"
+        Right val ->
+          Right DogHouse.expectationGood
+            @=? Arrowy.decode val
+    , THU.testCase "DogHouse-Arrowy-B" $ case Json.decode DogHouse.sampleBad of
+        Left _ -> fail "failed to parse into syntax tree"
+        Right val ->
+          Left Arrowy.badErrors
+            @=? Arrowy.decode val
+    , THU.testCase "Arrowy-strings-A" $ case Json.decode sampleArrowyStrings of
+        Left _ -> fail "failed to parse into syntax tree"
+        Right val ->
+          Left
+            ( Exts.fromList
+                [ Error
+                    { context = Ctx.Index 2 (Ctx.Key "foo" Ctx.Top)
+                    , message = "expected string"
+                    }
+                ]
+            )
+            @=? first JES.toSmallArray (A.run (A.object >>> A.member "foo" >>> A.strings) val)
+    , THU.testCase "Arrowy-encode-errors" $
+        "$.dogs[1].age: expected number, $.dogs[1].age: expected null"
+          @=? JES.encode Arrowy.badErrors
+    ]
+
 sampleArrowyStrings :: Bytes
-sampleArrowyStrings = Bytes.fromAsciiString
-  "{\"bla\": true, \"foo\" : [ \"bar\", \"baz\", null, \"hey\"] }"
+sampleArrowyStrings =
+  Ascii.fromString
+    "{\"bla\": true, \"foo\" : [ \"bar\", \"baz\", null, \"hey\"] }"
diff --git a/test/Monadic.hs b/test/Monadic.hs
--- a/test/Monadic.hs
+++ b/test/Monadic.hs
@@ -1,7 +1,6 @@
-{-# language ApplicativeDo #-}
-{-# language NamedFieldPuns #-}
-{-# language OverloadedStrings #-}
-{-# language QuasiQuotes #-}
+{-# LANGUAGE ApplicativeDo #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 module Monadic
   ( decode
@@ -9,11 +8,11 @@
   ) where
 
 import Control.Monad ((>=>))
-import DogHouse(Dog(..),House(..))
-import Json.Parser (MemberParser)
+import DogHouse (Dog (..), House (..))
+import Json.Context (Context (Index, Key, Top))
+import Json.Error (Error (..))
 import Json.Errors (Errors)
-import Json.Error (Error(..))
-import Json.Context (Context(Key,Index,Top))
+import Json.Parser (MemberParser)
 
 import qualified Json
 import qualified Json.Errors as Errors
@@ -29,17 +28,20 @@
     arr <- P.array v
     flip P.smallArray arr $ \e -> do
       P.object e >>= P.members dogMemberParser
-  pure (House{address,dogs})
+  pure (House {address, dogs})
 
 dogMemberParser :: MemberParser Dog
 dogMemberParser = do
   name <- P.key "name" P.string
   age <- P.key "age" (P.number >=> P.int)
   alive <- P.key "alive" P.boolean
-  pure Dog{name,age,alive}
+  pure Dog {name, age, alive}
 
 expectationBad :: Either Errors House
-expectationBad = Left $ Errors.singleton $ Error
-  { context = Key "age" $ Index 1 $ Key "dogs" $ Top
-  , message = "expected number"
-  }
+expectationBad =
+  Left $
+    Errors.singleton $
+      Error
+        { context = Key "age" $ Index 1 $ Key "dogs" $ Top
+        , message = "expected number"
+        }
