diff --git a/argo.cabal b/argo.cabal
--- a/argo.cabal
+++ b/argo.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.2
 
 name: argo
-version: 0.2022.2.23
+version: 0.2022.2.27
 
 build-type: Simple
 category: JSON
@@ -67,41 +67,41 @@
     autogen-modules: Paths_argo
     exposed-modules:
         Argo
-        Argo.Class.HasCodec
-        Argo.Codec.Array
-        Argo.Codec.Codec
-        Argo.Codec.List
-        Argo.Codec.Object
-        Argo.Codec.Value
-        Argo.Decode
-        Argo.Encode
-        Argo.Json.Array
-        Argo.Json.Boolean
-        Argo.Json.Member
-        Argo.Json.Name
-        Argo.Json.Null
-        Argo.Json.Number
-        Argo.Json.Object
-        Argo.Json.String
-        Argo.Json.Value
-        Argo.Literal
-        Argo.Main
-        Argo.Pattern
-        Argo.Pointer.Pointer
-        Argo.Pointer.Token
-        Argo.QuasiQuoter
-        Argo.Schema.Identifier
-        Argo.Schema.Schema
-        Argo.Type.Config
-        Argo.Type.Decimal
-        Argo.Type.Decoder
-        Argo.Type.Encoder
-        Argo.Type.Flag
-        Argo.Type.Indent
-        Argo.Type.Nullable
-        Argo.Type.Optional
-        Argo.Type.Permission
-        Argo.Type.Settings
+        Argo.Internal.Class.HasCodec
+        Argo.Internal.Codec.Array
+        Argo.Internal.Codec.Codec
+        Argo.Internal.Codec.List
+        Argo.Internal.Codec.Object
+        Argo.Internal.Codec.Value
+        Argo.Internal.Decode
+        Argo.Internal.Encode
+        Argo.Internal.Json.Array
+        Argo.Internal.Json.Boolean
+        Argo.Internal.Json.Member
+        Argo.Internal.Json.Name
+        Argo.Internal.Json.Null
+        Argo.Internal.Json.Number
+        Argo.Internal.Json.Object
+        Argo.Internal.Json.String
+        Argo.Internal.Json.Value
+        Argo.Internal.Literal
+        Argo.Internal.Main
+        Argo.Internal.Pattern
+        Argo.Internal.Pointer.Pointer
+        Argo.Internal.Pointer.Token
+        Argo.Internal.QuasiQuoter
+        Argo.Internal.Schema.Identifier
+        Argo.Internal.Schema.Schema
+        Argo.Internal.Type.Config
+        Argo.Internal.Type.Decimal
+        Argo.Internal.Type.Decoder
+        Argo.Internal.Type.Encoder
+        Argo.Internal.Type.Flag
+        Argo.Internal.Type.Indent
+        Argo.Internal.Type.Nullable
+        Argo.Internal.Type.Optional
+        Argo.Internal.Type.Permission
+        Argo.Internal.Type.Settings
     hs-source-dirs: source/library
     other-modules:
         Argo.Vendor.Builder
diff --git a/source/executable/Main.hs b/source/executable/Main.hs
--- a/source/executable/Main.hs
+++ b/source/executable/Main.hs
@@ -1,4 +1,4 @@
-import qualified Argo.Main as Argo
+import qualified Argo.Internal.Main as Argo
 
 main :: IO ()
 main = Argo.main
diff --git a/source/library/Argo.hs b/source/library/Argo.hs
--- a/source/library/Argo.hs
+++ b/source/library/Argo.hs
@@ -7,34 +7,85 @@
         , Pattern.Object
         , Pattern.String
         )
-    , Name.Name(Pattern.Name)
-    , Member.Member(Member)
+    -- * Decoding
+    , Decode.decode
+    , Decode.decodePointer
+    , Decode.fromValue
+    -- * Encoding
     , Encode.encode
     , Encode.encodeWith
-    , Indent.Indent(Spaces, Tab)
-    , Decode.decode
+    , Encode.encodePointer
+    , Encode.toValue
+    -- * Codecs
     , HasCodec.HasCodec(codec)
+    , Codec.identified
+    , Codec.withIdentifier
+    , Codec.map
+    , Codec.mapMaybe
+    , Codec.project
+    -- ** Arrays
+    , Codec.fromArrayCodec
+    , element
+    -- ** Objects
+    , Codec.fromObjectCodec
+    , required
+    , optional
+    -- * Schemas
+    , Codec.schema
+    , oneOf
+    -- * Pointers
+    , Pointer.evaluate
+    -- * Quasi Quoters
     , QuasiQuoter.value
     , QuasiQuoter.pointer
-    , Pointer.Pointer(Pointer)
-    , Token.Token(Token)
-    , Pointer.evaluate
-    , Encode.encodePointer
-    , Decode.decodePointer
+    -- * Types
+    , Codec
     , Decimal.Decimal(Pattern.Decimal)
+    , Identifier.Identifier(Identifier.Identifier)
+    , Indent.Indent(Indent.Spaces, Indent.Tab)
+    , Member.Member(Member.Member)
+    , Name.Name(Pattern.Name)
+    , Permission.Permission(Permission.Allow, Permission.Forbid)
+    , Pointer.Pointer(Pointer.Pointer)
     , Schema.Schema
+    , Token.Token(Token.Token)
     ) where
 
-import qualified Argo.Class.HasCodec as HasCodec
-import qualified Argo.Decode as Decode
-import qualified Argo.Encode as Encode
-import qualified Argo.Json.Member as Member
-import qualified Argo.Json.Name as Name
-import qualified Argo.Json.Value as Value
-import qualified Argo.Pattern as Pattern
-import qualified Argo.Pointer.Pointer as Pointer
-import qualified Argo.Pointer.Token as Token
-import qualified Argo.QuasiQuoter as QuasiQuoter
-import qualified Argo.Schema.Schema as Schema
-import qualified Argo.Type.Decimal as Decimal
-import qualified Argo.Type.Indent as Indent
+import qualified Argo.Internal.Class.HasCodec as HasCodec
+import qualified Argo.Internal.Codec.Array as Codec
+import qualified Argo.Internal.Codec.Codec as Codec
+import qualified Argo.Internal.Codec.Object as Codec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Decode as Decode
+import qualified Argo.Internal.Encode as Encode
+import qualified Argo.Internal.Json.Member as Member
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Pattern as Pattern
+import qualified Argo.Internal.Pointer.Pointer as Pointer
+import qualified Argo.Internal.Pointer.Token as Token
+import qualified Argo.Internal.QuasiQuoter as QuasiQuoter
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Internal.Schema.Schema as Schema
+import qualified Argo.Internal.Type.Decimal as Decimal
+import qualified Argo.Internal.Type.Indent as Indent
+import qualified Argo.Internal.Type.Permission as Permission
+import qualified Data.Foldable as Foldable
+
+type Codec a = Codec.Value a
+
+element :: HasCodec.HasCodec o => (i -> o) -> Codec.Element i o
+element f = Codec.project f $ Codec.element HasCodec.codec
+
+oneOf :: Foldable t => t Schema.Schema -> Schema.Schema
+oneOf = Foldable.fold
+
+optional
+    :: HasCodec.HasCodec f
+    => (i -> Maybe f)
+    -> Name.Name
+    -> Codec.Member i (Maybe f)
+optional f k = Codec.project f $ HasCodec.optionalNullable k HasCodec.codec
+
+required :: HasCodec.HasCodec f => (i -> f) -> Name.Name -> Codec.Member i f
+required f k = Codec.project f $ Codec.required k HasCodec.codec
diff --git a/source/library/Argo/Class/HasCodec.hs b/source/library/Argo/Class/HasCodec.hs
deleted file mode 100644
--- a/source/library/Argo/Class/HasCodec.hs
+++ /dev/null
@@ -1,818 +0,0 @@
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-
-module Argo.Class.HasCodec where
-
-import Control.Applicative ((<|>))
-
-import qualified Argo.Codec.Array as Codec
-import qualified Argo.Codec.Codec as Codec
-import qualified Argo.Codec.Object as Codec
-import qualified Argo.Codec.Value as Codec
-import qualified Argo.Json.Array as Array
-import qualified Argo.Json.Boolean as Boolean
-import qualified Argo.Json.Member as Member
-import qualified Argo.Json.Name as Name
-import qualified Argo.Json.Null as Null
-import qualified Argo.Json.Number as Number
-import qualified Argo.Json.Object as Object
-import qualified Argo.Json.String as String
-import qualified Argo.Json.Value as Value
-import qualified Argo.Pointer.Pointer as Pointer
-import qualified Argo.Schema.Identifier as Identifier
-import qualified Argo.Schema.Schema as Schema
-import qualified Argo.Type.Config as Config
-import qualified Argo.Type.Decimal as Decimal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Type.Nullable as Nullable
-import qualified Argo.Type.Optional as Optional
-import qualified Argo.Type.Permission as Permission
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.ByteString as ByteString
-import qualified Argo.Vendor.Map as Map
-import qualified Argo.Vendor.Text as Text
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Data.Bits as Bits
-import qualified Data.Functor.Identity as Identity
-import qualified Data.Int as Int
-import qualified Data.List.NonEmpty as NonEmpty
-import qualified Data.Maybe as Maybe
-import qualified Data.Typeable as Typeable
-import qualified Data.Word as Word
-import qualified Numeric.Natural as Natural
-
-class Typeable.Typeable a => HasCodec a where
-    codec :: Codec.Value a
-
-instance HasCodec Value.Value where
-    codec = valueCodec Schema.true id Just
-
-instance HasCodec Null.Null where
-    codec =
-        let schema = Schema.Null
-        in
-            valueCodec schema Value.Null $ \value -> case value of
-                Value.Null null_ -> Just null_
-                _ -> Nothing
-
-instance HasCodec Boolean.Boolean where
-    codec =
-        let schema = Schema.Boolean
-        in
-            valueCodec schema Value.Boolean $ \value -> case value of
-                Value.Boolean boolean -> Just boolean
-                _ -> Nothing
-
-instance HasCodec Number.Number where
-    codec =
-        let schema = Schema.Number
-        in
-            valueCodec schema Value.Number $ \value -> case value of
-                Value.Number number -> Just number
-                _ -> Nothing
-
-instance HasCodec String.String where
-    codec =
-        let schema = Schema.String Nothing Nothing
-        in
-            valueCodec schema Value.String $ \value -> case value of
-                Value.String string -> Just string
-                _ -> Nothing
-
-instance HasCodec a => HasCodec (Array.Array a) where
-    codec = Codec.identified Codec.Codec
-        { Codec.decode = do
-            array <- castValue "Array" $ \value -> case value of
-                Value.Array array -> Just array
-                _ -> Nothing
-            either (Trans.lift . Trans.throwE) (pure . Array.fromList)
-                . traverse (Codec.decodeWith codec)
-                $ Array.toList array
-        , Codec.encode =
-            Codec.tap
-            $ Trans.lift
-            . Trans.put
-            . Value.Array
-            . Array.fromList
-            . fmap (Codec.encodeWith codec)
-            . Array.toList
-        , Codec.schema = do
-            ref <- Codec.getRef (codec :: Codec.Value a)
-            pure . Schema.unidentified $ Schema.Array
-                Nothing
-                Nothing
-                (Left ref)
-                Nothing
-        }
-
-instance HasCodec a => HasCodec (Object.Object a) where
-    codec = Codec.identified Codec.Codec
-        { Codec.decode = do
-            object <- castValue "Object" $ \value -> case value of
-                Value.Object object -> Just object
-                _ -> Nothing
-            either (Trans.lift . Trans.throwE) (pure . Object.fromList)
-                . traverse
-                      (\(Member.Member k v) ->
-                          Member.Member k <$> Codec.decodeWith codec v
-                      )
-                $ Object.toList object
-        , Codec.encode =
-            Codec.tap
-            $ Trans.lift
-            . Trans.put
-            . Value.Object
-            . Object.fromList
-            . fmap
-                  (\(Member.Member k v) ->
-                      Member.Member k $ Codec.encodeWith codec v
-                  )
-            . Object.toList
-        , Codec.schema = do
-            ref <- Codec.getRef (codec :: Codec.Value a)
-            pure . Schema.unidentified . Schema.Object [] [] $ Just ref
-        }
-
-instance HasCodec a => HasCodec (Nullable.Nullable a) where
-    codec = nullable codec
-
-instance HasCodec a => HasCodec (Maybe a) where
-    codec =
-        Codec.identified $ Codec.map Nullable.toMaybe Nullable.fromMaybe codec
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    ) => HasCodec (Either a b) where
-    codec =
-        Codec.identified
-            $ Codec.mapMaybe
-                  (Just . Left)
-                  (either Just $ const Nothing)
-                  (Codec.tagged "Left" codec)
-            <|> Codec.mapMaybe
-                    (Just . Right)
-                    (either (const Nothing) Just)
-                    (Codec.tagged "Right" codec)
-
-instance HasCodec () where
-    codec =
-        Codec.identified . Codec.fromArrayCodec Permission.Forbid $ pure ()
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    ) => HasCodec (a, b) where
-    codec =
-        Codec.identified
-            . Codec.fromArrayCodec Permission.Forbid
-            $ (,)
-            <$> Codec.project fst (Codec.element codec)
-            <*> Codec.project snd (Codec.element codec)
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    , HasCodec c
-    ) => HasCodec (a, b, c) where
-    codec =
-        Codec.identified
-            . Codec.fromArrayCodec Permission.Forbid
-            $ (,,)
-            <$> Codec.project (\(a, _, _) -> a) (Codec.element codec)
-            <*> Codec.project (\(_, b, _) -> b) (Codec.element codec)
-            <*> Codec.project (\(_, _, c) -> c) (Codec.element codec)
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    , HasCodec c
-    , HasCodec d
-    ) => HasCodec (a, b, c, d) where
-    codec =
-        Codec.identified
-            . Codec.fromArrayCodec Permission.Forbid
-            $ (,,,)
-            <$> Codec.project (\(a, _, _, _) -> a) (Codec.element codec)
-            <*> Codec.project (\(_, b, _, _) -> b) (Codec.element codec)
-            <*> Codec.project (\(_, _, c, _) -> c) (Codec.element codec)
-            <*> Codec.project (\(_, _, _, d) -> d) (Codec.element codec)
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    , HasCodec c
-    , HasCodec d
-    , HasCodec e
-    ) => HasCodec (a, b, c, d, e) where
-    codec =
-        Codec.identified
-            . Codec.fromArrayCodec Permission.Forbid
-            $ (,,,,)
-            <$> Codec.project (\(a, _, _, _, _) -> a) (Codec.element codec)
-            <*> Codec.project (\(_, b, _, _, _) -> b) (Codec.element codec)
-            <*> Codec.project (\(_, _, c, _, _) -> c) (Codec.element codec)
-            <*> Codec.project (\(_, _, _, d, _) -> d) (Codec.element codec)
-            <*> Codec.project (\(_, _, _, _, e) -> e) (Codec.element codec)
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    , HasCodec c
-    , HasCodec d
-    , HasCodec e
-    , HasCodec f
-    ) => HasCodec (a, b, c, d, e, f) where
-    codec =
-        Codec.identified
-            . Codec.fromArrayCodec Permission.Forbid
-            $ (,,,,,)
-            <$> Codec.project (\(a, _, _, _, _, _) -> a) (Codec.element codec)
-            <*> Codec.project (\(_, b, _, _, _, _) -> b) (Codec.element codec)
-            <*> Codec.project (\(_, _, c, _, _, _) -> c) (Codec.element codec)
-            <*> Codec.project (\(_, _, _, d, _, _) -> d) (Codec.element codec)
-            <*> Codec.project (\(_, _, _, _, e, _) -> e) (Codec.element codec)
-            <*> Codec.project (\(_, _, _, _, _, f) -> f) (Codec.element codec)
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    , HasCodec c
-    , HasCodec d
-    , HasCodec e
-    , HasCodec f
-    , HasCodec g
-    ) => HasCodec (a, b, c, d, e, f, g) where
-    codec =
-        Codec.identified
-            . Codec.fromArrayCodec Permission.Forbid
-            $ (,,,,,,)
-            <$> Codec.project
-                    (\(a, _, _, _, _, _, _) -> a)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, b, _, _, _, _, _) -> b)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, c, _, _, _, _) -> c)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, d, _, _, _) -> d)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, _, e, _, _) -> e)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, _, _, f, _) -> f)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, _, _, _, g) -> g)
-                    (Codec.element codec)
-
-instance
-    ( HasCodec a
-    , HasCodec b
-    , HasCodec c
-    , HasCodec d
-    , HasCodec e
-    , HasCodec f
-    , HasCodec g
-    , HasCodec h
-    ) => HasCodec (a, b, c, d, e, f, g, h) where
-    codec =
-        Codec.identified
-            . Codec.fromArrayCodec Permission.Forbid
-            $ (,,,,,,,)
-            <$> Codec.project
-                    (\(a, _, _, _, _, _, _, _) -> a)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, b, _, _, _, _, _, _) -> b)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, c, _, _, _, _, _) -> c)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, d, _, _, _, _) -> d)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, _, e, _, _, _) -> e)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, _, _, f, _, _) -> f)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, _, _, _, g, _) -> g)
-                    (Codec.element codec)
-            <*> Codec.project
-                    (\(_, _, _, _, _, _, _, h) -> h)
-                    (Codec.element codec)
-
-instance HasCodec Bool where
-    codec = Codec.identified $ Codec.map Boolean.toBool Boolean.fromBool codec
-
-instance HasCodec Decimal.Decimal where
-    codec =
-        Codec.identified $ Codec.map Number.toDecimal Number.fromDecimal codec
-
-instance HasCodec Text.Text where
-    codec = Codec.identified $ Codec.map String.toText String.fromText codec
-
-instance {-# OVERLAPPABLE #-} HasCodec a => HasCodec [a] where
-    codec = Codec.identified $ Codec.map Array.toList Array.fromList codec
-
-instance HasCodec a => HasCodec (Map.Map Name.Name a) where
-    codec = Codec.identified $ Codec.map
-        (Map.fromList . fmap Member.toTuple . Object.toList)
-        (Object.fromList . fmap Member.fromTuple . Map.toList)
-        codec
-
-instance HasCodec a => HasCodec (Map.Map String.String a) where
-    codec = Codec.identified $ Codec.map
-        (Map.mapKeys Name.toString)
-        (Map.mapKeys Name.fromString)
-        codec
-
-instance HasCodec a => HasCodec (Map.Map Text.Text a) where
-    codec = Codec.identified $ Codec.map
-        (Map.mapKeys String.toText)
-        (Map.mapKeys String.fromText)
-        codec
-
-instance HasCodec a => HasCodec (Map.Map Text.LazyText a) where
-    codec = Codec.identified $ Codec.map
-        (Map.mapKeys Text.fromStrict)
-        (Map.mapKeys Text.toStrict)
-        codec
-
-instance HasCodec a => HasCodec (Map.Map String a) where
-    codec = Codec.identified
-        $ Codec.map (Map.mapKeys Text.unpack) (Map.mapKeys Text.pack) codec
-
-instance HasCodec String where
-    codec = Codec.identified $ Codec.map Text.unpack Text.pack codec
-
-instance HasCodec Char where
-    codec =
-        let schema = Schema.unidentified $ Schema.String (Just 1) (Just 1)
-        in
-            Codec.identified $ Codec.mapMaybe
-                (\x -> case Text.uncons x of
-                    Just (y, z) | Text.null z -> Just y
-                    _ -> Nothing
-                )
-                (Just . Text.singleton)
-                codec { Codec.schema = pure schema }
-
-instance HasCodec Text.LazyText where
-    codec = Codec.identified $ Codec.map Text.fromStrict Text.toStrict codec
-
-instance HasCodec a => HasCodec (NonEmpty.NonEmpty a) where
-    codec =
-        let
-            schema = do
-                itemSchema <- Codec.schema (codec :: Codec.Value a)
-                pure . Schema.unidentified $ Schema.Array
-                    (Just 1)
-                    Nothing
-                    (Left $ Schema.maybeRef itemSchema)
-                    Nothing
-        in
-            Codec.identified $ Codec.mapMaybe
-                NonEmpty.nonEmpty
-                (Just . NonEmpty.toList)
-                codec { Codec.schema = schema }
-
-instance HasCodec Integer where
-    codec =
-        let schema = Schema.unidentified $ Schema.Integer Nothing Nothing
-        in
-            Codec.identified $ Codec.mapMaybe
-                Decimal.toInteger
-                (Just . Decimal.fromInteger)
-                codec { Codec.schema = pure schema }
-
-instance HasCodec Int where
-    codec = integralCodec
-
-instance HasCodec Int.Int8 where
-    codec = integralCodec
-
-instance HasCodec Int.Int16 where
-    codec = integralCodec
-
-instance HasCodec Int.Int32 where
-    codec = integralCodec
-
-instance HasCodec Int.Int64 where
-    codec = integralCodec
-
-instance HasCodec Natural.Natural where
-    codec =
-        let
-            from = Bits.toIntegralSized :: Integer -> Maybe Natural.Natural
-            into = fromIntegral :: Natural.Natural -> Integer
-            schema = Schema.unidentified $ Schema.Integer (Just 0) Nothing
-        in Codec.identified $ Codec.mapMaybe
-            from
-            (Just . into)
-            codec { Codec.schema = pure schema }
-
-instance HasCodec Word where
-    codec = integralCodec
-
-instance HasCodec Word.Word8 where
-    codec = integralCodec
-
-instance HasCodec Word.Word16 where
-    codec = integralCodec
-
-instance HasCodec Word.Word32 where
-    codec = integralCodec
-
-instance HasCodec Word.Word64 where
-    codec = integralCodec
-
-instance HasCodec Float where
-    codec = Codec.identified $ Codec.mapMaybe
-        (Just . Decimal.toRealFloat)
-        Decimal.fromRealFloat
-        codec
-
-instance HasCodec Double where
-    codec = Codec.identified $ Codec.mapMaybe
-        (Just . Decimal.toRealFloat)
-        Decimal.fromRealFloat
-        codec
-
-instance HasCodec Pointer.Pointer where
-    codec = Codec.identified $ Codec.mapMaybe
-        (either (const Nothing) Just
-        . Decoder.run Pointer.decode
-        . Text.encodeUtf8
-        )
-        (either (const Nothing) Just
-        . Text.decodeUtf8'
-        . ByteString.toStrict
-        . Builder.toLazyByteString
-        . Encoder.run Config.initial
-        . Pointer.encode
-        )
-        codec
-
-instance HasCodec Identifier.Identifier where
-    codec =
-        let prefix = Text.pack "#/definitions/"
-        in
-            Codec.identified $ Codec.mapMaybe
-                (fmap Identifier.fromText . Text.stripPrefix prefix)
-                (Just . mappend prefix . Identifier.toText)
-                codec
-
-instance HasCodec Name.Name where
-    codec = Codec.identified $ Codec.map Name.fromString Name.toString codec
-
-instance HasCodec Schema.Schema where
-    codec =
-        let
-            trueCodec =
-                Codec.mapMaybe
-                        (const $ Just Schema.True)
-                        (\x -> case x of
-                            Schema.True -> Just ()
-                            _ -> Nothing
-                        )
-                    . Codec.literalCodec
-                    . Value.Boolean
-                    $ Boolean.fromBool True
-            falseCodec =
-                Codec.mapMaybe
-                        (const $ Just Schema.False)
-                        (\x -> case x of
-                            Schema.False -> Just ()
-                            _ -> Nothing
-                        )
-                    . Codec.literalCodec
-                    . Value.Boolean
-                    $ Boolean.fromBool False
-            constCodec =
-                Codec.mapMaybe
-                        (Just . Schema.Const)
-                        (\x -> case x of
-                            Schema.Const y -> Just y
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    $ Codec.required (name "const") codec
-            nullCodec =
-                Codec.mapMaybe
-                        (const $ Just Schema.Null)
-                        (\x -> case x of
-                            Schema.Null -> Just ()
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    . Codec.required (name "type")
-                    . Codec.literalCodec
-                    . Value.String
-                    . String.fromText
-                    $ Text.pack "null"
-            booleanCodec =
-                Codec.mapMaybe
-                        (const $ Just Schema.Boolean)
-                        (\x -> case x of
-                            Schema.Boolean -> Just ()
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    . Codec.required (name "type")
-                    . Codec.literalCodec
-                    . Value.String
-                    . String.fromText
-                    $ Text.pack "boolean"
-            numberCodec =
-                Codec.mapMaybe
-                        (const $ Just Schema.Number)
-                        (\x -> case x of
-                            Schema.Number -> Just ()
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    . Codec.required (name "type")
-                    . Codec.literalCodec
-                    . Value.String
-                    . String.fromText
-                    $ Text.pack "number"
-            integerCodec =
-                Codec.mapMaybe
-                        (\(_, y, z) -> Just $ Schema.Integer
-                            (Optional.toMaybe y)
-                            (Optional.toMaybe z)
-                        )
-                        (\x -> case x of
-                            Schema.Integer y z ->
-                                Just
-                                    ( ()
-                                    , Optional.fromMaybe y
-                                    , Optional.fromMaybe z
-                                    )
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    $ (,,)
-                    <$> Codec.project
-                            (\(a, _, _) -> a)
-                            (Codec.required (name "type")
-                            . Codec.literalCodec
-                            . Value.String
-                            . String.fromText
-                            $ Text.pack "integer"
-                            )
-                    <*> Codec.project
-                            (\(_, b, _) -> b)
-                            (Codec.optional (name "minimum") codec)
-                    <*> Codec.project
-                            (\(_, _, c) -> c)
-                            (Codec.optional (name "maximum") codec)
-            stringCodec =
-                Codec.mapMaybe
-                        (\(_, y, z) -> Just $ Schema.String
-                            (Optional.toMaybe y)
-                            (Optional.toMaybe z)
-                        )
-                        (\x -> case x of
-                            Schema.String y z ->
-                                Just
-                                    ( ()
-                                    , Optional.fromMaybe y
-                                    , Optional.fromMaybe z
-                                    )
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    $ (,,)
-                    <$> Codec.project
-                            (\(a, _, _) -> a)
-                            (Codec.required (name "type")
-                            . Codec.literalCodec
-                            . Value.String
-                            . String.fromText
-                            $ Text.pack "string"
-                            )
-                    <*> Codec.project
-                            (\(_, b, _) -> b)
-                            (Codec.optional (name "minLength") codec)
-                    <*> Codec.project
-                            (\(_, _, c) -> c)
-                            (Codec.optional (name "maxLength") codec)
-            arrayCodec =
-                Codec.mapMaybe
-                        (\(_, lo, hi, e, m) -> Just $ Schema.Array
-                            (Optional.toMaybe lo)
-                            (Optional.toMaybe hi)
-                            e
-                            (Optional.toMaybe m)
-                        )
-                        (\x -> case x of
-                            Schema.Array lo hi e m -> Just
-                                ( ()
-                                , Optional.fromMaybe lo
-                                , Optional.fromMaybe hi
-                                , e
-                                , Optional.fromMaybe m
-                                )
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    $ (,,,,)
-                    <$> Codec.project
-                            (\(a, _, _, _, _) -> a)
-                            (Codec.required (name "type")
-                            . Codec.literalCodec
-                            . Value.String
-                            . String.fromText
-                            $ Text.pack "array"
-                            )
-                    <*> Codec.project
-                            (\(_, b, _, _, _) -> b)
-                            (Codec.optional (name "minItems") codec)
-                    <*> Codec.project
-                            (\(_, _, c, _, _) -> c)
-                            (Codec.optional (name "maxItems") codec)
-                    <*> Codec.project
-                            (\(_, _, _, d, _) -> d)
-                            (Codec.required (name "items")
-                            . Codec.withIdentifier
-                                  (Identifier.fromText
-                                  $ Text.pack "OneOf Schema [Schema]"
-                                  )
-                            $ Codec.mapMaybe
-                                  (Just . Left)
-                                  (either Just $ const Nothing)
-                                  codec
-                            <|> Codec.mapMaybe
-                                    (Just . Right)
-                                    (either (const Nothing) Just)
-                                    codec
-                            )
-                    <*> Codec.project
-                            (\(_, _, _, _, e) -> e)
-                            (Codec.optional (name "additionalItems") codec)
-            objectCodec =
-                Codec.mapMaybe
-                        (\(_, ps, rs, m) -> Just $ Schema.Object
-                            (fmap Member.toTuple
-                            . foldMap Object.toList
-                            $ Optional.toMaybe ps
-                            )
-                            (Maybe.fromMaybe [] $ Optional.toMaybe rs)
-                            (Optional.toMaybe m)
-                        )
-                        (\x -> case x of
-                            Schema.Object ps rs m -> Just
-                                ( ()
-                                , if null ps
-                                    then Optional.nothing
-                                    else Optional.just . Object.fromList $ fmap
-                                        Member.fromTuple
-                                        ps
-                                , if null rs
-                                    then Optional.nothing
-                                    else Optional.just rs
-                                , Optional.fromMaybe m
-                                )
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    $ (,,,)
-                    <$> Codec.project
-                            (\(a, _, _, _) -> a)
-                            (Codec.required (name "type")
-                            . Codec.literalCodec
-                            . Value.String
-                            . String.fromText
-                            $ Text.pack "object"
-                            )
-                    <*> Codec.project
-                            (\(_, b, _, _) -> b)
-                            (Codec.optional (name "properties") codec)
-                    <*> Codec.project
-                            (\(_, _, c, _) -> c)
-                            (Codec.optional (name "required") codec)
-                    <*> Codec.project
-                            (\(_, _, _, d) -> d)
-                            (Codec.optional (name "additionalProperties") codec
-                            )
-            refCodec =
-                Codec.mapMaybe
-                        (Just . Schema.Ref)
-                        (\x -> case x of
-                            Schema.Ref y -> Just y
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    $ Codec.required (name "$ref") codec
-            oneOfCodec =
-                Codec.mapMaybe
-                        (Just . Schema.OneOf)
-                        (\x -> case x of
-                            Schema.OneOf y -> Just y
-                            _ -> Nothing
-                        )
-                    . Codec.fromObjectCodec Permission.Forbid
-                    $ Codec.required (name "oneOf") codec
-        in
-            Codec.identified
-            $ trueCodec
-            <|> falseCodec
-            <|> constCodec
-            <|> nullCodec
-            <|> booleanCodec
-            <|> numberCodec
-            <|> integerCodec
-            <|> stringCodec
-            <|> arrayCodec
-            <|> objectCodec
-            <|> refCodec
-            <|> oneOfCodec
-
-name :: String -> Name.Name
-name = Name.fromString . String.fromText . Text.pack
-
-valueCodec
-    :: forall a
-     . Typeable.Typeable a
-    => Schema.Schema
-    -> (a -> Value.Value)
-    -> (Value.Value -> Maybe a)
-    -> Codec.Value a
-valueCodec schema toValue fromValue = Codec.identified Codec.Codec
-    { Codec.decode = castValue
-        (Codec.typeName (Typeable.Proxy :: Typeable.Proxy a))
-        fromValue
-    , Codec.encode = Codec.tap $ Trans.lift . Trans.put . toValue
-    , Codec.schema = pure $ Schema.unidentified schema
-    }
-
-castValue
-    :: String
-    -> (Value.Value -> Maybe a)
-    -> Trans.ReaderT
-           Value.Value
-           (Trans.ExceptT String Identity.Identity)
-           a
-castValue expected fromValue = do
-    value <- Trans.ask
-    case fromValue value of
-        Nothing -> Trans.lift . Trans.throwE $ typeMismatch expected value
-        Just x -> pure x
-
-typeMismatch :: String -> Value.Value -> String
-typeMismatch expected value =
-    let
-        actual = case value of
-            Value.Null _ -> "Null"
-            Value.Boolean _ -> "Boolean"
-            Value.Number _ -> "Number"
-            Value.String _ -> "String"
-            Value.Array _ -> "Array"
-            Value.Object _ -> "Object"
-    in "expected " <> expected <> " but got " <> actual
-
-integralCodec
-    :: forall a
-     . (Bits.Bits a, Bounded a, Integral a, Typeable.Typeable a)
-    => Codec.Value a
-integralCodec =
-    let
-        from = Bits.toIntegralSized :: Integer -> Maybe a
-        into = fromIntegral :: a -> Integer
-        schema = Schema.unidentified $ Schema.Integer
-            (Just $ toInteger (minBound :: a))
-            (Just $ toInteger (maxBound :: a))
-    in Codec.identified $ Codec.mapMaybe
-        from
-        (Just . into)
-        codec { Codec.schema = pure schema }
-
-optionalNullable
-    :: Typeable.Typeable a
-    => Name.Name
-    -> Codec.Value a
-    -> Codec.Object (Maybe a)
-optionalNullable k =
-    Codec.map
-            (maybe Nothing Nullable.toMaybe . Optional.toMaybe)
-            (Optional.fromMaybe . fmap Nullable.just)
-        . Codec.optional k
-        . nullable
-
-nullable
-    :: Typeable.Typeable a
-    => Codec.Value a
-    -> Codec.Value (Nullable.Nullable a)
-nullable c =
-    Codec.identified
-        $ Codec.mapMaybe (Just . Nullable.just) Nullable.toMaybe c
-        <|> Codec.map (const Nullable.nothing) (const $ Null.fromUnit ()) codec
diff --git a/source/library/Argo/Codec/Array.hs b/source/library/Argo/Codec/Array.hs
deleted file mode 100644
--- a/source/library/Argo/Codec/Array.hs
+++ /dev/null
@@ -1,61 +0,0 @@
-module Argo.Codec.Array where
-
-import qualified Argo.Codec.Codec as Codec
-import qualified Argo.Codec.List as Codec
-import qualified Argo.Codec.Value as Codec
-import qualified Argo.Json.Array as Array
-import qualified Argo.Json.Value as Value
-import qualified Argo.Schema.Identifier as Identifier
-import qualified Argo.Schema.Schema as Schema
-import qualified Argo.Type.Permission as Permission
-import qualified Argo.Vendor.Map as Map
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Data.Functor.Identity as Identity
-import qualified Data.List.NonEmpty as NonEmpty
-
-type Array a
-    = Codec.List
-          ( Trans.AccumT
-                (Map.Map Identifier.Identifier Schema.Schema)
-                Identity.Identity
-                [(Maybe Identifier.Identifier, Schema.Schema)]
-          )
-          Value.Value
-          a
-
-fromArrayCodec :: Permission.Permission -> Array a -> Codec.Value a
-fromArrayCodec =
-    Codec.fromListCodec
-            (\permission schemasM -> do
-                schemas <- schemasM
-                pure . Schema.unidentified $ Schema.Array
-                    (Just . fromIntegral $ length schemas)
-                    (case permission of
-                        Permission.Allow -> Nothing
-                        Permission.Forbid ->
-                            Just . fromIntegral $ length schemas
-                    )
-                    (case NonEmpty.nonEmpty $ fmap Schema.maybeRef schemas of
-                        Nothing -> Left Schema.False
-                        Just xs -> Right xs
-                    )
-                    Nothing
-            )
-        $ Codec.map Array.toList Array.fromList Codec.arrayCodec
-
-element :: Codec.Value a -> Array a
-element c = Codec.Codec
-    { Codec.decode = do
-        l <- Trans.get
-        case l of
-            [] -> Trans.lift $ Trans.throwE "unexpected empty list"
-            h : t -> case Codec.decodeWith c h of
-                Left y -> Trans.lift $ Trans.throwE y
-                Right y -> do
-                    Trans.put t
-                    pure y
-    , Codec.encode = \x -> do
-        Trans.tell [Codec.encodeWith c x]
-        pure x
-    , Codec.schema = pure . Schema.unidentified <$> Codec.getRef c
-    }
diff --git a/source/library/Argo/Codec/Codec.hs b/source/library/Argo/Codec/Codec.hs
deleted file mode 100644
--- a/source/library/Argo/Codec/Codec.hs
+++ /dev/null
@@ -1,92 +0,0 @@
-{-# LANGUAGE FlexibleInstances #-}
-
-module Argo.Codec.Codec where
-
-import Control.Applicative ((<|>))
-
-import qualified Control.Applicative as Applicative
-
-data Codec r w s i o = Codec
-    { decode :: r o
-    , encode :: i -> w o
-    , schema :: s
-    }
-
-instance (Functor r, Functor w) => Functor (Codec r w s i) where
-    fmap f c = Codec
-        { decode = f <$> decode c
-        , encode = fmap f . encode c
-        , schema = schema c
-        }
-
-instance
-    ( Applicative r
-    , Applicative w
-    , Applicative s
-    , Monoid a
-    ) => Applicative (Codec r w (s a) i) where
-    pure x = Codec
-        { decode = pure x
-        , encode = const $ pure x
-        , schema = pure mempty
-        }
-    cf <*> cx = Codec
-        { decode = decode cf <*> decode cx
-        , encode = \i -> encode cf i <*> encode cx i
-        , schema = (<>) <$> schema cf <*> schema cx
-        }
-
-instance
-    ( Applicative.Alternative r
-    , Applicative.Alternative w
-    , Applicative s
-    , Monoid a
-    ) => Applicative.Alternative (Codec r w (s a) i) where
-    empty = Codec
-        { decode = Applicative.empty
-        , encode = const Applicative.empty
-        , schema = pure mempty
-        }
-    cx <|> cy = Codec
-        { decode = decode cx <|> decode cy
-        , encode = \i -> encode cx i <|> encode cy i
-        , schema = (<>) <$> schema cx <*> schema cy
-        }
-
-map
-    :: (Functor r, Functor w)
-    => (a -> b)
-    -> (b -> a)
-    -> Codec r w s a a
-    -> Codec r w s b b
-map f g c = Codec
-    { decode = f <$> decode c
-    , encode = fmap f . encode c . g
-    , schema = schema c
-    }
-
-mapMaybe
-    :: (Applicative.Alternative r, Applicative.Alternative w, Monad r, Monad w)
-    => (o2 -> Maybe o1)
-    -> (i1 -> Maybe i2)
-    -> Codec r w s i2 o2
-    -> Codec r w s i1 o1
-mapMaybe f g c = Codec
-    { decode = do
-        o2 <- decode c
-        toAlternative $ f o2
-    , encode = \i1 -> do
-        i2 <- toAlternative $ g i1
-        o2 <- encode c i2
-        toAlternative $ f o2
-    , schema = schema c
-    }
-
-project :: (i -> f) -> Codec r w s f o -> Codec r w s i o
-project f c = c { encode = encode c . f }
-
-tap :: Functor f => (a -> f b) -> a -> f a
-tap f x = x <$ f x
-
-toAlternative :: Applicative.Alternative m => Maybe a -> m a
-toAlternative = maybe Applicative.empty pure
diff --git a/source/library/Argo/Codec/List.hs b/source/library/Argo/Codec/List.hs
deleted file mode 100644
--- a/source/library/Argo/Codec/List.hs
+++ /dev/null
@@ -1,57 +0,0 @@
-module Argo.Codec.List where
-
-import qualified Argo.Codec.Codec as Codec
-import qualified Argo.Codec.Value as Codec
-import qualified Argo.Schema.Identifier as Identifier
-import qualified Argo.Schema.Schema as Schema
-import qualified Argo.Type.Permission as Permission
-import qualified Argo.Vendor.Map as Map
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-import qualified Data.Functor.Identity as Identity
-
-type List s e a
-    = Codec.Codec
-          (Trans.StateT [e] (Trans.ExceptT String Identity.Identity))
-          (Trans.WriterT [e] Identity.Identity)
-          s
-          a
-          a
-
-fromListCodec
-    :: ( Permission.Permission
-       -> s
-       -> Trans.AccumT
-              (Map.Map Identifier.Identifier Schema.Schema)
-              Identity.Identity
-              (Maybe Identifier.Identifier, Schema.Schema)
-       )
-    -> Codec.Value [e]
-    -> Permission.Permission
-    -> List s e a
-    -> Codec.Value a
-fromListCodec f ce p ca = Codec.Codec
-    { Codec.decode = do
-        xs <- Codec.decode ce
-        case
-                Identity.runIdentity . Trans.runExceptT $ Trans.runStateT
-                    (Codec.decode ca)
-                    xs
-            of
-                Left x -> Trans.lift $ Trans.throwE x
-                Right (x, ys) -> do
-                    case (p, ys) of
-                        (Permission.Forbid, _ : _) ->
-                            Trans.lift $ Trans.throwE "leftover elements"
-                        _ -> pure ()
-                    pure x
-    , Codec.encode = \x -> do
-        Monad.void
-            . Codec.encode ce
-            . snd
-            . Identity.runIdentity
-            . Trans.runWriterT
-            $ Codec.encode ca x
-        pure x
-    , Codec.schema = f p $ Codec.schema ca
-    }
diff --git a/source/library/Argo/Codec/Object.hs b/source/library/Argo/Codec/Object.hs
deleted file mode 100644
--- a/source/library/Argo/Codec/Object.hs
+++ /dev/null
@@ -1,116 +0,0 @@
-module Argo.Codec.Object where
-
-import qualified Argo.Codec.Codec as Codec
-import qualified Argo.Codec.List as Codec
-import qualified Argo.Codec.Value as Codec
-import qualified Argo.Json.Member as Member
-import qualified Argo.Json.Name as Name
-import qualified Argo.Json.Object as Object
-import qualified Argo.Json.String as String
-import qualified Argo.Json.Value as Value
-import qualified Argo.Schema.Identifier as Identifier
-import qualified Argo.Schema.Schema as Schema
-import qualified Argo.Type.Optional as Optional
-import qualified Argo.Type.Permission as Permission
-import qualified Argo.Vendor.Map as Map
-import qualified Argo.Vendor.Text as Text
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-import qualified Data.Functor.Identity as Identity
-import qualified Data.List as List
-import qualified Data.Maybe as Maybe
-
-type Object a
-    = Codec.List
-          ( Trans.AccumT
-                (Map.Map Identifier.Identifier Schema.Schema)
-                Identity.Identity
-                [ ( (Name.Name, Bool)
-                  , (Maybe Identifier.Identifier, Schema.Schema)
-                  )
-                ]
-          )
-          (Member.Member Value.Value)
-          a
-
-fromObjectCodec :: Permission.Permission -> Object a -> Codec.Value a
-fromObjectCodec =
-    Codec.fromListCodec
-            (\permission schemasM -> do
-                schemas <- schemasM
-                pure
-                    . Schema.unidentified
-                    . Schema.Object
-                          (fmap
-                              (\((k, _), s) -> (k, Schema.maybeRef s))
-                              schemas
-                          )
-                          (Maybe.mapMaybe
-                              (\((k, r), _) -> if r then Just k else Nothing)
-                              schemas
-                          )
-                    $ case permission of
-                          Permission.Allow -> Nothing
-                          Permission.Forbid -> Just Schema.False
-            )
-        $ Codec.map Object.toList Object.fromList Codec.objectCodec
-
-required :: Name.Name -> Codec.Value a -> Object a
-required k c = Codec.Codec
-    { Codec.decode = do
-        m <- Codec.decode (optional k c)
-        case Optional.toMaybe m of
-            Nothing ->
-                Trans.lift
-                    . Trans.throwE
-                    $ "missing required member: "
-                    <> show k
-            Just x -> pure x
-    , Codec.encode = \x -> do
-        Monad.void . Codec.encode (optional k c) $ Optional.just x
-        pure x
-    , Codec.schema =
-        pure . (,) (k, True) . Schema.unidentified <$> Codec.getRef c
-    }
-
-optional :: Name.Name -> Codec.Value a -> Object (Optional.Optional a)
-optional k c = Codec.Codec
-    { Codec.decode = do
-        xs <- Trans.get
-        case List.partition (\(Member.Member j _) -> j == k) xs of
-            (Member.Member _ x : _, ys) -> case Codec.decodeWith c x of
-                Left y -> Trans.lift $ Trans.throwE y
-                Right y -> do
-                    Trans.put ys
-                    pure $ Optional.just y
-            _ -> pure Optional.nothing
-    , Codec.encode = \x -> do
-        case Optional.toMaybe x of
-            Nothing -> pure ()
-            Just y -> Trans.tell [Member.Member k $ Codec.encodeWith c y]
-        pure x
-    , Codec.schema =
-        pure . (,) (k, False) . Schema.unidentified <$> Codec.getRef c
-    }
-
-tagged :: String -> Codec.Value a -> Codec.Value a
-tagged t c =
-    Codec.map snd ((,) ())
-        . fromObjectCodec Permission.Forbid
-        $ (,)
-        <$> Codec.project
-                fst
-                (required
-                    (Name.fromString . String.fromText $ Text.pack "type")
-                    (Codec.literalCodec
-                    . Value.String
-                    . String.fromText
-                    $ Text.pack t
-                    )
-                )
-        <*> Codec.project
-                snd
-                (required
-                    (Name.fromString . String.fromText $ Text.pack "value")
-                    c
-                )
diff --git a/source/library/Argo/Codec/Value.hs b/source/library/Argo/Codec/Value.hs
deleted file mode 100644
--- a/source/library/Argo/Codec/Value.hs
+++ /dev/null
@@ -1,124 +0,0 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-
-module Argo.Codec.Value where
-
-import qualified Argo.Codec.Codec as Codec
-import qualified Argo.Json.Array as Array
-import qualified Argo.Json.Null as Null
-import qualified Argo.Json.Object as Object
-import qualified Argo.Json.Value as Value
-import qualified Argo.Schema.Identifier as Identifier
-import qualified Argo.Schema.Schema as Schema
-import qualified Argo.Vendor.Map as Map
-import qualified Argo.Vendor.Text as Text
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-import qualified Data.Functor.Identity as Identity
-import qualified Data.Typeable as Typeable
-
-decodeWith :: Value a -> Value.Value -> Either String a
-decodeWith c =
-    Identity.runIdentity . Trans.runExceptT . Trans.runReaderT (Codec.decode c)
-
-encodeWith :: Value a -> a -> Value.Value
-encodeWith c x =
-    snd
-        . Identity.runIdentity
-        . Trans.runStateT (Trans.runMaybeT $ Codec.encode c x)
-        . Value.Null
-        $ Null.fromUnit ()
-
-type Value a
-    = Codec.Codec
-          (Trans.ReaderT Value.Value (Trans.ExceptT String Identity.Identity))
-          (Trans.MaybeT (Trans.StateT Value.Value Identity.Identity))
-          ( Trans.AccumT
-                (Map.Map Identifier.Identifier Schema.Schema)
-                Identity.Identity
-                (Maybe Identifier.Identifier, Schema.Schema)
-          )
-          a
-          a
-
-arrayCodec :: Value (Array.Array Value.Value)
-arrayCodec = Codec.Codec
-    { Codec.decode = do
-        x <- Trans.ask
-        case x of
-            Value.Array y -> pure y
-            _ ->
-                Trans.lift . Trans.throwE $ "expected Array but got " <> show x
-    , Codec.encode = \x -> do
-        Trans.lift . Trans.put $ Value.Array x
-        pure x
-    , Codec.schema = pure $ Schema.unidentified Schema.false
-    }
-
-objectCodec :: Value (Object.Object Value.Value)
-objectCodec = Codec.Codec
-    { Codec.decode = do
-        x <- Trans.ask
-        case x of
-            Value.Object y -> pure y
-            _ ->
-                Trans.lift
-                    . Trans.throwE
-                    $ "expected Object but got "
-                    <> show x
-    , Codec.encode = \x -> do
-        Trans.lift . Trans.put $ Value.Object x
-        pure x
-    , Codec.schema = pure $ Schema.unidentified Schema.false
-    }
-
-literalCodec :: Value.Value -> Value ()
-literalCodec expected = Codec.Codec
-    { Codec.decode = do
-        actual <- Trans.ask
-        Monad.when (actual /= expected)
-            . Trans.lift
-            . Trans.throwE
-            $ "expected "
-            <> show expected
-            <> " but got "
-            <> show actual
-    , Codec.encode = const . Trans.lift $ Trans.put expected
-    , Codec.schema = pure . Schema.unidentified $ Schema.Const expected
-    }
-
-identified :: forall a . Typeable.Typeable a => Value a -> Value a
-identified = withIdentifier . Identifier.fromText . Text.pack $ typeName
-    (Typeable.Proxy :: Typeable.Proxy a)
-
-withIdentifier :: Identifier.Identifier -> Value a -> Value a
-withIdentifier identifier codec =
-    let
-        newSchema = do
-            (_, schema) <- Codec.schema codec
-            Trans.add $ Map.singleton identifier schema
-            pure $ Schema.withIdentifier identifier schema
-    in codec { Codec.schema = newSchema }
-
-typeName :: Typeable.Typeable a => Typeable.Proxy a -> String
-typeName = show . Typeable.typeRep
-
-getRef
-    :: Value a
-    -> Trans.AccumT
-           (Map.Map Identifier.Identifier Schema.Schema)
-           Identity.Identity
-           Schema.Schema
-getRef codec = do
-    let
-        (maybeIdentifier, schema) =
-            fst . Identity.runIdentity $ Trans.runAccumT
-                (Codec.schema codec)
-                Map.empty
-    case maybeIdentifier of
-        Nothing -> pure schema
-        Just identifier -> do
-            schemas <- Trans.look
-            Monad.unless (Map.member identifier schemas) $ do
-                Trans.add $ Map.singleton identifier schema
-                Monad.void $ Codec.schema codec
-            pure $ Schema.Ref identifier
diff --git a/source/library/Argo/Decode.hs b/source/library/Argo/Decode.hs
deleted file mode 100644
--- a/source/library/Argo/Decode.hs
+++ /dev/null
@@ -1,16 +0,0 @@
-module Argo.Decode where
-
-import qualified Argo.Class.HasCodec as HasCodec
-import qualified Argo.Codec.Value as Codec
-import qualified Argo.Json.Value as Value
-import qualified Argo.Pointer.Pointer as Pointer
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Vendor.ByteString as ByteString
-
-decode :: HasCodec.HasCodec a => ByteString.ByteString -> Either String a
-decode x = do
-    y <- Decoder.run (Decoder.spaces *> Value.decode) x
-    Codec.decodeWith HasCodec.codec y
-
-decodePointer :: ByteString.ByteString -> Either String Pointer.Pointer
-decodePointer = Decoder.run Pointer.decode
diff --git a/source/library/Argo/Encode.hs b/source/library/Argo/Encode.hs
deleted file mode 100644
--- a/source/library/Argo/Encode.hs
+++ /dev/null
@@ -1,30 +0,0 @@
-module Argo.Encode where
-
-import qualified Argo.Class.HasCodec as HasCodec
-import qualified Argo.Codec.Value as Codec
-import qualified Argo.Json.Value as Value
-import qualified Argo.Literal as Literal
-import qualified Argo.Pointer.Pointer as Pointer
-import qualified Argo.Type.Config as Config
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Type.Indent as Indent
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-
-encode :: HasCodec.HasCodec a => a -> Builder.Builder
-encode = encodeWith $ Indent.Spaces 0
-
-encodeWith :: HasCodec.HasCodec a => Indent.Indent -> a -> Builder.Builder
-encodeWith i x =
-    let c = Config.initial { Config.indent = i }
-    in
-        Encoder.run c $ do
-            Value.encode $ Codec.encodeWith HasCodec.codec x
-            Monad.when (Config.hasIndent c)
-                . Trans.lift
-                . Trans.tell
-                $ Builder.word8 Literal.newLine
-
-encodePointer :: Pointer.Pointer -> Builder.Builder
-encodePointer = Encoder.run Config.initial . Pointer.encode
diff --git a/source/library/Argo/Internal/Class/HasCodec.hs b/source/library/Argo/Internal/Class/HasCodec.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Class/HasCodec.hs
@@ -0,0 +1,818 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Argo.Internal.Class.HasCodec where
+
+import Control.Applicative ((<|>))
+
+import qualified Argo.Internal.Codec.Array as Codec
+import qualified Argo.Internal.Codec.Codec as Codec
+import qualified Argo.Internal.Codec.Object as Codec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Json.Array as Array
+import qualified Argo.Internal.Json.Boolean as Boolean
+import qualified Argo.Internal.Json.Member as Member
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Json.Null as Null
+import qualified Argo.Internal.Json.Number as Number
+import qualified Argo.Internal.Json.Object as Object
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Pointer.Pointer as Pointer
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Internal.Schema.Schema as Schema
+import qualified Argo.Internal.Type.Config as Config
+import qualified Argo.Internal.Type.Decimal as Decimal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Internal.Type.Nullable as Nullable
+import qualified Argo.Internal.Type.Optional as Optional
+import qualified Argo.Internal.Type.Permission as Permission
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.ByteString as ByteString
+import qualified Argo.Vendor.Map as Map
+import qualified Argo.Vendor.Text as Text
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Data.Bits as Bits
+import qualified Data.Functor.Identity as Identity
+import qualified Data.Int as Int
+import qualified Data.List.NonEmpty as NonEmpty
+import qualified Data.Maybe as Maybe
+import qualified Data.Typeable as Typeable
+import qualified Data.Word as Word
+import qualified Numeric.Natural as Natural
+
+class Typeable.Typeable a => HasCodec a where
+    codec :: Codec.Value a
+
+instance HasCodec Value.Value where
+    codec = valueCodec Schema.true id Just
+
+instance HasCodec Null.Null where
+    codec =
+        let schema = Schema.Null
+        in
+            valueCodec schema Value.Null $ \value -> case value of
+                Value.Null null_ -> Just null_
+                _ -> Nothing
+
+instance HasCodec Boolean.Boolean where
+    codec =
+        let schema = Schema.Boolean
+        in
+            valueCodec schema Value.Boolean $ \value -> case value of
+                Value.Boolean boolean -> Just boolean
+                _ -> Nothing
+
+instance HasCodec Number.Number where
+    codec =
+        let schema = Schema.Number
+        in
+            valueCodec schema Value.Number $ \value -> case value of
+                Value.Number number -> Just number
+                _ -> Nothing
+
+instance HasCodec String.String where
+    codec =
+        let schema = Schema.String Nothing Nothing
+        in
+            valueCodec schema Value.String $ \value -> case value of
+                Value.String string -> Just string
+                _ -> Nothing
+
+instance HasCodec a => HasCodec (Array.Array a) where
+    codec = Codec.identified Codec.Codec
+        { Codec.decode = do
+            array <- castValue "Array" $ \value -> case value of
+                Value.Array array -> Just array
+                _ -> Nothing
+            either (Trans.lift . Trans.throwE) (pure . Array.fromList)
+                . traverse (Codec.decodeWith codec)
+                $ Array.toList array
+        , Codec.encode =
+            Codec.tap
+            $ Trans.lift
+            . Trans.put
+            . Value.Array
+            . Array.fromList
+            . fmap (Codec.encodeWith codec)
+            . Array.toList
+        , Codec.schema = do
+            ref <- Codec.getRef (codec :: Codec.Value a)
+            pure . Schema.unidentified $ Schema.Array
+                Nothing
+                Nothing
+                (Left ref)
+                Nothing
+        }
+
+instance HasCodec a => HasCodec (Object.Object a) where
+    codec = Codec.identified Codec.Codec
+        { Codec.decode = do
+            object <- castValue "Object" $ \value -> case value of
+                Value.Object object -> Just object
+                _ -> Nothing
+            either (Trans.lift . Trans.throwE) (pure . Object.fromList)
+                . traverse
+                      (\(Member.Member k v) ->
+                          Member.Member k <$> Codec.decodeWith codec v
+                      )
+                $ Object.toList object
+        , Codec.encode =
+            Codec.tap
+            $ Trans.lift
+            . Trans.put
+            . Value.Object
+            . Object.fromList
+            . fmap
+                  (\(Member.Member k v) ->
+                      Member.Member k $ Codec.encodeWith codec v
+                  )
+            . Object.toList
+        , Codec.schema = do
+            ref <- Codec.getRef (codec :: Codec.Value a)
+            pure . Schema.unidentified . Schema.Object [] [] $ Just ref
+        }
+
+instance HasCodec a => HasCodec (Nullable.Nullable a) where
+    codec = nullable codec
+
+instance HasCodec a => HasCodec (Maybe a) where
+    codec =
+        Codec.identified $ Codec.map Nullable.toMaybe Nullable.fromMaybe codec
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    ) => HasCodec (Either a b) where
+    codec =
+        Codec.identified
+            $ Codec.mapMaybe
+                  (Just . Left)
+                  (either Just $ const Nothing)
+                  (Codec.tagged "Left" codec)
+            <|> Codec.mapMaybe
+                    (Just . Right)
+                    (either (const Nothing) Just)
+                    (Codec.tagged "Right" codec)
+
+instance HasCodec () where
+    codec =
+        Codec.identified . Codec.fromArrayCodec Permission.Forbid $ pure ()
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    ) => HasCodec (a, b) where
+    codec =
+        Codec.identified
+            . Codec.fromArrayCodec Permission.Forbid
+            $ (,)
+            <$> Codec.project fst (Codec.element codec)
+            <*> Codec.project snd (Codec.element codec)
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    , HasCodec c
+    ) => HasCodec (a, b, c) where
+    codec =
+        Codec.identified
+            . Codec.fromArrayCodec Permission.Forbid
+            $ (,,)
+            <$> Codec.project (\(a, _, _) -> a) (Codec.element codec)
+            <*> Codec.project (\(_, b, _) -> b) (Codec.element codec)
+            <*> Codec.project (\(_, _, c) -> c) (Codec.element codec)
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    , HasCodec c
+    , HasCodec d
+    ) => HasCodec (a, b, c, d) where
+    codec =
+        Codec.identified
+            . Codec.fromArrayCodec Permission.Forbid
+            $ (,,,)
+            <$> Codec.project (\(a, _, _, _) -> a) (Codec.element codec)
+            <*> Codec.project (\(_, b, _, _) -> b) (Codec.element codec)
+            <*> Codec.project (\(_, _, c, _) -> c) (Codec.element codec)
+            <*> Codec.project (\(_, _, _, d) -> d) (Codec.element codec)
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    , HasCodec c
+    , HasCodec d
+    , HasCodec e
+    ) => HasCodec (a, b, c, d, e) where
+    codec =
+        Codec.identified
+            . Codec.fromArrayCodec Permission.Forbid
+            $ (,,,,)
+            <$> Codec.project (\(a, _, _, _, _) -> a) (Codec.element codec)
+            <*> Codec.project (\(_, b, _, _, _) -> b) (Codec.element codec)
+            <*> Codec.project (\(_, _, c, _, _) -> c) (Codec.element codec)
+            <*> Codec.project (\(_, _, _, d, _) -> d) (Codec.element codec)
+            <*> Codec.project (\(_, _, _, _, e) -> e) (Codec.element codec)
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    , HasCodec c
+    , HasCodec d
+    , HasCodec e
+    , HasCodec f
+    ) => HasCodec (a, b, c, d, e, f) where
+    codec =
+        Codec.identified
+            . Codec.fromArrayCodec Permission.Forbid
+            $ (,,,,,)
+            <$> Codec.project (\(a, _, _, _, _, _) -> a) (Codec.element codec)
+            <*> Codec.project (\(_, b, _, _, _, _) -> b) (Codec.element codec)
+            <*> Codec.project (\(_, _, c, _, _, _) -> c) (Codec.element codec)
+            <*> Codec.project (\(_, _, _, d, _, _) -> d) (Codec.element codec)
+            <*> Codec.project (\(_, _, _, _, e, _) -> e) (Codec.element codec)
+            <*> Codec.project (\(_, _, _, _, _, f) -> f) (Codec.element codec)
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    , HasCodec c
+    , HasCodec d
+    , HasCodec e
+    , HasCodec f
+    , HasCodec g
+    ) => HasCodec (a, b, c, d, e, f, g) where
+    codec =
+        Codec.identified
+            . Codec.fromArrayCodec Permission.Forbid
+            $ (,,,,,,)
+            <$> Codec.project
+                    (\(a, _, _, _, _, _, _) -> a)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, b, _, _, _, _, _) -> b)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, c, _, _, _, _) -> c)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, d, _, _, _) -> d)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, _, e, _, _) -> e)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, _, _, f, _) -> f)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, _, _, _, g) -> g)
+                    (Codec.element codec)
+
+instance
+    ( HasCodec a
+    , HasCodec b
+    , HasCodec c
+    , HasCodec d
+    , HasCodec e
+    , HasCodec f
+    , HasCodec g
+    , HasCodec h
+    ) => HasCodec (a, b, c, d, e, f, g, h) where
+    codec =
+        Codec.identified
+            . Codec.fromArrayCodec Permission.Forbid
+            $ (,,,,,,,)
+            <$> Codec.project
+                    (\(a, _, _, _, _, _, _, _) -> a)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, b, _, _, _, _, _, _) -> b)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, c, _, _, _, _, _) -> c)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, d, _, _, _, _) -> d)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, _, e, _, _, _) -> e)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, _, _, f, _, _) -> f)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, _, _, _, g, _) -> g)
+                    (Codec.element codec)
+            <*> Codec.project
+                    (\(_, _, _, _, _, _, _, h) -> h)
+                    (Codec.element codec)
+
+instance HasCodec Bool where
+    codec = Codec.identified $ Codec.map Boolean.toBool Boolean.fromBool codec
+
+instance HasCodec Decimal.Decimal where
+    codec =
+        Codec.identified $ Codec.map Number.toDecimal Number.fromDecimal codec
+
+instance HasCodec Text.Text where
+    codec = Codec.identified $ Codec.map String.toText String.fromText codec
+
+instance {-# OVERLAPPABLE #-} HasCodec a => HasCodec [a] where
+    codec = Codec.identified $ Codec.map Array.toList Array.fromList codec
+
+instance HasCodec a => HasCodec (Map.Map Name.Name a) where
+    codec = Codec.identified $ Codec.map
+        (Map.fromList . fmap Member.toTuple . Object.toList)
+        (Object.fromList . fmap Member.fromTuple . Map.toList)
+        codec
+
+instance HasCodec a => HasCodec (Map.Map String.String a) where
+    codec = Codec.identified $ Codec.map
+        (Map.mapKeys Name.toString)
+        (Map.mapKeys Name.fromString)
+        codec
+
+instance HasCodec a => HasCodec (Map.Map Text.Text a) where
+    codec = Codec.identified $ Codec.map
+        (Map.mapKeys String.toText)
+        (Map.mapKeys String.fromText)
+        codec
+
+instance HasCodec a => HasCodec (Map.Map Text.LazyText a) where
+    codec = Codec.identified $ Codec.map
+        (Map.mapKeys Text.fromStrict)
+        (Map.mapKeys Text.toStrict)
+        codec
+
+instance HasCodec a => HasCodec (Map.Map String a) where
+    codec = Codec.identified
+        $ Codec.map (Map.mapKeys Text.unpack) (Map.mapKeys Text.pack) codec
+
+instance HasCodec String where
+    codec = Codec.identified $ Codec.map Text.unpack Text.pack codec
+
+instance HasCodec Char where
+    codec =
+        let schema = Schema.unidentified $ Schema.String (Just 1) (Just 1)
+        in
+            Codec.identified $ Codec.mapMaybe
+                (\x -> case Text.uncons x of
+                    Just (y, z) | Text.null z -> Just y
+                    _ -> Nothing
+                )
+                (Just . Text.singleton)
+                codec { Codec.schema = pure schema }
+
+instance HasCodec Text.LazyText where
+    codec = Codec.identified $ Codec.map Text.fromStrict Text.toStrict codec
+
+instance HasCodec a => HasCodec (NonEmpty.NonEmpty a) where
+    codec =
+        let
+            schema = do
+                itemSchema <- Codec.schema (codec :: Codec.Value a)
+                pure . Schema.unidentified $ Schema.Array
+                    (Just 1)
+                    Nothing
+                    (Left $ Schema.maybeRef itemSchema)
+                    Nothing
+        in
+            Codec.identified $ Codec.mapMaybe
+                NonEmpty.nonEmpty
+                (Just . NonEmpty.toList)
+                codec { Codec.schema = schema }
+
+instance HasCodec Integer where
+    codec =
+        let schema = Schema.unidentified $ Schema.Integer Nothing Nothing
+        in
+            Codec.identified $ Codec.mapMaybe
+                Decimal.toInteger
+                (Just . Decimal.fromInteger)
+                codec { Codec.schema = pure schema }
+
+instance HasCodec Int where
+    codec = integralCodec
+
+instance HasCodec Int.Int8 where
+    codec = integralCodec
+
+instance HasCodec Int.Int16 where
+    codec = integralCodec
+
+instance HasCodec Int.Int32 where
+    codec = integralCodec
+
+instance HasCodec Int.Int64 where
+    codec = integralCodec
+
+instance HasCodec Natural.Natural where
+    codec =
+        let
+            from = Bits.toIntegralSized :: Integer -> Maybe Natural.Natural
+            into = fromIntegral :: Natural.Natural -> Integer
+            schema = Schema.unidentified $ Schema.Integer (Just 0) Nothing
+        in Codec.identified $ Codec.mapMaybe
+            from
+            (Just . into)
+            codec { Codec.schema = pure schema }
+
+instance HasCodec Word where
+    codec = integralCodec
+
+instance HasCodec Word.Word8 where
+    codec = integralCodec
+
+instance HasCodec Word.Word16 where
+    codec = integralCodec
+
+instance HasCodec Word.Word32 where
+    codec = integralCodec
+
+instance HasCodec Word.Word64 where
+    codec = integralCodec
+
+instance HasCodec Float where
+    codec = Codec.identified $ Codec.mapMaybe
+        (Just . Decimal.toRealFloat)
+        Decimal.fromRealFloat
+        codec
+
+instance HasCodec Double where
+    codec = Codec.identified $ Codec.mapMaybe
+        (Just . Decimal.toRealFloat)
+        Decimal.fromRealFloat
+        codec
+
+instance HasCodec Pointer.Pointer where
+    codec = Codec.identified $ Codec.mapMaybe
+        (either (const Nothing) Just
+        . Decoder.run Pointer.decode
+        . Text.encodeUtf8
+        )
+        (either (const Nothing) Just
+        . Text.decodeUtf8'
+        . ByteString.toStrict
+        . Builder.toLazyByteString
+        . Encoder.run Config.initial
+        . Pointer.encode
+        )
+        codec
+
+instance HasCodec Identifier.Identifier where
+    codec =
+        let prefix = Text.pack "#/definitions/"
+        in
+            Codec.identified $ Codec.mapMaybe
+                (fmap Identifier.fromText . Text.stripPrefix prefix)
+                (Just . mappend prefix . Identifier.toText)
+                codec
+
+instance HasCodec Name.Name where
+    codec = Codec.identified $ Codec.map Name.fromString Name.toString codec
+
+instance HasCodec Schema.Schema where
+    codec =
+        let
+            trueCodec =
+                Codec.mapMaybe
+                        (const $ Just Schema.True)
+                        (\x -> case x of
+                            Schema.True -> Just ()
+                            _ -> Nothing
+                        )
+                    . Codec.literalCodec
+                    . Value.Boolean
+                    $ Boolean.fromBool True
+            falseCodec =
+                Codec.mapMaybe
+                        (const $ Just Schema.False)
+                        (\x -> case x of
+                            Schema.False -> Just ()
+                            _ -> Nothing
+                        )
+                    . Codec.literalCodec
+                    . Value.Boolean
+                    $ Boolean.fromBool False
+            constCodec =
+                Codec.mapMaybe
+                        (Just . Schema.Const)
+                        (\x -> case x of
+                            Schema.Const y -> Just y
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    $ Codec.required (name "const") codec
+            nullCodec =
+                Codec.mapMaybe
+                        (const $ Just Schema.Null)
+                        (\x -> case x of
+                            Schema.Null -> Just ()
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    . Codec.required (name "type")
+                    . Codec.literalCodec
+                    . Value.String
+                    . String.fromText
+                    $ Text.pack "null"
+            booleanCodec =
+                Codec.mapMaybe
+                        (const $ Just Schema.Boolean)
+                        (\x -> case x of
+                            Schema.Boolean -> Just ()
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    . Codec.required (name "type")
+                    . Codec.literalCodec
+                    . Value.String
+                    . String.fromText
+                    $ Text.pack "boolean"
+            numberCodec =
+                Codec.mapMaybe
+                        (const $ Just Schema.Number)
+                        (\x -> case x of
+                            Schema.Number -> Just ()
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    . Codec.required (name "type")
+                    . Codec.literalCodec
+                    . Value.String
+                    . String.fromText
+                    $ Text.pack "number"
+            integerCodec =
+                Codec.mapMaybe
+                        (\(_, y, z) -> Just $ Schema.Integer
+                            (Optional.toMaybe y)
+                            (Optional.toMaybe z)
+                        )
+                        (\x -> case x of
+                            Schema.Integer y z ->
+                                Just
+                                    ( ()
+                                    , Optional.fromMaybe y
+                                    , Optional.fromMaybe z
+                                    )
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    $ (,,)
+                    <$> Codec.project
+                            (\(a, _, _) -> a)
+                            (Codec.required (name "type")
+                            . Codec.literalCodec
+                            . Value.String
+                            . String.fromText
+                            $ Text.pack "integer"
+                            )
+                    <*> Codec.project
+                            (\(_, b, _) -> b)
+                            (Codec.optional (name "minimum") codec)
+                    <*> Codec.project
+                            (\(_, _, c) -> c)
+                            (Codec.optional (name "maximum") codec)
+            stringCodec =
+                Codec.mapMaybe
+                        (\(_, y, z) -> Just $ Schema.String
+                            (Optional.toMaybe y)
+                            (Optional.toMaybe z)
+                        )
+                        (\x -> case x of
+                            Schema.String y z ->
+                                Just
+                                    ( ()
+                                    , Optional.fromMaybe y
+                                    , Optional.fromMaybe z
+                                    )
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    $ (,,)
+                    <$> Codec.project
+                            (\(a, _, _) -> a)
+                            (Codec.required (name "type")
+                            . Codec.literalCodec
+                            . Value.String
+                            . String.fromText
+                            $ Text.pack "string"
+                            )
+                    <*> Codec.project
+                            (\(_, b, _) -> b)
+                            (Codec.optional (name "minLength") codec)
+                    <*> Codec.project
+                            (\(_, _, c) -> c)
+                            (Codec.optional (name "maxLength") codec)
+            arrayCodec =
+                Codec.mapMaybe
+                        (\(_, lo, hi, e, m) -> Just $ Schema.Array
+                            (Optional.toMaybe lo)
+                            (Optional.toMaybe hi)
+                            e
+                            (Optional.toMaybe m)
+                        )
+                        (\x -> case x of
+                            Schema.Array lo hi e m -> Just
+                                ( ()
+                                , Optional.fromMaybe lo
+                                , Optional.fromMaybe hi
+                                , e
+                                , Optional.fromMaybe m
+                                )
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    $ (,,,,)
+                    <$> Codec.project
+                            (\(a, _, _, _, _) -> a)
+                            (Codec.required (name "type")
+                            . Codec.literalCodec
+                            . Value.String
+                            . String.fromText
+                            $ Text.pack "array"
+                            )
+                    <*> Codec.project
+                            (\(_, b, _, _, _) -> b)
+                            (Codec.optional (name "minItems") codec)
+                    <*> Codec.project
+                            (\(_, _, c, _, _) -> c)
+                            (Codec.optional (name "maxItems") codec)
+                    <*> Codec.project
+                            (\(_, _, _, d, _) -> d)
+                            (Codec.required (name "items")
+                            . Codec.withIdentifier
+                                  (Identifier.fromText
+                                  $ Text.pack "OneOf Schema [Schema]"
+                                  )
+                            $ Codec.mapMaybe
+                                  (Just . Left)
+                                  (either Just $ const Nothing)
+                                  codec
+                            <|> Codec.mapMaybe
+                                    (Just . Right)
+                                    (either (const Nothing) Just)
+                                    codec
+                            )
+                    <*> Codec.project
+                            (\(_, _, _, _, e) -> e)
+                            (Codec.optional (name "additionalItems") codec)
+            objectCodec =
+                Codec.mapMaybe
+                        (\(_, ps, rs, m) -> Just $ Schema.Object
+                            (fmap Member.toTuple
+                            . foldMap Object.toList
+                            $ Optional.toMaybe ps
+                            )
+                            (Maybe.fromMaybe [] $ Optional.toMaybe rs)
+                            (Optional.toMaybe m)
+                        )
+                        (\x -> case x of
+                            Schema.Object ps rs m -> Just
+                                ( ()
+                                , if null ps
+                                    then Optional.nothing
+                                    else Optional.just . Object.fromList $ fmap
+                                        Member.fromTuple
+                                        ps
+                                , if null rs
+                                    then Optional.nothing
+                                    else Optional.just rs
+                                , Optional.fromMaybe m
+                                )
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    $ (,,,)
+                    <$> Codec.project
+                            (\(a, _, _, _) -> a)
+                            (Codec.required (name "type")
+                            . Codec.literalCodec
+                            . Value.String
+                            . String.fromText
+                            $ Text.pack "object"
+                            )
+                    <*> Codec.project
+                            (\(_, b, _, _) -> b)
+                            (Codec.optional (name "properties") codec)
+                    <*> Codec.project
+                            (\(_, _, c, _) -> c)
+                            (Codec.optional (name "required") codec)
+                    <*> Codec.project
+                            (\(_, _, _, d) -> d)
+                            (Codec.optional (name "additionalProperties") codec
+                            )
+            refCodec =
+                Codec.mapMaybe
+                        (Just . Schema.Ref)
+                        (\x -> case x of
+                            Schema.Ref y -> Just y
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    $ Codec.required (name "$ref") codec
+            oneOfCodec =
+                Codec.mapMaybe
+                        (Just . Schema.OneOf)
+                        (\x -> case x of
+                            Schema.OneOf y -> Just y
+                            _ -> Nothing
+                        )
+                    . Codec.fromObjectCodec Permission.Forbid
+                    $ Codec.required (name "oneOf") codec
+        in
+            Codec.identified
+            $ trueCodec
+            <|> falseCodec
+            <|> constCodec
+            <|> nullCodec
+            <|> booleanCodec
+            <|> numberCodec
+            <|> integerCodec
+            <|> stringCodec
+            <|> arrayCodec
+            <|> objectCodec
+            <|> refCodec
+            <|> oneOfCodec
+
+name :: String -> Name.Name
+name = Name.fromString . String.fromText . Text.pack
+
+valueCodec
+    :: forall a
+     . Typeable.Typeable a
+    => Schema.Schema
+    -> (a -> Value.Value)
+    -> (Value.Value -> Maybe a)
+    -> Codec.Value a
+valueCodec schema toValue fromValue = Codec.identified Codec.Codec
+    { Codec.decode = castValue
+        (Codec.typeName (Typeable.Proxy :: Typeable.Proxy a))
+        fromValue
+    , Codec.encode = Codec.tap $ Trans.lift . Trans.put . toValue
+    , Codec.schema = pure $ Schema.unidentified schema
+    }
+
+castValue
+    :: String
+    -> (Value.Value -> Maybe a)
+    -> Trans.ReaderT
+           Value.Value
+           (Trans.ExceptT String Identity.Identity)
+           a
+castValue expected fromValue = do
+    value <- Trans.ask
+    case fromValue value of
+        Nothing -> Trans.lift . Trans.throwE $ typeMismatch expected value
+        Just x -> pure x
+
+typeMismatch :: String -> Value.Value -> String
+typeMismatch expected value =
+    let
+        actual = case value of
+            Value.Null _ -> "Null"
+            Value.Boolean _ -> "Boolean"
+            Value.Number _ -> "Number"
+            Value.String _ -> "String"
+            Value.Array _ -> "Array"
+            Value.Object _ -> "Object"
+    in "expected " <> expected <> " but got " <> actual
+
+integralCodec
+    :: forall a
+     . (Bits.Bits a, Bounded a, Integral a, Typeable.Typeable a)
+    => Codec.Value a
+integralCodec =
+    let
+        from = Bits.toIntegralSized :: Integer -> Maybe a
+        into = fromIntegral :: a -> Integer
+        schema = Schema.unidentified $ Schema.Integer
+            (Just $ toInteger (minBound :: a))
+            (Just $ toInteger (maxBound :: a))
+    in Codec.identified $ Codec.mapMaybe
+        from
+        (Just . into)
+        codec { Codec.schema = pure schema }
+
+optionalNullable
+    :: Typeable.Typeable a
+    => Name.Name
+    -> Codec.Value a
+    -> Codec.Object (Maybe a)
+optionalNullable k =
+    Codec.map
+            (maybe Nothing Nullable.toMaybe . Optional.toMaybe)
+            (Optional.fromMaybe . fmap Nullable.just)
+        . Codec.optional k
+        . nullable
+
+nullable
+    :: Typeable.Typeable a
+    => Codec.Value a
+    -> Codec.Value (Nullable.Nullable a)
+nullable c =
+    Codec.identified
+        $ Codec.mapMaybe (Just . Nullable.just) Nullable.toMaybe c
+        <|> Codec.map (const Nullable.nothing) (const $ Null.fromUnit ()) codec
diff --git a/source/library/Argo/Internal/Codec/Array.hs b/source/library/Argo/Internal/Codec/Array.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Codec/Array.hs
@@ -0,0 +1,64 @@
+module Argo.Internal.Codec.Array where
+
+import qualified Argo.Internal.Codec.Codec as Codec
+import qualified Argo.Internal.Codec.List as Codec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Json.Array as Array
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Internal.Schema.Schema as Schema
+import qualified Argo.Internal.Type.Permission as Permission
+import qualified Argo.Vendor.Map as Map
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Data.Functor.Identity as Identity
+import qualified Data.List.NonEmpty as NonEmpty
+
+type Array a = Element a a
+
+type Element a b
+    = Codec.Item
+          ( Trans.AccumT
+                (Map.Map Identifier.Identifier Schema.Schema)
+                Identity.Identity
+                [(Maybe Identifier.Identifier, Schema.Schema)]
+          )
+          Value.Value
+          a
+          b
+
+fromArrayCodec :: Permission.Permission -> Array a -> Codec.Value a
+fromArrayCodec =
+    Codec.fromListCodec
+            (\permission schemasM -> do
+                schemas <- schemasM
+                pure . Schema.unidentified $ Schema.Array
+                    (Just . fromIntegral $ length schemas)
+                    (case permission of
+                        Permission.Allow -> Nothing
+                        Permission.Forbid ->
+                            Just . fromIntegral $ length schemas
+                    )
+                    (case NonEmpty.nonEmpty $ fmap Schema.maybeRef schemas of
+                        Nothing -> Left Schema.False
+                        Just xs -> Right xs
+                    )
+                    Nothing
+            )
+        $ Codec.map Array.toList Array.fromList Codec.arrayCodec
+
+element :: Codec.Value a -> Array a
+element c = Codec.Codec
+    { Codec.decode = do
+        l <- Trans.get
+        case l of
+            [] -> Trans.lift $ Trans.throwE "unexpected empty list"
+            h : t -> case Codec.decodeWith c h of
+                Left y -> Trans.lift $ Trans.throwE y
+                Right y -> do
+                    Trans.put t
+                    pure y
+    , Codec.encode = \x -> do
+        Trans.tell [Codec.encodeWith c x]
+        pure x
+    , Codec.schema = pure . Schema.unidentified <$> Codec.getRef c
+    }
diff --git a/source/library/Argo/Internal/Codec/Codec.hs b/source/library/Argo/Internal/Codec/Codec.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Codec/Codec.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE FlexibleInstances #-}
+
+module Argo.Internal.Codec.Codec where
+
+import Control.Applicative ((<|>))
+
+import qualified Control.Applicative as Applicative
+
+data Codec r w s i o = Codec
+    { decode :: r o
+    , encode :: i -> w o
+    , schema :: s
+    }
+
+instance (Functor r, Functor w) => Functor (Codec r w s i) where
+    fmap f c = Codec
+        { decode = f <$> decode c
+        , encode = fmap f . encode c
+        , schema = schema c
+        }
+
+instance
+    ( Applicative r
+    , Applicative w
+    , Applicative s
+    , Monoid a
+    ) => Applicative (Codec r w (s a) i) where
+    pure x = Codec
+        { decode = pure x
+        , encode = const $ pure x
+        , schema = pure mempty
+        }
+    cf <*> cx = Codec
+        { decode = decode cf <*> decode cx
+        , encode = \i -> encode cf i <*> encode cx i
+        , schema = (<>) <$> schema cf <*> schema cx
+        }
+
+instance
+    ( Applicative.Alternative r
+    , Applicative.Alternative w
+    , Applicative s
+    , Monoid a
+    ) => Applicative.Alternative (Codec r w (s a) i) where
+    empty = Codec
+        { decode = Applicative.empty
+        , encode = const Applicative.empty
+        , schema = pure mempty
+        }
+    cx <|> cy = Codec
+        { decode = decode cx <|> decode cy
+        , encode = \i -> encode cx i <|> encode cy i
+        , schema = (<>) <$> schema cx <*> schema cy
+        }
+
+map
+    :: (Functor r, Functor w)
+    => (a -> b)
+    -> (b -> a)
+    -> Codec r w s a a
+    -> Codec r w s b b
+map f g c = Codec
+    { decode = f <$> decode c
+    , encode = fmap f . encode c . g
+    , schema = schema c
+    }
+
+mapMaybe
+    :: (Applicative.Alternative r, Applicative.Alternative w, Monad r, Monad w)
+    => (o2 -> Maybe o1)
+    -> (i1 -> Maybe i2)
+    -> Codec r w s i2 o2
+    -> Codec r w s i1 o1
+mapMaybe f g c = Codec
+    { decode = do
+        o2 <- decode c
+        toAlternative $ f o2
+    , encode = \i1 -> do
+        i2 <- toAlternative $ g i1
+        o2 <- encode c i2
+        toAlternative $ f o2
+    , schema = schema c
+    }
+
+project :: (i -> f) -> Codec r w s f o -> Codec r w s i o
+project f c = c { encode = encode c . f }
+
+tap :: Functor f => (a -> f b) -> a -> f a
+tap f x = x <$ f x
+
+toAlternative :: Applicative.Alternative m => Maybe a -> m a
+toAlternative = maybe Applicative.empty pure
diff --git a/source/library/Argo/Internal/Codec/List.hs b/source/library/Argo/Internal/Codec/List.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Codec/List.hs
@@ -0,0 +1,59 @@
+module Argo.Internal.Codec.List where
+
+import qualified Argo.Internal.Codec.Codec as Codec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Internal.Schema.Schema as Schema
+import qualified Argo.Internal.Type.Permission as Permission
+import qualified Argo.Vendor.Map as Map
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+import qualified Data.Functor.Identity as Identity
+
+type List s e a = Item s e a a
+
+type Item s e a b
+    = Codec.Codec
+          (Trans.StateT [e] (Trans.ExceptT String Identity.Identity))
+          (Trans.WriterT [e] Identity.Identity)
+          s
+          a
+          b
+
+fromListCodec
+    :: ( Permission.Permission
+       -> s
+       -> Trans.AccumT
+              (Map.Map Identifier.Identifier Schema.Schema)
+              Identity.Identity
+              (Maybe Identifier.Identifier, Schema.Schema)
+       )
+    -> Codec.Value [e]
+    -> Permission.Permission
+    -> List s e a
+    -> Codec.Value a
+fromListCodec f ce p ca = Codec.Codec
+    { Codec.decode = do
+        xs <- Codec.decode ce
+        case
+                Identity.runIdentity . Trans.runExceptT $ Trans.runStateT
+                    (Codec.decode ca)
+                    xs
+            of
+                Left x -> Trans.lift $ Trans.throwE x
+                Right (x, ys) -> do
+                    case (p, ys) of
+                        (Permission.Forbid, _ : _) ->
+                            Trans.lift $ Trans.throwE "leftover elements"
+                        _ -> pure ()
+                    pure x
+    , Codec.encode = \x -> do
+        Monad.void
+            . Codec.encode ce
+            . snd
+            . Identity.runIdentity
+            . Trans.runWriterT
+            $ Codec.encode ca x
+        pure x
+    , Codec.schema = f p $ Codec.schema ca
+    }
diff --git a/source/library/Argo/Internal/Codec/Object.hs b/source/library/Argo/Internal/Codec/Object.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Codec/Object.hs
@@ -0,0 +1,119 @@
+module Argo.Internal.Codec.Object where
+
+import qualified Argo.Internal.Codec.Codec as Codec
+import qualified Argo.Internal.Codec.List as Codec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Json.Member as Member
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Json.Object as Object
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Internal.Schema.Schema as Schema
+import qualified Argo.Internal.Type.Optional as Optional
+import qualified Argo.Internal.Type.Permission as Permission
+import qualified Argo.Vendor.Map as Map
+import qualified Argo.Vendor.Text as Text
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+import qualified Data.Functor.Identity as Identity
+import qualified Data.List as List
+import qualified Data.Maybe as Maybe
+
+type Object a = Member a a
+
+type Member a b
+    = Codec.Item
+          ( Trans.AccumT
+                (Map.Map Identifier.Identifier Schema.Schema)
+                Identity.Identity
+                [ ( (Name.Name, Bool)
+                  , (Maybe Identifier.Identifier, Schema.Schema)
+                  )
+                ]
+          )
+          (Member.Member Value.Value)
+          a
+          b
+
+fromObjectCodec :: Permission.Permission -> Object a -> Codec.Value a
+fromObjectCodec =
+    Codec.fromListCodec
+            (\permission schemasM -> do
+                schemas <- schemasM
+                pure
+                    . Schema.unidentified
+                    . Schema.Object
+                          (fmap
+                              (\((k, _), s) -> (k, Schema.maybeRef s))
+                              schemas
+                          )
+                          (Maybe.mapMaybe
+                              (\((k, r), _) -> if r then Just k else Nothing)
+                              schemas
+                          )
+                    $ case permission of
+                          Permission.Allow -> Nothing
+                          Permission.Forbid -> Just Schema.False
+            )
+        $ Codec.map Object.toList Object.fromList Codec.objectCodec
+
+required :: Name.Name -> Codec.Value a -> Object a
+required k c = Codec.Codec
+    { Codec.decode = do
+        m <- Codec.decode (optional k c)
+        case Optional.toMaybe m of
+            Nothing ->
+                Trans.lift
+                    . Trans.throwE
+                    $ "missing required member: "
+                    <> show k
+            Just x -> pure x
+    , Codec.encode = \x -> do
+        Monad.void . Codec.encode (optional k c) $ Optional.just x
+        pure x
+    , Codec.schema =
+        pure . (,) (k, True) . Schema.unidentified <$> Codec.getRef c
+    }
+
+optional :: Name.Name -> Codec.Value a -> Object (Optional.Optional a)
+optional k c = Codec.Codec
+    { Codec.decode = do
+        xs <- Trans.get
+        case List.partition (\(Member.Member j _) -> j == k) xs of
+            (Member.Member _ x : _, ys) -> case Codec.decodeWith c x of
+                Left y -> Trans.lift $ Trans.throwE y
+                Right y -> do
+                    Trans.put ys
+                    pure $ Optional.just y
+            _ -> pure Optional.nothing
+    , Codec.encode = \x -> do
+        case Optional.toMaybe x of
+            Nothing -> pure ()
+            Just y -> Trans.tell [Member.Member k $ Codec.encodeWith c y]
+        pure x
+    , Codec.schema =
+        pure . (,) (k, False) . Schema.unidentified <$> Codec.getRef c
+    }
+
+tagged :: String -> Codec.Value a -> Codec.Value a
+tagged t c =
+    Codec.map snd ((,) ())
+        . fromObjectCodec Permission.Forbid
+        $ (,)
+        <$> Codec.project
+                fst
+                (required
+                    (Name.fromString . String.fromText $ Text.pack "type")
+                    (Codec.literalCodec
+                    . Value.String
+                    . String.fromText
+                    $ Text.pack t
+                    )
+                )
+        <*> Codec.project
+                snd
+                (required
+                    (Name.fromString . String.fromText $ Text.pack "value")
+                    c
+                )
diff --git a/source/library/Argo/Internal/Codec/Value.hs b/source/library/Argo/Internal/Codec/Value.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Codec/Value.hs
@@ -0,0 +1,124 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Argo.Internal.Codec.Value where
+
+import qualified Argo.Internal.Codec.Codec as Codec
+import qualified Argo.Internal.Json.Array as Array
+import qualified Argo.Internal.Json.Null as Null
+import qualified Argo.Internal.Json.Object as Object
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Internal.Schema.Schema as Schema
+import qualified Argo.Vendor.Map as Map
+import qualified Argo.Vendor.Text as Text
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+import qualified Data.Functor.Identity as Identity
+import qualified Data.Typeable as Typeable
+
+decodeWith :: Value a -> Value.Value -> Either String a
+decodeWith c =
+    Identity.runIdentity . Trans.runExceptT . Trans.runReaderT (Codec.decode c)
+
+encodeWith :: Value a -> a -> Value.Value
+encodeWith c x =
+    snd
+        . Identity.runIdentity
+        . Trans.runStateT (Trans.runMaybeT $ Codec.encode c x)
+        . Value.Null
+        $ Null.fromUnit ()
+
+type Value a
+    = Codec.Codec
+          (Trans.ReaderT Value.Value (Trans.ExceptT String Identity.Identity))
+          (Trans.MaybeT (Trans.StateT Value.Value Identity.Identity))
+          ( Trans.AccumT
+                (Map.Map Identifier.Identifier Schema.Schema)
+                Identity.Identity
+                (Maybe Identifier.Identifier, Schema.Schema)
+          )
+          a
+          a
+
+arrayCodec :: Value (Array.Array Value.Value)
+arrayCodec = Codec.Codec
+    { Codec.decode = do
+        x <- Trans.ask
+        case x of
+            Value.Array y -> pure y
+            _ ->
+                Trans.lift . Trans.throwE $ "expected Array but got " <> show x
+    , Codec.encode = \x -> do
+        Trans.lift . Trans.put $ Value.Array x
+        pure x
+    , Codec.schema = pure $ Schema.unidentified Schema.false
+    }
+
+objectCodec :: Value (Object.Object Value.Value)
+objectCodec = Codec.Codec
+    { Codec.decode = do
+        x <- Trans.ask
+        case x of
+            Value.Object y -> pure y
+            _ ->
+                Trans.lift
+                    . Trans.throwE
+                    $ "expected Object but got "
+                    <> show x
+    , Codec.encode = \x -> do
+        Trans.lift . Trans.put $ Value.Object x
+        pure x
+    , Codec.schema = pure $ Schema.unidentified Schema.false
+    }
+
+literalCodec :: Value.Value -> Value ()
+literalCodec expected = Codec.Codec
+    { Codec.decode = do
+        actual <- Trans.ask
+        Monad.when (actual /= expected)
+            . Trans.lift
+            . Trans.throwE
+            $ "expected "
+            <> show expected
+            <> " but got "
+            <> show actual
+    , Codec.encode = const . Trans.lift $ Trans.put expected
+    , Codec.schema = pure . Schema.unidentified $ Schema.Const expected
+    }
+
+identified :: forall a . Typeable.Typeable a => Value a -> Value a
+identified = withIdentifier . Identifier.fromText . Text.pack $ typeName
+    (Typeable.Proxy :: Typeable.Proxy a)
+
+withIdentifier :: Identifier.Identifier -> Value a -> Value a
+withIdentifier identifier codec =
+    let
+        newSchema = do
+            (_, schema) <- Codec.schema codec
+            Trans.add $ Map.singleton identifier schema
+            pure $ Schema.withIdentifier identifier schema
+    in codec { Codec.schema = newSchema }
+
+typeName :: Typeable.Typeable a => Typeable.Proxy a -> String
+typeName = show . Typeable.typeRep
+
+getRef
+    :: Value a
+    -> Trans.AccumT
+           (Map.Map Identifier.Identifier Schema.Schema)
+           Identity.Identity
+           Schema.Schema
+getRef codec = do
+    let
+        (maybeIdentifier, schema) =
+            fst . Identity.runIdentity $ Trans.runAccumT
+                (Codec.schema codec)
+                Map.empty
+    case maybeIdentifier of
+        Nothing -> pure schema
+        Just identifier -> do
+            schemas <- Trans.look
+            Monad.unless (Map.member identifier schemas) $ do
+                Trans.add $ Map.singleton identifier schema
+                Monad.void $ Codec.schema codec
+            pure $ Schema.Ref identifier
diff --git a/source/library/Argo/Internal/Decode.hs b/source/library/Argo/Internal/Decode.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Decode.hs
@@ -0,0 +1,19 @@
+module Argo.Internal.Decode where
+
+import qualified Argo.Internal.Class.HasCodec as HasCodec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Pointer.Pointer as Pointer
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Vendor.ByteString as ByteString
+
+decode :: HasCodec.HasCodec a => ByteString.ByteString -> Either String a
+decode x = do
+    y <- Decoder.run (Decoder.spaces *> Value.decode) x
+    fromValue y
+
+decodePointer :: ByteString.ByteString -> Either String Pointer.Pointer
+decodePointer = Decoder.run Pointer.decode
+
+fromValue :: HasCodec.HasCodec a => Value.Value -> Either String a
+fromValue = Codec.decodeWith HasCodec.codec
diff --git a/source/library/Argo/Internal/Encode.hs b/source/library/Argo/Internal/Encode.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Encode.hs
@@ -0,0 +1,33 @@
+module Argo.Internal.Encode where
+
+import qualified Argo.Internal.Class.HasCodec as HasCodec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Pointer.Pointer as Pointer
+import qualified Argo.Internal.Type.Config as Config
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Internal.Type.Indent as Indent
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+
+encode :: HasCodec.HasCodec a => a -> Builder.Builder
+encode = encodeWith $ Indent.Spaces 0
+
+encodeWith :: HasCodec.HasCodec a => Indent.Indent -> a -> Builder.Builder
+encodeWith i x =
+    let c = Config.initial { Config.indent = i }
+    in
+        Encoder.run c $ do
+            Value.encode $ toValue x
+            Monad.when (Config.hasIndent c)
+                . Trans.lift
+                . Trans.tell
+                $ Builder.word8 Literal.newLine
+
+encodePointer :: Pointer.Pointer -> Builder.Builder
+encodePointer = Encoder.run Config.initial . Pointer.encode
+
+toValue :: HasCodec.HasCodec a => a -> Value.Value
+toValue = Codec.encodeWith HasCodec.codec
diff --git a/source/library/Argo/Internal/Json/Array.hs b/source/library/Argo/Internal/Json/Array.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Array.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Array where
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Transformers as Trans
+import qualified GHC.Generics as Generics
+
+newtype Array value
+    = Array [value]
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromList :: [value] -> Array value
+fromList = Array
+
+toList :: Array value -> [value]
+toList (Array x) = x
+
+encode :: (value -> Encoder.Encoder ()) -> Array value -> Encoder.Encoder ()
+encode f =
+    Encoder.list
+            (Trans.lift . Trans.tell $ Builder.word8 Literal.leftSquareBracket)
+            (Trans.lift . Trans.tell $ Builder.word8 Literal.rightSquareBracket
+            )
+            (Trans.lift . Trans.tell $ Builder.word8 Literal.comma)
+            f
+        . toList
+
+decode :: Decoder.Decoder value -> Decoder.Decoder (Array value)
+decode f = do
+    Decoder.word8 Literal.leftSquareBracket
+    Decoder.spaces
+    xs <- Decoder.list f
+    Decoder.word8 Literal.rightSquareBracket
+    Decoder.spaces
+    pure $ fromList xs
diff --git a/source/library/Argo/Internal/Json/Boolean.hs b/source/library/Argo/Internal/Json/Boolean.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Boolean.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Boolean where
+
+import Control.Applicative ((<|>))
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Transformers as Trans
+import qualified GHC.Generics as Generics
+
+newtype Boolean
+    = Boolean Bool
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromBool :: Bool -> Boolean
+fromBool = Boolean
+
+toBool :: Boolean -> Bool
+toBool (Boolean x) = x
+
+encode :: Boolean -> Encoder.Encoder ()
+encode x = Trans.lift . Trans.tell . Builder.byteString $ if toBool x
+    then Literal.true
+    else Literal.false
+
+decode :: Decoder.Decoder Boolean
+decode = decodeFalse <|> decodeTrue
+
+decodeFalse :: Decoder.Decoder Boolean
+decodeFalse =
+    fromBool False <$ Decoder.byteString Literal.false <* Decoder.spaces
+
+decodeTrue :: Decoder.Decoder Boolean
+decodeTrue =
+    fromBool True <$ Decoder.byteString Literal.true <* Decoder.spaces
diff --git a/source/library/Argo/Internal/Json/Member.hs b/source/library/Argo/Internal/Json/Member.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Member.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Member where
+
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Config as Config
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+import qualified GHC.Generics as Generics
+
+data Member value = Member Name.Name value
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromTuple :: (Name.Name, value) -> Member value
+fromTuple = uncurry Member
+
+toTuple :: Member value -> (Name.Name, value)
+toTuple (Member k v) = (k, v)
+
+encode :: (value -> Encoder.Encoder ()) -> Member value -> Encoder.Encoder ()
+encode f (Member x y) = do
+    Name.encode x
+    Trans.lift . Trans.tell $ Builder.word8 Literal.colon
+    config <- Trans.ask
+    Monad.when (Config.hasIndent config)
+        . Trans.lift
+        . Trans.tell
+        $ Builder.word8 Literal.space
+    f y
+
+decode :: Decoder.Decoder value -> Decoder.Decoder (Member value)
+decode g =
+    Member
+        <$> Name.decode
+        <* Decoder.word8 Literal.colon
+        <* Decoder.spaces
+        <*> g
diff --git a/source/library/Argo/Internal/Json/Name.hs b/source/library/Argo/Internal/Json/Name.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Name.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Name where
+
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Data.String
+import qualified GHC.Generics as Generics
+
+newtype Name
+    = Name String.String
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Ord, Show)
+
+instance Data.String.IsString Name where
+    fromString = fromString . Data.String.fromString
+
+fromString :: String.String -> Name
+fromString = Name
+
+toString :: Name -> String.String
+toString (Name x) = x
+
+encode :: Name -> Encoder.Encoder ()
+encode = String.encode . toString
+
+decode :: Decoder.Decoder Name
+decode = fromString <$> String.decode
diff --git a/source/library/Argo/Internal/Json/Null.hs b/source/library/Argo/Internal/Json/Null.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Null.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Null where
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Transformers as Trans
+import qualified GHC.Generics as Generics
+
+newtype Null
+    = Null ()
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromUnit :: () -> Null
+fromUnit = Null
+
+toUnit :: Null -> ()
+toUnit (Null x) = x
+
+encode :: Null -> Encoder.Encoder ()
+encode = const . Trans.lift . Trans.tell $ Builder.byteString Literal.null
+
+decode :: Decoder.Decoder Null
+decode = fromUnit () <$ Decoder.byteString Literal.null <* Decoder.spaces
diff --git a/source/library/Argo/Internal/Json/Number.hs b/source/library/Argo/Internal/Json/Number.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Number.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Number where
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Decimal as Decimal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.ByteString as ByteString
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Applicative as Applicative
+import qualified Control.Monad as Monad
+import qualified Data.Bool as Bool
+import qualified Data.Maybe as Maybe
+import qualified Data.Word as Word
+import qualified GHC.Generics as Generics
+
+newtype Number
+    = Number Decimal.Decimal
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromDecimal :: Decimal.Decimal -> Number
+fromDecimal = Number
+
+toDecimal :: Number -> Decimal.Decimal
+toDecimal (Number x) = x
+
+encode :: Number -> Encoder.Encoder ()
+encode x = do
+    let Decimal.Decimal s e = toDecimal x
+    Trans.lift . Trans.tell $ Builder.integerDec s
+    Monad.when (e /= 0)
+        . Trans.lift
+        . Trans.tell
+        $ Builder.word8 Literal.latinSmallLetterE
+        <> Builder.integerDec e
+
+decode :: Decoder.Decoder Number
+decode = do
+    ni <- fmap Maybe.isJust . Applicative.optional $ Decoder.word8
+        Literal.hyphenMinus
+    i <- Decoder.takeWhile1 Decoder.isDigit
+    Monad.when
+            (ByteString.length i
+            > 1
+            && ByteString.elemIndex Literal.digitZero i
+            == Just 0
+            )
+        . Trans.lift
+        $ Trans.throwE "leading zero"
+    f <- fmap (Maybe.fromMaybe ByteString.empty) . Applicative.optional $ do
+        Decoder.word8 Literal.fullStop
+        Decoder.takeWhile1 Decoder.isDigit
+    (ne, e) <-
+        fmap (Maybe.fromMaybe (False, ByteString.empty))
+        . Applicative.optional
+        $ do
+              Monad.void
+                  . Decoder.satisfy
+                  $ \x ->
+                        (x == Literal.latinSmallLetterE)
+                            || (x == Literal.latinCapitalLetterE)
+              ne <-
+                  fmap (== Just Literal.hyphenMinus)
+                  . Applicative.optional
+                  . Decoder.satisfy
+                  $ \x -> x == Literal.hyphenMinus || x == Literal.plusSign
+              e <- Decoder.takeWhile1 Decoder.isDigit
+              pure (ne, e)
+    Decoder.spaces
+    pure . fromDecimal $ Decimal.decimal
+        (negateIf ni $ (fromDigits i * 10 ^ ByteString.length f) + fromDigits f
+        )
+        (negateIf ne (fromDigits e) - intToInteger (ByteString.length f))
+
+negateIf :: Bool -> Integer -> Integer
+negateIf = Bool.bool id negate
+
+fromDigits :: ByteString.ByteString -> Integer
+fromDigits =
+    ByteString.foldl' (\a e -> (a * 10) + word8ToInteger (e - 0x30)) 0
+
+intToInteger :: Int -> Integer
+intToInteger = fromIntegral
+
+word8ToInteger :: Word.Word8 -> Integer
+word8ToInteger = fromIntegral
diff --git a/source/library/Argo/Internal/Json/Object.hs b/source/library/Argo/Internal/Json/Object.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Object.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Object where
+
+import qualified Argo.Internal.Json.Member as Member
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+import qualified GHC.Generics as Generics
+
+newtype Object value
+    = Object [Member.Member value]
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromList :: [Member.Member value] -> Object value
+fromList = Object
+
+toList :: Object value -> [Member.Member value]
+toList (Object x) = x
+
+encode :: (value -> Encoder.Encoder ()) -> Object value -> Encoder.Encoder ()
+encode f =
+    Encoder.list
+            (Trans.lift . Trans.tell $ Builder.word8 Literal.leftCurlyBracket)
+            (Trans.lift . Trans.tell $ Builder.word8 Literal.rightCurlyBracket)
+            (Trans.lift . Trans.tell $ Builder.word8 Literal.comma)
+            (Member.encode f)
+        . toList
+
+encodeElement
+    :: (value -> Encoder.Encoder ())
+    -> Int
+    -> Member.Member value
+    -> Encoder.Encoder ()
+encodeElement f i x = do
+    Monad.when (i > 0) . Trans.lift . Trans.tell $ Builder.word8 Literal.comma
+    Member.encode f x
+
+decode :: Decoder.Decoder value -> Decoder.Decoder (Object value)
+decode f = do
+    Decoder.word8 Literal.leftCurlyBracket
+    Decoder.spaces
+    xs <- Decoder.list $ Member.decode f
+    Decoder.word8 Literal.rightCurlyBracket
+    Decoder.spaces
+    pure $ fromList xs
diff --git a/source/library/Argo/Internal/Json/String.hs b/source/library/Argo/Internal/Json/String.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/String.hs
@@ -0,0 +1,179 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.String where
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.ByteString as ByteString
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Text as Text
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+import qualified Data.Char as Char
+import qualified Data.String as String
+import qualified Data.Word as Word
+import qualified GHC.Generics as Generics
+
+newtype String
+    = String Text.Text
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Ord, Show)
+
+instance String.IsString Argo.Internal.Json.String.String where
+    fromString = fromText . String.fromString
+
+fromText :: Text.Text -> Argo.Internal.Json.String.String
+fromText = String
+
+toText :: Argo.Internal.Json.String.String -> Text.Text
+toText (String x) = x
+
+encode :: Argo.Internal.Json.String.String -> Encoder.Encoder ()
+encode x =
+    Trans.lift
+        . Trans.tell
+        $ Builder.word8 Literal.quotationMark
+        <> Text.encodeUtf8BuilderEscaped encodeChar (toText x)
+        <> Builder.word8 Literal.quotationMark
+
+encodeChar :: Builder.BoundedPrim Word.Word8
+encodeChar =
+    Builder.condB
+            (== Literal.quotationMark)
+            (encodeShortEscape Literal.quotationMark)
+        . Builder.condB
+              (== Literal.reverseSolidus)
+              (encodeShortEscape Literal.reverseSolidus)
+        . Builder.condB
+              (== Literal.backspace)
+              (encodeShortEscape Literal.latinSmallLetterB)
+        . Builder.condB
+              (== Literal.formFeed)
+              (encodeShortEscape Literal.latinSmallLetterF)
+        . Builder.condB
+              (== Literal.newLine)
+              (encodeShortEscape Literal.latinSmallLetterN)
+        . Builder.condB
+              (== Literal.carriageReturn)
+              (encodeShortEscape Literal.latinSmallLetterR)
+        . Builder.condB
+              (== Literal.horizontalTabulation)
+              (encodeShortEscape Literal.latinSmallLetterT)
+        . Builder.condB (< Literal.space) encodeLongEscape
+        $ Builder.liftFixedToBounded Builder.word8F
+
+encodeShortEscape :: Word.Word8 -> Builder.BoundedPrim a
+encodeShortEscape x =
+    Builder.liftFixedToBounded
+        $ const (Literal.reverseSolidus, x)
+        Builder.>$< Builder.word8F
+        Builder.>*< Builder.word8F
+
+encodeLongEscape :: Builder.BoundedPrim Word.Word8
+encodeLongEscape =
+    Builder.liftFixedToBounded
+        $ (\x ->
+              ( Literal.reverseSolidus
+              , (Literal.latinSmallLetterU, word8ToWord16 x)
+              )
+          )
+        Builder.>$< Builder.word8F
+        Builder.>*< Builder.word8F
+        Builder.>*< Builder.word16HexFixed
+
+word8ToWord16 :: Word.Word8 -> Word.Word16
+word8ToWord16 = fromIntegral
+
+decode :: Decoder.Decoder Argo.Internal.Json.String.String
+decode = do
+    Decoder.word8 Literal.quotationMark
+    b1 <- Trans.get
+    i <- case getClose b1 0 of
+        Nothing -> Trans.lift $ Trans.throwE "unterminated string"
+        Just i -> pure i
+    let (xs, b2) = ByteString.splitAt i b1
+    Monad.when (ByteString.any (< Literal.space) xs)
+        . Trans.lift
+        $ Trans.throwE "unescaped control character"
+    Trans.put b2
+    Decoder.word8 Literal.quotationMark
+    Decoder.spaces
+    case Text.decodeUtf8' xs of
+        Left e -> Trans.lift . Trans.throwE $ show e
+        Right x -> case unescapeText x of
+            Left e -> Trans.lift $ Trans.throwE e
+            Right y -> pure $ fromText y
+
+findAt :: Word.Word8 -> Int -> ByteString.ByteString -> Maybe Int
+findAt x i = fmap (+ i) . ByteString.elemIndex x . ByteString.drop i
+
+countConsecutive :: Word.Word8 -> Int -> ByteString.ByteString -> Int
+countConsecutive x i =
+    ByteString.length . ByteString.takeWhileEnd (== x) . ByteString.take i
+
+getClose :: ByteString.ByteString -> Int -> Maybe Int
+getClose b i = do
+    j <- findAt Literal.quotationMark i b
+    let n = countConsecutive Literal.reverseSolidus j b
+    if even n then Just j else getClose b $ j + 1
+
+unescapeText :: Text.Text -> Either Prelude.String Text.Text
+unescapeText =
+    fmap (Text.pack . combineSurrogatePairs) . unescapeString . Text.unpack
+
+combineSurrogatePairs :: Prelude.String -> Prelude.String
+combineSurrogatePairs xs = case xs of
+    "" -> xs
+    x : y : zs | isHighSurrogate x && isLowSurrogate y ->
+        combineSurrogatePair x y : combineSurrogatePairs zs
+    x : ys -> x : combineSurrogatePairs ys
+
+combineSurrogatePair :: Char -> Char -> Char
+combineSurrogatePair hi lo =
+    Char.chr
+        $ 0x10000
+        + ((Char.ord hi - 0xd800) * 0x400)
+        + (Char.ord lo - 0xdc00)
+
+isHighSurrogate :: Char -> Bool
+isHighSurrogate x = '\xd800' <= x && x <= '\xdbff'
+
+isLowSurrogate :: Char -> Bool
+isLowSurrogate x = '\xdc00' <= x && x <= '\xdfff'
+
+unescapeString :: Prelude.String -> Either Prelude.String Prelude.String
+unescapeString xs = case xs of
+    "" -> pure xs
+    '\\' : ys -> case ys of
+        "" -> Left "empty escape"
+        x : zs -> case x of
+            '"' -> ('"' :) <$> unescapeString zs
+            '\\' -> ('\\' :) <$> unescapeString zs
+            '/' -> ('/' :) <$> unescapeString zs
+            'b' -> ('\b' :) <$> unescapeString zs
+            'f' -> ('\f' :) <$> unescapeString zs
+            'n' -> ('\n' :) <$> unescapeString zs
+            'r' -> ('\r' :) <$> unescapeString zs
+            't' -> ('\t' :) <$> unescapeString zs
+            'u' -> case zs of
+                a : b : c : d : es | Just y <- fromLongEscape a b c d ->
+                    (y :) <$> unescapeString es
+                _ -> Left "invalid long escape"
+            _ -> Left "invalid short escape"
+    x : ys -> (x :) <$> unescapeString ys
+
+fromLongEscape :: Char -> Char -> Char -> Char -> Maybe Char
+fromLongEscape a b c d = do
+    w <- fromHexadecimalDigit a
+    x <- fromHexadecimalDigit b
+    y <- fromHexadecimalDigit c
+    z <- fromHexadecimalDigit d
+    pure . Char.chr $ (0x1000 * w) + (0x100 * x) + (0x10 * y) + z
+
+fromHexadecimalDigit :: Char -> Maybe Int
+fromHexadecimalDigit x =
+    if Char.isHexDigit x then Just $ Char.digitToInt x else Nothing
diff --git a/source/library/Argo/Internal/Json/Value.hs b/source/library/Argo/Internal/Json/Value.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Json/Value.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Json.Value where
+
+import Control.Applicative ((<|>))
+
+import qualified Argo.Internal.Json.Array as Array
+import qualified Argo.Internal.Json.Boolean as Boolean
+import qualified Argo.Internal.Json.Null as Null
+import qualified Argo.Internal.Json.Number as Number
+import qualified Argo.Internal.Json.Object as Object
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Data.String
+import qualified GHC.Generics as Generics
+
+-- | A JSON (JavaScript Object Notation) value, as described by RFC 8259.
+-- <https://datatracker.ietf.org/doc/html/rfc8259>
+data Value
+    = Null Null.Null
+    | Boolean Boolean.Boolean
+    | Number Number.Number
+    | String String.String
+    | Array (Array.Array Value)
+    | Object (Object.Object Value)
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+instance Data.String.IsString Value where
+    fromString = String . Data.String.fromString
+
+encode :: Value -> Encoder.Encoder ()
+encode x = case x of
+    Null y -> Null.encode y
+    Boolean y -> Boolean.encode y
+    Number y -> Number.encode y
+    String y -> String.encode y
+    Array y -> Array.encode encode y
+    Object y -> Object.encode encode y
+
+decode :: Decoder.Decoder Value
+decode =
+    (Null <$> Null.decode)
+        <|> (Boolean <$> Boolean.decode)
+        <|> (Number <$> Number.decode)
+        <|> (String <$> String.decode)
+        <|> (Array <$> Array.decode decode)
+        <|> (Object <$> Object.decode decode)
diff --git a/source/library/Argo/Internal/Literal.hs b/source/library/Argo/Internal/Literal.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Literal.hs
@@ -0,0 +1,103 @@
+module Argo.Internal.Literal where
+
+import qualified Argo.Vendor.ByteString as ByteString
+import qualified Data.Word as Word
+
+backspace :: Word.Word8
+backspace = 0x08
+
+carriageReturn :: Word.Word8
+carriageReturn = 0x0d
+
+colon :: Word.Word8
+colon = 0x3a
+
+comma :: Word.Word8
+comma = 0x2c
+
+digitNine :: Word.Word8
+digitNine = 0x39
+
+digitOne :: Word.Word8
+digitOne = 0x31
+
+digitZero :: Word.Word8
+digitZero = 0x30
+
+false :: ByteString.ByteString
+false = ByteString.pack [0x66, 0x61, 0x6c, 0x73, 0x65]
+
+formFeed :: Word.Word8
+formFeed = 0x0c
+
+fullStop :: Word.Word8
+fullStop = 0x2e
+
+horizontalTabulation :: Word.Word8
+horizontalTabulation = 0x09
+
+hyphenMinus :: Word.Word8
+hyphenMinus = 0x2d
+
+latinCapitalLetterE :: Word.Word8
+latinCapitalLetterE = 0x45
+
+latinSmallLetterB :: Word.Word8
+latinSmallLetterB = 0x62
+
+latinSmallLetterE :: Word.Word8
+latinSmallLetterE = 0x65
+
+latinSmallLetterF :: Word.Word8
+latinSmallLetterF = 0x66
+
+latinSmallLetterN :: Word.Word8
+latinSmallLetterN = 0x6e
+
+latinSmallLetterR :: Word.Word8
+latinSmallLetterR = 0x72
+
+latinSmallLetterT :: Word.Word8
+latinSmallLetterT = 0x74
+
+latinSmallLetterU :: Word.Word8
+latinSmallLetterU = 0x75
+
+leftCurlyBracket :: Word.Word8
+leftCurlyBracket = 0x7b
+
+leftSquareBracket :: Word.Word8
+leftSquareBracket = 0x5b
+
+newLine :: Word.Word8
+newLine = 0x0a
+
+null :: ByteString.ByteString
+null = ByteString.pack [0x6e, 0x75, 0x6c, 0x6c]
+
+plusSign :: Word.Word8
+plusSign = 0x2b
+
+quotationMark :: Word.Word8
+quotationMark = 0x22
+
+reverseSolidus :: Word.Word8
+reverseSolidus = 0x5c
+
+rightCurlyBracket :: Word.Word8
+rightCurlyBracket = 0x7d
+
+rightSquareBracket :: Word.Word8
+rightSquareBracket = 0x5d
+
+solidus :: Word.Word8
+solidus = 0x2f
+
+space :: Word.Word8
+space = 0x20
+
+tilde :: Word.Word8
+tilde = 0x7e
+
+true :: ByteString.ByteString
+true = ByteString.pack [0x74, 0x72, 0x75, 0x65]
diff --git a/source/library/Argo/Internal/Main.hs b/source/library/Argo/Internal/Main.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Main.hs
@@ -0,0 +1,88 @@
+module Argo.Internal.Main where
+
+import qualified Argo.Internal.Decode as Decode
+import qualified Argo.Internal.Encode as Encode
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Type.Flag as Flag
+import qualified Argo.Internal.Type.Settings as Settings
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.ByteString as ByteString
+import qualified Control.Monad as Monad
+import qualified Data.Version as Version
+import qualified Paths_argo as This
+import qualified System.Console.GetOpt as Console
+import qualified System.Environment as Environment
+import qualified System.Exit as Exit
+import qualified System.IO as IO
+
+main :: IO ()
+main = do
+    name <- Environment.getProgName
+    arguments <- Environment.getArgs
+    mainWith name arguments
+
+mainWith :: String -> [String] -> IO ()
+mainWith name arguments = do
+    let ((warnings, errors), flags) = getFlags arguments
+    mapM_ (IO.hPutStrLn IO.stderr) warnings
+    mapM_ (IO.hPutStr IO.stderr) errors
+    Monad.unless (null errors) Exit.exitFailure
+
+    withSettings name flags $ \settings -> do
+        contents <- ByteString.getContents
+        value <- case Decode.decode contents of
+            Left e -> fail e
+            Right x -> pure (x :: Value.Value)
+        Builder.hPutBuilder IO.stdout
+            $ Encode.encodeWith (Settings.indent settings) value
+
+getFlags :: [String] -> (([String], [String]), [Flag.Flag])
+getFlags arguments =
+    let
+        (flags, xs, ys, errors) =
+            Console.getOpt' Console.Permute options arguments
+        warnings =
+            fmap (mappend "unknown argument " . quote) xs
+                <> fmap (mappend "unknown option " . quote) ys
+    in ((warnings, errors), flags)
+
+withSettings :: String -> [Flag.Flag] -> (Settings.Settings -> IO ()) -> IO ()
+withSettings name flags callback = do
+    settings <- either fail pure
+        $ Monad.foldM Settings.applyFlag Settings.initial flags
+    if Settings.help settings
+        then putStr
+            $ Console.usageInfo (name <> " version " <> version) options
+        else if Settings.version settings
+            then putStrLn version
+            else callback settings
+
+quote :: String -> String
+quote x = "`" <> x <> "'"
+
+version :: String
+version = Version.showVersion This.version
+
+options :: [Console.OptDescr Flag.Flag]
+options =
+    [ Console.Option
+        ['h', '?']
+        ["help"]
+        (Console.NoArg Flag.Help)
+        "shows this help message and exits"
+    , Console.Option
+        ['v']
+        ["version"]
+        (Console.NoArg Flag.Version)
+        "shows the version number and exits"
+    , Console.Option
+        ['s']
+        ["spaces"]
+        (Console.ReqArg Flag.Spaces "INT")
+        "pretty-prints the output using INT sapces"
+    , Console.Option
+        ['t']
+        ["tab"]
+        (Console.NoArg Flag.Tab)
+        "pretty-prints the output using tabs"
+    ]
diff --git a/source/library/Argo/Internal/Pattern.hs b/source/library/Argo/Internal/Pattern.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Pattern.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE PatternSynonyms #-}
+
+module Argo.Internal.Pattern where
+
+import qualified Argo.Internal.Json.Array as Array
+import qualified Argo.Internal.Json.Boolean as Boolean
+import qualified Argo.Internal.Json.Member as Member
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Json.Null as Null
+import qualified Argo.Internal.Json.Number as Number
+import qualified Argo.Internal.Json.Object as Object
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Type.Decimal as Decimal
+import qualified Argo.Vendor.Text as Text
+
+pattern Null :: Value.Value
+pattern Null = Value.Null (Null.Null ())
+
+pattern Boolean :: Bool -> Value.Value
+pattern Boolean x = Value.Boolean (Boolean.Boolean x)
+
+pattern Number :: Decimal.Decimal -> Value.Value
+pattern Number x = Value.Number (Number.Number x)
+
+pattern String :: Text.Text -> Value.Value
+pattern String x = Value.String (String.String x)
+
+pattern Array :: [Value.Value] -> Value.Value
+pattern Array x = Value.Array (Array.Array x)
+
+pattern Object :: [Member.Member Value.Value] -> Value.Value
+pattern Object x = Value.Object (Object.Object x)
+
+{-# COMPLETE Null, Boolean, Number, String, Array, Object #-}
+
+pattern Name :: Text.Text -> Name.Name
+pattern Name x = Name.Name (String.String x)
+
+{-# COMPLETE Name #-}
+
+pattern Decimal :: Integer -> Integer -> Decimal.Decimal
+pattern Decimal s e <- Decimal.Decimal s e where
+    Decimal s e = Decimal.decimal s e
+
+{-# COMPLETE Decimal #-}
diff --git a/source/library/Argo/Internal/Pointer/Pointer.hs b/source/library/Argo/Internal/Pointer/Pointer.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Pointer/Pointer.hs
@@ -0,0 +1,87 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Pointer.Pointer where
+
+import qualified Argo.Internal.Json.Array as Array
+import qualified Argo.Internal.Json.Member as Member
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Json.Object as Object
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Pointer.Token as Token
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Text as Text
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Applicative as Applicative
+import qualified Data.List as List
+import qualified GHC.Generics as Generics
+import qualified Text.Read as Read
+
+-- | A JSON pointer, as described by RFC 6901.
+-- <https://datatracker.ietf.org/doc/html/rfc6901>
+newtype Pointer
+    = Pointer [Token.Token]
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+fromList :: [Token.Token] -> Pointer
+fromList = Pointer
+
+toList :: Pointer -> [Token.Token]
+toList (Pointer x) = x
+
+decode :: Decoder.Decoder Pointer
+decode = fromList <$> Applicative.many decodeToken
+
+decodeToken :: Decoder.Decoder Token.Token
+decodeToken = do
+    Decoder.word8 Literal.solidus
+    Token.decode
+
+encode :: Pointer -> Encoder.Encoder ()
+encode = mapM_ encodeToken . toList
+
+encodeToken :: Token.Token -> Encoder.Encoder ()
+encodeToken x = do
+    Trans.lift . Trans.tell $ Builder.word8 Literal.solidus
+    Token.encode x
+
+evaluate :: Pointer -> Value.Value -> Either String Value.Value
+evaluate p v = case toList p of
+    [] -> pure v
+    t : ts -> do
+        w <- case v of
+            Value.Array a -> atIndex t a
+            Value.Object o -> atKey t o
+            _ -> Left "not indexable"
+        evaluate (fromList ts) w
+
+atIndex :: Token.Token -> Array.Array value -> Either String value
+atIndex t a = do
+    i <- tokenToIndex t
+    case drop i $ Array.toList a of
+        [] -> Left $ "missing index: " <> show t
+        e : _ -> pure e
+
+tokenToIndex :: Token.Token -> Either String Int
+tokenToIndex token = do
+    let text = Token.toText token
+        invalid = "invalid index: " <> show token
+    case Text.uncons text of
+        Just ('0', rest) -> if Text.null rest then pure 0 else Left invalid
+        _ -> maybe (Left invalid) pure . Read.readMaybe $ Text.unpack text
+
+atKey :: Token.Token -> Object.Object value -> Either String value
+atKey t =
+    maybe (Left $ "missing key: " <> show t) (\(Member.Member _ v) -> pure v)
+        . List.find
+              (\(Member.Member k _) ->
+                  String.toText (Name.toString k) == Token.toText t
+              )
+        . Object.toList
diff --git a/source/library/Argo/Internal/Pointer/Token.hs b/source/library/Argo/Internal/Pointer/Token.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Pointer/Token.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Pointer.Token where
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Decoder as Decoder
+import qualified Argo.Internal.Type.Encoder as Encoder
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.ByteString as ByteString
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Text as Text
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Data.String as String
+import qualified Data.Word as Word
+import qualified GHC.Generics as Generics
+
+newtype Token
+    = Token Text.Text
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+instance String.IsString Token where
+    fromString = fromText . String.fromString
+
+fromText :: Text.Text -> Token
+fromText = Token
+
+toText :: Token -> Text.Text
+toText (Token x) = x
+
+decode :: Decoder.Decoder Token
+decode = do
+    x <- Decoder.takeWhile $ (/=) Literal.solidus
+    y <- either (Trans.lift . Trans.throwE) pure $ unescape x
+    case Text.decodeUtf8' y of
+        Left e -> Trans.lift . Trans.throwE $ show e
+        Right z -> pure $ fromText z
+
+unescape :: ByteString.ByteString -> Either String ByteString.ByteString
+unescape = fmap ByteString.pack . unescapeHelper . ByteString.unpack
+
+unescapeHelper :: [Word.Word8] -> Either String [Word.Word8]
+unescapeHelper xs = case xs of
+    [] -> pure xs
+    x : ys -> if x == Literal.tilde
+        then case ys of
+            y : zs
+                | y == Literal.digitZero
+                -> (:) Literal.tilde <$> unescapeHelper zs
+                | y == Literal.digitOne
+                -> (:) Literal.solidus <$> unescapeHelper zs
+            _ -> Left "invalid escape"
+        else (:) x <$> unescapeHelper ys
+
+encode :: Token -> Encoder.Encoder ()
+encode =
+    Trans.lift . Trans.tell . Text.encodeUtf8BuilderEscaped encodeChar . toText
+
+encodeChar :: Builder.BoundedPrim Word.Word8
+encodeChar =
+    Builder.condB (== Literal.tilde) (encodeEscape Literal.digitZero)
+        . Builder.condB (== Literal.solidus) (encodeEscape Literal.digitOne)
+        $ Builder.liftFixedToBounded Builder.word8F
+
+encodeEscape :: Word.Word8 -> Builder.BoundedPrim a
+encodeEscape x =
+    Builder.liftFixedToBounded
+        $ const (Literal.tilde, x)
+        Builder.>$< Builder.word8F
+        Builder.>*< Builder.word8F
diff --git a/source/library/Argo/Internal/QuasiQuoter.hs b/source/library/Argo/Internal/QuasiQuoter.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/QuasiQuoter.hs
@@ -0,0 +1,35 @@
+module Argo.Internal.QuasiQuoter where
+
+import qualified Argo.Internal.Decode as Decode
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Text as Text
+
+pointer :: TH.QuasiQuoter
+pointer = defaultQuasiQuoter
+    { TH.quoteExp =
+        either fail TH.lift
+        . Decode.decodePointer
+        . Text.encodeUtf8
+        . Text.pack
+    }
+
+value :: TH.QuasiQuoter
+value = defaultQuasiQuoter
+    { TH.quoteExp =
+        either fail (TH.lift . asValue)
+        . Decode.decode
+        . Text.encodeUtf8
+        . Text.pack
+    }
+
+asValue :: Value.Value -> Value.Value
+asValue = id
+
+defaultQuasiQuoter :: TH.QuasiQuoter
+defaultQuasiQuoter = TH.QuasiQuoter
+    { TH.quoteDec = const $ fail "cannot be used as a declaration"
+    , TH.quoteExp = const $ fail "cannot be used as an expression"
+    , TH.quotePat = const $ fail "cannot be used as a pattern"
+    , TH.quoteType = const $ fail "cannot be used as a type"
+    }
diff --git a/source/library/Argo/Internal/Schema/Identifier.hs b/source/library/Argo/Internal/Schema/Identifier.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Schema/Identifier.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Schema.Identifier where
+
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Text as Text
+import qualified Data.String as String
+import qualified GHC.Generics as Generics
+
+newtype Identifier
+    = Identifier Text.Text
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Ord, Show)
+
+instance String.IsString Identifier where
+    fromString = fromText . String.fromString
+
+instance Semigroup Identifier where
+    x <> y = fromText $ toText x <> toText y
+
+fromText :: Text.Text -> Identifier
+fromText = Identifier
+
+toText :: Identifier -> Text.Text
+toText (Identifier x) = x
diff --git a/source/library/Argo/Internal/Schema/Schema.hs b/source/library/Argo/Internal/Schema/Schema.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Schema/Schema.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Schema.Schema where
+
+import qualified Argo.Internal.Json.Member as Member
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Json.Value as Value
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Argo.Vendor.Text as Text
+import qualified Data.List.NonEmpty as NonEmpty
+import qualified GHC.Generics as Generics
+import qualified Numeric.Natural as Natural
+
+-- | A JSON Schema.
+-- <https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-01>
+data Schema
+    = Array
+        (Maybe Natural.Natural)
+        (Maybe Natural.Natural)
+        (Either Schema (NonEmpty.NonEmpty Schema))
+        (Maybe Schema)
+    | Boolean
+    | Const Value.Value
+    | False
+    | Integer (Maybe Integer) (Maybe Integer)
+    | Null
+    | Number
+    | Object [(Name.Name, Schema)] [Name.Name] (Maybe Schema)
+    | OneOf [Schema]
+    | Ref Identifier.Identifier
+    | String (Maybe Natural.Natural) (Maybe Natural.Natural)
+    | True
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+instance Semigroup Schema where
+    x <> y = case (x, y) of
+        (OneOf xs, OneOf ys) -> OneOf $ xs <> ys
+        (OneOf xs, _) -> OneOf $ y : xs
+        (_, OneOf ys) -> OneOf $ x : ys
+        (_, _) -> OneOf [x, y]
+
+instance Monoid Schema where
+    mempty = Argo.Internal.Schema.Schema.True
+
+member :: String -> a -> Member.Member a
+member k v =
+    Member.fromTuple (Name.fromString . String.fromText $ Text.pack k, v)
+
+maybeRef :: (Maybe Identifier.Identifier, Schema) -> Schema
+maybeRef (m, s) = maybe s Ref m
+
+false :: Schema
+false = Argo.Internal.Schema.Schema.False
+
+true :: Schema
+true = Argo.Internal.Schema.Schema.True
+
+unidentified :: Schema -> (Maybe Identifier.Identifier, Schema)
+unidentified s = (Nothing, s)
+
+withIdentifier
+    :: Identifier.Identifier -> Schema -> (Maybe Identifier.Identifier, Schema)
+withIdentifier i s = (Just i, s)
diff --git a/source/library/Argo/Internal/Type/Config.hs b/source/library/Argo/Internal/Type/Config.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Config.hs
@@ -0,0 +1,20 @@
+module Argo.Internal.Type.Config where
+
+import qualified Argo.Internal.Type.Indent as Indent
+
+data Config = Config
+    { indent :: Indent.Indent
+    , level :: Int
+    }
+    deriving (Eq, Show)
+
+initial :: Config
+initial = Config { indent = Indent.Spaces 0, level = 0 }
+
+hasIndent :: Config -> Bool
+hasIndent x = case indent x of
+    Indent.Spaces y -> y > 0
+    Indent.Tab -> True
+
+increaseLevel :: Config -> Config
+increaseLevel x = x { level = level x + 1 }
diff --git a/source/library/Argo/Internal/Type/Decimal.hs b/source/library/Argo/Internal/Type/Decimal.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Decimal.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Type.Decimal where
+
+import Data.Ratio ((%))
+
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified Data.List as List
+import qualified Data.Ratio as Ratio
+import qualified GHC.Generics as Generics
+import qualified Numeric
+
+data Decimal = Decimal Integer Integer
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+negate :: Decimal -> Decimal
+negate (Decimal s e) = Decimal (-s) e
+
+decimal :: Integer -> Integer -> Decimal
+decimal s = normalize . Decimal s
+
+normalize :: Decimal -> Decimal
+normalize (Decimal s e) = if s == 0
+    then Decimal 0 0
+    else
+        let (q, r) = quotRem s 10
+        in if r == 0 then normalize $ Decimal q (e + 1) else Decimal s e
+
+fromInteger :: Integer -> Decimal
+fromInteger = flip decimal 0
+
+toInteger :: Decimal -> Maybe Integer
+toInteger (Decimal s e) = if e < 0 then Nothing else Just $ s * 10 ^ e
+
+fromRealFloat :: RealFloat a => a -> Maybe Decimal
+fromRealFloat x = if isNaN x || isInfinite x
+    then Nothing
+    else
+        Just
+        . (if x < 0 then Argo.Internal.Type.Decimal.negate else id)
+        . uncurry fromDigits
+        . Numeric.floatToDigits 10
+        $ abs x
+
+toRealFloat :: RealFloat a => Decimal -> a
+toRealFloat = Prelude.fromRational . Argo.Internal.Type.Decimal.toRational
+
+fromDigits :: [Int] -> Int -> Decimal
+fromDigits ds e = uncurry decimal $ List.foldl'
+    (\(a, n) d -> (a * 10 + Prelude.toInteger d, n - 1))
+    (0, Prelude.toInteger e)
+    ds
+
+toRational :: Decimal -> Rational
+toRational d@(Decimal s e) = maybe (s % (10 ^ (-e))) fromIntegral
+    $ Argo.Internal.Type.Decimal.toInteger d
+
+fromRational :: Rational -> Maybe Decimal
+fromRational r =
+    let
+        n = Ratio.numerator r
+        d1 = Ratio.denominator r
+        (t, d2) = factor 2 (0 :: Integer) d1
+        (f, d3) = factor 5 (0 :: Integer) d2
+        p = max t f
+    in if d3 == 1
+        then Just $ decimal (n * 2 ^ (p - t) * 5 ^ (p - f)) (-p)
+        else Nothing
+
+-- factor d 0 x = (p, y) <=> x = (d ^ p) * y
+factor :: (Num a, Integral b) => b -> a -> b -> (a, b)
+factor d n x =
+    let (q, r) = quotRem x d
+    in if x /= 0 && r == 0 then factor d (n + 1) q else (n, x)
diff --git a/source/library/Argo/Internal/Type/Decoder.hs b/source/library/Argo/Internal/Type/Decoder.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Decoder.hs
@@ -0,0 +1,92 @@
+module Argo.Internal.Type.Decoder where
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Vendor.ByteString as ByteString
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Applicative as Applicative
+import qualified Control.Monad as Monad
+import qualified Data.Functor.Identity as Identity
+import qualified Data.Word as Word
+
+type Decoder
+    = Trans.StateT
+          ByteString.ByteString
+          (Trans.ExceptT String Identity.Identity)
+
+unwrap
+    :: Decoder a
+    -> ByteString.ByteString
+    -> Either String (a, ByteString.ByteString)
+unwrap d = Identity.runIdentity . Trans.runExceptT . Trans.runStateT d
+
+run :: Decoder a -> ByteString.ByteString -> Either String a
+run d = fmap fst . unwrap (d <* eof)
+
+list :: Decoder a -> Decoder [a]
+list f = listWith f []
+
+listWith :: Decoder a -> [a] -> Decoder [a]
+listWith f xs = do
+    m <- Applicative.optional $ do
+        Monad.unless (null xs) $ do
+            word8 Literal.comma
+            spaces
+        f
+    case m of
+        Nothing -> pure $ reverse xs
+        Just x -> listWith f $ x : xs
+
+byteString :: ByteString.ByteString -> Decoder ()
+byteString x = do
+    b1 <- Trans.get
+    case ByteString.stripPrefix x b1 of
+        Nothing -> Trans.lift . Trans.throwE $ "byteString: " <> show x
+        Just b2 -> Trans.put b2
+
+dropWhile :: (Word.Word8 -> Bool) -> Decoder ()
+dropWhile f = do
+    b <- Trans.get
+    Trans.put $ ByteString.dropWhile f b
+
+eof :: Decoder ()
+eof = do
+    b <- Trans.get
+    Monad.unless (ByteString.null b) . Trans.lift $ Trans.throwE "eof"
+
+isDigit :: Word.Word8 -> Bool
+isDigit x = Literal.digitZero <= x && x <= Literal.digitNine
+
+isSpace :: Word.Word8 -> Bool
+isSpace x =
+    (x == Literal.space)
+        || (x == Literal.horizontalTabulation)
+        || (x == Literal.newLine)
+        || (x == Literal.carriageReturn)
+
+satisfy :: (Word.Word8 -> Bool) -> Decoder Word.Word8
+satisfy f = do
+    b1 <- Trans.get
+    case ByteString.uncons b1 of
+        Just (x, b2) | f x -> do
+            Trans.put b2
+            pure x
+        _ -> Trans.lift $ Trans.throwE "satisfy"
+
+spaces :: Decoder ()
+spaces = Argo.Internal.Type.Decoder.dropWhile isSpace
+
+takeWhile :: (Word.Word8 -> Bool) -> Decoder ByteString.ByteString
+takeWhile f = do
+    b1 <- Trans.get
+    let (x, b2) = ByteString.span f b1
+    Trans.put b2
+    pure x
+
+takeWhile1 :: (Word.Word8 -> Bool) -> Decoder ByteString.ByteString
+takeWhile1 f = do
+    x <- Argo.Internal.Type.Decoder.takeWhile f
+    Monad.when (ByteString.null x) . Trans.lift $ Trans.throwE "takeWhile1"
+    pure x
+
+word8 :: Word.Word8 -> Decoder ()
+word8 = Monad.void . satisfy . (==)
diff --git a/source/library/Argo/Internal/Type/Encoder.hs b/source/library/Argo/Internal/Type/Encoder.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Encoder.hs
@@ -0,0 +1,61 @@
+module Argo.Internal.Type.Encoder where
+
+import qualified Argo.Internal.Literal as Literal
+import qualified Argo.Internal.Type.Config as Config
+import qualified Argo.Internal.Type.Indent as Indent
+import qualified Argo.Vendor.Builder as Builder
+import qualified Argo.Vendor.Transformers as Trans
+import qualified Control.Monad as Monad
+import qualified Data.Functor.Identity as Identity
+import qualified Data.Semigroup as Semigroup
+
+type Encoder
+    = Trans.ReaderT
+          Config.Config
+          (Trans.WriterT Builder.Builder Identity.Identity)
+
+unwrap :: Config.Config -> Encoder a -> (a, Builder.Builder)
+unwrap c = Identity.runIdentity . Trans.runWriterT . flip Trans.runReaderT c
+
+run :: Config.Config -> Encoder a -> Builder.Builder
+run c = snd . unwrap c
+
+list
+    :: Encoder ()
+    -> Encoder ()
+    -> Encoder ()
+    -> (a -> Encoder ())
+    -> [a]
+    -> Encoder ()
+list left right separator encode xs = do
+    left
+    case xs of
+        [] -> pure ()
+        x : ys -> do
+            Trans.local Config.increaseLevel $ do
+                maybeIndent <- makeIndent <$> Trans.ask
+                withNewLine maybeIndent
+                encode x
+                Monad.forM_ ys $ \y -> do
+                    separator
+                    withNewLine maybeIndent
+                    encode y
+            maybeIndent <- makeIndent <$> Trans.ask
+            withNewLine maybeIndent
+    right
+
+makeIndent :: Config.Config -> Maybe Builder.Builder
+makeIndent config = case Config.indent config of
+    Indent.Spaces spaces -> if spaces <= 0
+        then Nothing
+        else
+            Just
+            . Semigroup.stimesMonoid (Config.level config * spaces)
+            $ Builder.word8 Literal.space
+    Indent.Tab ->
+        Just . Semigroup.stimesMonoid (Config.level config) $ Builder.word8
+            Literal.horizontalTabulation
+
+withNewLine :: Maybe Builder.Builder -> Encoder ()
+withNewLine = maybe (pure ()) $ Trans.lift . Trans.tell . mappend
+    (Builder.word8 Literal.newLine)
diff --git a/source/library/Argo/Internal/Type/Flag.hs b/source/library/Argo/Internal/Type/Flag.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Flag.hs
@@ -0,0 +1,8 @@
+module Argo.Internal.Type.Flag where
+
+data Flag
+    = Help
+    | Spaces String
+    | Tab
+    | Version
+    deriving (Eq, Show)
diff --git a/source/library/Argo/Internal/Type/Indent.hs b/source/library/Argo/Internal/Type/Indent.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Indent.hs
@@ -0,0 +1,6 @@
+module Argo.Internal.Type.Indent where
+
+data Indent
+    = Spaces Int
+    | Tab
+    deriving (Eq, Show)
diff --git a/source/library/Argo/Internal/Type/Nullable.hs b/source/library/Argo/Internal/Type/Nullable.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Nullable.hs
@@ -0,0 +1,17 @@
+module Argo.Internal.Type.Nullable where
+
+newtype Nullable a
+    = Nullable (Maybe a)
+    deriving (Eq, Show)
+
+fromMaybe :: Maybe a -> Nullable a
+fromMaybe = Nullable
+
+toMaybe :: Nullable a -> Maybe a
+toMaybe (Nullable x) = x
+
+nothing :: Nullable a
+nothing = fromMaybe Nothing
+
+just :: a -> Nullable a
+just = fromMaybe . Just
diff --git a/source/library/Argo/Internal/Type/Optional.hs b/source/library/Argo/Internal/Type/Optional.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Optional.hs
@@ -0,0 +1,17 @@
+module Argo.Internal.Type.Optional where
+
+newtype Optional a
+    = Optional (Maybe a)
+    deriving (Eq, Show)
+
+fromMaybe :: Maybe a -> Optional a
+fromMaybe = Optional
+
+toMaybe :: Optional a -> Maybe a
+toMaybe (Optional x) = x
+
+nothing :: Optional a
+nothing = fromMaybe Nothing
+
+just :: a -> Optional a
+just = fromMaybe . Just
diff --git a/source/library/Argo/Internal/Type/Permission.hs b/source/library/Argo/Internal/Type/Permission.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Permission.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveLift #-}
+
+module Argo.Internal.Type.Permission where
+
+import qualified Argo.Vendor.DeepSeq as DeepSeq
+import qualified Argo.Vendor.TemplateHaskell as TH
+import qualified GHC.Generics as Generics
+
+data Permission
+    = Allow
+    | Forbid
+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
+
+toBool :: Permission -> Bool
+toBool x = case x of
+    Allow -> True
+    Forbid -> False
diff --git a/source/library/Argo/Internal/Type/Settings.hs b/source/library/Argo/Internal/Type/Settings.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Argo/Internal/Type/Settings.hs
@@ -0,0 +1,24 @@
+module Argo.Internal.Type.Settings where
+
+import qualified Argo.Internal.Type.Flag as Flag
+import qualified Argo.Internal.Type.Indent as Indent
+import qualified Text.Read as Read
+
+data Settings = Settings
+    { help :: Bool
+    , indent :: Indent.Indent
+    , version :: Bool
+    }
+    deriving (Eq, Show)
+
+initial :: Settings
+initial = Settings { help = False, indent = Indent.Spaces 0, version = False }
+
+applyFlag :: Settings -> Flag.Flag -> Either String Settings
+applyFlag settings flag = case flag of
+    Flag.Help -> pure settings { help = True }
+    Flag.Spaces string -> do
+        int <- Read.readEither string
+        pure settings { indent = Indent.Spaces int }
+    Flag.Tab -> pure settings { indent = Indent.Tab }
+    Flag.Version -> pure settings { version = True }
diff --git a/source/library/Argo/Json/Array.hs b/source/library/Argo/Json/Array.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Array.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Array where
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Transformers as Trans
-import qualified GHC.Generics as Generics
-
-newtype Array value
-    = Array [value]
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-fromList :: [value] -> Array value
-fromList = Array
-
-toList :: Array value -> [value]
-toList (Array x) = x
-
-encode :: (value -> Encoder.Encoder ()) -> Array value -> Encoder.Encoder ()
-encode f =
-    Encoder.list
-            (Trans.lift . Trans.tell $ Builder.word8 Literal.leftSquareBracket)
-            (Trans.lift . Trans.tell $ Builder.word8 Literal.rightSquareBracket
-            )
-            (Trans.lift . Trans.tell $ Builder.word8 Literal.comma)
-            f
-        . toList
-
-decode :: Decoder.Decoder value -> Decoder.Decoder (Array value)
-decode f = do
-    Decoder.word8 Literal.leftSquareBracket
-    Decoder.spaces
-    xs <- Decoder.list f
-    Decoder.word8 Literal.rightSquareBracket
-    Decoder.spaces
-    pure $ fromList xs
diff --git a/source/library/Argo/Json/Boolean.hs b/source/library/Argo/Json/Boolean.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Boolean.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Boolean where
-
-import Control.Applicative ((<|>))
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Transformers as Trans
-import qualified GHC.Generics as Generics
-
-newtype Boolean
-    = Boolean Bool
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-fromBool :: Bool -> Boolean
-fromBool = Boolean
-
-toBool :: Boolean -> Bool
-toBool (Boolean x) = x
-
-encode :: Boolean -> Encoder.Encoder ()
-encode x = Trans.lift . Trans.tell . Builder.byteString $ if toBool x
-    then Literal.true
-    else Literal.false
-
-decode :: Decoder.Decoder Boolean
-decode = decodeFalse <|> decodeTrue
-
-decodeFalse :: Decoder.Decoder Boolean
-decodeFalse =
-    fromBool False <$ Decoder.byteString Literal.false <* Decoder.spaces
-
-decodeTrue :: Decoder.Decoder Boolean
-decodeTrue =
-    fromBool True <$ Decoder.byteString Literal.true <* Decoder.spaces
diff --git a/source/library/Argo/Json/Member.hs b/source/library/Argo/Json/Member.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Member.hs
+++ /dev/null
@@ -1,45 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Member where
-
-import qualified Argo.Json.Name as Name
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Config as Config
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-import qualified GHC.Generics as Generics
-
-data Member value = Member Name.Name value
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-fromTuple :: (Name.Name, value) -> Member value
-fromTuple = uncurry Member
-
-toTuple :: Member value -> (Name.Name, value)
-toTuple (Member k v) = (k, v)
-
-encode :: (value -> Encoder.Encoder ()) -> Member value -> Encoder.Encoder ()
-encode f (Member x y) = do
-    Name.encode x
-    Trans.lift . Trans.tell $ Builder.word8 Literal.colon
-    config <- Trans.ask
-    Monad.when (Config.hasIndent config)
-        . Trans.lift
-        . Trans.tell
-        $ Builder.word8 Literal.space
-    f y
-
-decode :: Decoder.Decoder value -> Decoder.Decoder (Member value)
-decode g =
-    Member
-        <$> Name.decode
-        <* Decoder.word8 Literal.colon
-        <* Decoder.spaces
-        <*> g
diff --git a/source/library/Argo/Json/Name.hs b/source/library/Argo/Json/Name.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Name.hs
+++ /dev/null
@@ -1,32 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Name where
-
-import qualified Argo.Json.String as String
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Data.String
-import qualified GHC.Generics as Generics
-
-newtype Name
-    = Name String.String
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Ord, Show)
-
-instance Data.String.IsString Name where
-    fromString = fromString . Data.String.fromString
-
-fromString :: String.String -> Name
-fromString = Name
-
-toString :: Name -> String.String
-toString (Name x) = x
-
-encode :: Name -> Encoder.Encoder ()
-encode = String.encode . toString
-
-decode :: Decoder.Decoder Name
-decode = fromString <$> String.decode
diff --git a/source/library/Argo/Json/Null.hs b/source/library/Argo/Json/Null.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Null.hs
+++ /dev/null
@@ -1,30 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Null where
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Transformers as Trans
-import qualified GHC.Generics as Generics
-
-newtype Null
-    = Null ()
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-fromUnit :: () -> Null
-fromUnit = Null
-
-toUnit :: Null -> ()
-toUnit (Null x) = x
-
-encode :: Null -> Encoder.Encoder ()
-encode = const . Trans.lift . Trans.tell $ Builder.byteString Literal.null
-
-decode :: Decoder.Decoder Null
-decode = fromUnit () <$ Decoder.byteString Literal.null <* Decoder.spaces
diff --git a/source/library/Argo/Json/Number.hs b/source/library/Argo/Json/Number.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Number.hs
+++ /dev/null
@@ -1,92 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Number where
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Decimal as Decimal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.ByteString as ByteString
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Applicative as Applicative
-import qualified Control.Monad as Monad
-import qualified Data.Bool as Bool
-import qualified Data.Maybe as Maybe
-import qualified Data.Word as Word
-import qualified GHC.Generics as Generics
-
-newtype Number
-    = Number Decimal.Decimal
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-fromDecimal :: Decimal.Decimal -> Number
-fromDecimal = Number
-
-toDecimal :: Number -> Decimal.Decimal
-toDecimal (Number x) = x
-
-encode :: Number -> Encoder.Encoder ()
-encode x = do
-    let Decimal.Decimal s e = toDecimal x
-    Trans.lift . Trans.tell $ Builder.integerDec s
-    Monad.when (e /= 0)
-        . Trans.lift
-        . Trans.tell
-        $ Builder.word8 Literal.latinSmallLetterE
-        <> Builder.integerDec e
-
-decode :: Decoder.Decoder Number
-decode = do
-    ni <- fmap Maybe.isJust . Applicative.optional $ Decoder.word8
-        Literal.hyphenMinus
-    i <- Decoder.takeWhile1 Decoder.isDigit
-    Monad.when
-            (ByteString.length i
-            > 1
-            && ByteString.elemIndex Literal.digitZero i
-            == Just 0
-            )
-        . Trans.lift
-        $ Trans.throwE "leading zero"
-    f <- fmap (Maybe.fromMaybe ByteString.empty) . Applicative.optional $ do
-        Decoder.word8 Literal.fullStop
-        Decoder.takeWhile1 Decoder.isDigit
-    (ne, e) <-
-        fmap (Maybe.fromMaybe (False, ByteString.empty))
-        . Applicative.optional
-        $ do
-              Monad.void
-                  . Decoder.satisfy
-                  $ \x ->
-                        (x == Literal.latinSmallLetterE)
-                            || (x == Literal.latinCapitalLetterE)
-              ne <-
-                  fmap (== Just Literal.hyphenMinus)
-                  . Applicative.optional
-                  . Decoder.satisfy
-                  $ \x -> x == Literal.hyphenMinus || x == Literal.plusSign
-              e <- Decoder.takeWhile1 Decoder.isDigit
-              pure (ne, e)
-    Decoder.spaces
-    pure . fromDecimal $ Decimal.decimal
-        (negateIf ni $ (fromDigits i * 10 ^ ByteString.length f) + fromDigits f
-        )
-        (negateIf ne (fromDigits e) - intToInteger (ByteString.length f))
-
-negateIf :: Bool -> Integer -> Integer
-negateIf = Bool.bool id negate
-
-fromDigits :: ByteString.ByteString -> Integer
-fromDigits =
-    ByteString.foldl' (\a e -> (a * 10) + word8ToInteger (e - 0x30)) 0
-
-intToInteger :: Int -> Integer
-intToInteger = fromIntegral
-
-word8ToInteger :: Word.Word8 -> Integer
-word8ToInteger = fromIntegral
diff --git a/source/library/Argo/Json/Object.hs b/source/library/Argo/Json/Object.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Object.hs
+++ /dev/null
@@ -1,53 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Object where
-
-import qualified Argo.Json.Member as Member
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-import qualified GHC.Generics as Generics
-
-newtype Object value
-    = Object [Member.Member value]
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-fromList :: [Member.Member value] -> Object value
-fromList = Object
-
-toList :: Object value -> [Member.Member value]
-toList (Object x) = x
-
-encode :: (value -> Encoder.Encoder ()) -> Object value -> Encoder.Encoder ()
-encode f =
-    Encoder.list
-            (Trans.lift . Trans.tell $ Builder.word8 Literal.leftCurlyBracket)
-            (Trans.lift . Trans.tell $ Builder.word8 Literal.rightCurlyBracket)
-            (Trans.lift . Trans.tell $ Builder.word8 Literal.comma)
-            (Member.encode f)
-        . toList
-
-encodeElement
-    :: (value -> Encoder.Encoder ())
-    -> Int
-    -> Member.Member value
-    -> Encoder.Encoder ()
-encodeElement f i x = do
-    Monad.when (i > 0) . Trans.lift . Trans.tell $ Builder.word8 Literal.comma
-    Member.encode f x
-
-decode :: Decoder.Decoder value -> Decoder.Decoder (Object value)
-decode f = do
-    Decoder.word8 Literal.leftCurlyBracket
-    Decoder.spaces
-    xs <- Decoder.list $ Member.decode f
-    Decoder.word8 Literal.rightCurlyBracket
-    Decoder.spaces
-    pure $ fromList xs
diff --git a/source/library/Argo/Json/String.hs b/source/library/Argo/Json/String.hs
deleted file mode 100644
--- a/source/library/Argo/Json/String.hs
+++ /dev/null
@@ -1,179 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.String where
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.ByteString as ByteString
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Text as Text
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-import qualified Data.Char as Char
-import qualified Data.String as String
-import qualified Data.Word as Word
-import qualified GHC.Generics as Generics
-
-newtype String
-    = String Text.Text
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Ord, Show)
-
-instance String.IsString Argo.Json.String.String where
-    fromString = fromText . String.fromString
-
-fromText :: Text.Text -> Argo.Json.String.String
-fromText = String
-
-toText :: Argo.Json.String.String -> Text.Text
-toText (String x) = x
-
-encode :: Argo.Json.String.String -> Encoder.Encoder ()
-encode x =
-    Trans.lift
-        . Trans.tell
-        $ Builder.word8 Literal.quotationMark
-        <> Text.encodeUtf8BuilderEscaped encodeChar (toText x)
-        <> Builder.word8 Literal.quotationMark
-
-encodeChar :: Builder.BoundedPrim Word.Word8
-encodeChar =
-    Builder.condB
-            (== Literal.quotationMark)
-            (encodeShortEscape Literal.quotationMark)
-        . Builder.condB
-              (== Literal.reverseSolidus)
-              (encodeShortEscape Literal.reverseSolidus)
-        . Builder.condB
-              (== Literal.backspace)
-              (encodeShortEscape Literal.latinSmallLetterB)
-        . Builder.condB
-              (== Literal.formFeed)
-              (encodeShortEscape Literal.latinSmallLetterF)
-        . Builder.condB
-              (== Literal.newLine)
-              (encodeShortEscape Literal.latinSmallLetterN)
-        . Builder.condB
-              (== Literal.carriageReturn)
-              (encodeShortEscape Literal.latinSmallLetterR)
-        . Builder.condB
-              (== Literal.horizontalTabulation)
-              (encodeShortEscape Literal.latinSmallLetterT)
-        . Builder.condB (< Literal.space) encodeLongEscape
-        $ Builder.liftFixedToBounded Builder.word8F
-
-encodeShortEscape :: Word.Word8 -> Builder.BoundedPrim a
-encodeShortEscape x =
-    Builder.liftFixedToBounded
-        $ const (Literal.reverseSolidus, x)
-        Builder.>$< Builder.word8F
-        Builder.>*< Builder.word8F
-
-encodeLongEscape :: Builder.BoundedPrim Word.Word8
-encodeLongEscape =
-    Builder.liftFixedToBounded
-        $ (\x ->
-              ( Literal.reverseSolidus
-              , (Literal.latinSmallLetterU, word8ToWord16 x)
-              )
-          )
-        Builder.>$< Builder.word8F
-        Builder.>*< Builder.word8F
-        Builder.>*< Builder.word16HexFixed
-
-word8ToWord16 :: Word.Word8 -> Word.Word16
-word8ToWord16 = fromIntegral
-
-decode :: Decoder.Decoder Argo.Json.String.String
-decode = do
-    Decoder.word8 Literal.quotationMark
-    b1 <- Trans.get
-    i <- case getClose b1 0 of
-        Nothing -> Trans.lift $ Trans.throwE "unterminated string"
-        Just i -> pure i
-    let (xs, b2) = ByteString.splitAt i b1
-    Monad.when (ByteString.any (< Literal.space) xs)
-        . Trans.lift
-        $ Trans.throwE "unescaped control character"
-    Trans.put b2
-    Decoder.word8 Literal.quotationMark
-    Decoder.spaces
-    case Text.decodeUtf8' xs of
-        Left e -> Trans.lift . Trans.throwE $ show e
-        Right x -> case unescapeText x of
-            Left e -> Trans.lift $ Trans.throwE e
-            Right y -> pure $ fromText y
-
-findAt :: Word.Word8 -> Int -> ByteString.ByteString -> Maybe Int
-findAt x i = fmap (+ i) . ByteString.elemIndex x . ByteString.drop i
-
-countConsecutive :: Word.Word8 -> Int -> ByteString.ByteString -> Int
-countConsecutive x i =
-    ByteString.length . ByteString.takeWhileEnd (== x) . ByteString.take i
-
-getClose :: ByteString.ByteString -> Int -> Maybe Int
-getClose b i = do
-    j <- findAt Literal.quotationMark i b
-    let n = countConsecutive Literal.reverseSolidus j b
-    if even n then Just j else getClose b $ j + 1
-
-unescapeText :: Text.Text -> Either Prelude.String Text.Text
-unescapeText =
-    fmap (Text.pack . combineSurrogatePairs) . unescapeString . Text.unpack
-
-combineSurrogatePairs :: Prelude.String -> Prelude.String
-combineSurrogatePairs xs = case xs of
-    "" -> xs
-    x : y : zs | isHighSurrogate x && isLowSurrogate y ->
-        combineSurrogatePair x y : combineSurrogatePairs zs
-    x : ys -> x : combineSurrogatePairs ys
-
-combineSurrogatePair :: Char -> Char -> Char
-combineSurrogatePair hi lo =
-    Char.chr
-        $ 0x10000
-        + ((Char.ord hi - 0xd800) * 0x400)
-        + (Char.ord lo - 0xdc00)
-
-isHighSurrogate :: Char -> Bool
-isHighSurrogate x = '\xd800' <= x && x <= '\xdbff'
-
-isLowSurrogate :: Char -> Bool
-isLowSurrogate x = '\xdc00' <= x && x <= '\xdfff'
-
-unescapeString :: Prelude.String -> Either Prelude.String Prelude.String
-unescapeString xs = case xs of
-    "" -> pure xs
-    '\\' : ys -> case ys of
-        "" -> Left "empty escape"
-        x : zs -> case x of
-            '"' -> ('"' :) <$> unescapeString zs
-            '\\' -> ('\\' :) <$> unescapeString zs
-            '/' -> ('/' :) <$> unescapeString zs
-            'b' -> ('\b' :) <$> unescapeString zs
-            'f' -> ('\f' :) <$> unescapeString zs
-            'n' -> ('\n' :) <$> unescapeString zs
-            'r' -> ('\r' :) <$> unescapeString zs
-            't' -> ('\t' :) <$> unescapeString zs
-            'u' -> case zs of
-                a : b : c : d : es | Just y <- fromLongEscape a b c d ->
-                    (y :) <$> unescapeString es
-                _ -> Left "invalid long escape"
-            _ -> Left "invalid short escape"
-    x : ys -> (x :) <$> unescapeString ys
-
-fromLongEscape :: Char -> Char -> Char -> Char -> Maybe Char
-fromLongEscape a b c d = do
-    w <- fromHexadecimalDigit a
-    x <- fromHexadecimalDigit b
-    y <- fromHexadecimalDigit c
-    z <- fromHexadecimalDigit d
-    pure . Char.chr $ (0x1000 * w) + (0x100 * x) + (0x10 * y) + z
-
-fromHexadecimalDigit :: Char -> Maybe Int
-fromHexadecimalDigit x =
-    if Char.isHexDigit x then Just $ Char.digitToInt x else Nothing
diff --git a/source/library/Argo/Json/Value.hs b/source/library/Argo/Json/Value.hs
deleted file mode 100644
--- a/source/library/Argo/Json/Value.hs
+++ /dev/null
@@ -1,52 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Json.Value where
-
-import Control.Applicative ((<|>))
-
-import qualified Argo.Json.Array as Array
-import qualified Argo.Json.Boolean as Boolean
-import qualified Argo.Json.Null as Null
-import qualified Argo.Json.Number as Number
-import qualified Argo.Json.Object as Object
-import qualified Argo.Json.String as String
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Data.String
-import qualified GHC.Generics as Generics
-
--- | A JSON (JavaScript Object Notation) value, as described by RFC 8259.
--- <https://datatracker.ietf.org/doc/html/rfc8259>
-data Value
-    = Null Null.Null
-    | Boolean Boolean.Boolean
-    | Number Number.Number
-    | String String.String
-    | Array (Array.Array Value)
-    | Object (Object.Object Value)
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-instance Data.String.IsString Value where
-    fromString = String . Data.String.fromString
-
-encode :: Value -> Encoder.Encoder ()
-encode x = case x of
-    Null y -> Null.encode y
-    Boolean y -> Boolean.encode y
-    Number y -> Number.encode y
-    String y -> String.encode y
-    Array y -> Array.encode encode y
-    Object y -> Object.encode encode y
-
-decode :: Decoder.Decoder Value
-decode =
-    (Null <$> Null.decode)
-        <|> (Boolean <$> Boolean.decode)
-        <|> (Number <$> Number.decode)
-        <|> (String <$> String.decode)
-        <|> (Array <$> Array.decode decode)
-        <|> (Object <$> Object.decode decode)
diff --git a/source/library/Argo/Literal.hs b/source/library/Argo/Literal.hs
deleted file mode 100644
--- a/source/library/Argo/Literal.hs
+++ /dev/null
@@ -1,103 +0,0 @@
-module Argo.Literal where
-
-import qualified Argo.Vendor.ByteString as ByteString
-import qualified Data.Word as Word
-
-backspace :: Word.Word8
-backspace = 0x08
-
-carriageReturn :: Word.Word8
-carriageReturn = 0x0d
-
-colon :: Word.Word8
-colon = 0x3a
-
-comma :: Word.Word8
-comma = 0x2c
-
-digitNine :: Word.Word8
-digitNine = 0x39
-
-digitOne :: Word.Word8
-digitOne = 0x31
-
-digitZero :: Word.Word8
-digitZero = 0x30
-
-false :: ByteString.ByteString
-false = ByteString.pack [0x66, 0x61, 0x6c, 0x73, 0x65]
-
-formFeed :: Word.Word8
-formFeed = 0x0c
-
-fullStop :: Word.Word8
-fullStop = 0x2e
-
-horizontalTabulation :: Word.Word8
-horizontalTabulation = 0x09
-
-hyphenMinus :: Word.Word8
-hyphenMinus = 0x2d
-
-latinCapitalLetterE :: Word.Word8
-latinCapitalLetterE = 0x45
-
-latinSmallLetterB :: Word.Word8
-latinSmallLetterB = 0x62
-
-latinSmallLetterE :: Word.Word8
-latinSmallLetterE = 0x65
-
-latinSmallLetterF :: Word.Word8
-latinSmallLetterF = 0x66
-
-latinSmallLetterN :: Word.Word8
-latinSmallLetterN = 0x6e
-
-latinSmallLetterR :: Word.Word8
-latinSmallLetterR = 0x72
-
-latinSmallLetterT :: Word.Word8
-latinSmallLetterT = 0x74
-
-latinSmallLetterU :: Word.Word8
-latinSmallLetterU = 0x75
-
-leftCurlyBracket :: Word.Word8
-leftCurlyBracket = 0x7b
-
-leftSquareBracket :: Word.Word8
-leftSquareBracket = 0x5b
-
-newLine :: Word.Word8
-newLine = 0x0a
-
-null :: ByteString.ByteString
-null = ByteString.pack [0x6e, 0x75, 0x6c, 0x6c]
-
-plusSign :: Word.Word8
-plusSign = 0x2b
-
-quotationMark :: Word.Word8
-quotationMark = 0x22
-
-reverseSolidus :: Word.Word8
-reverseSolidus = 0x5c
-
-rightCurlyBracket :: Word.Word8
-rightCurlyBracket = 0x7d
-
-rightSquareBracket :: Word.Word8
-rightSquareBracket = 0x5d
-
-solidus :: Word.Word8
-solidus = 0x2f
-
-space :: Word.Word8
-space = 0x20
-
-tilde :: Word.Word8
-tilde = 0x7e
-
-true :: ByteString.ByteString
-true = ByteString.pack [0x74, 0x72, 0x75, 0x65]
diff --git a/source/library/Argo/Main.hs b/source/library/Argo/Main.hs
deleted file mode 100644
--- a/source/library/Argo/Main.hs
+++ /dev/null
@@ -1,86 +0,0 @@
-module Argo.Main where
-
-import qualified Argo
-import qualified Argo.Type.Flag as Flag
-import qualified Argo.Type.Settings as Settings
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.ByteString as ByteString
-import qualified Control.Monad as Monad
-import qualified Data.Version as Version
-import qualified Paths_argo as This
-import qualified System.Console.GetOpt as Console
-import qualified System.Environment as Environment
-import qualified System.Exit as Exit
-import qualified System.IO as IO
-
-main :: IO ()
-main = do
-    name <- Environment.getProgName
-    arguments <- Environment.getArgs
-    mainWith name arguments
-
-mainWith :: String -> [String] -> IO ()
-mainWith name arguments = do
-    let ((warnings, errors), flags) = getFlags arguments
-    mapM_ (IO.hPutStrLn IO.stderr) warnings
-    mapM_ (IO.hPutStr IO.stderr) errors
-    Monad.unless (null errors) Exit.exitFailure
-
-    withSettings name flags $ \settings -> do
-        contents <- ByteString.getContents
-        value <- case Argo.decode contents of
-            Left e -> fail e
-            Right x -> pure (x :: Argo.Value)
-        Builder.hPutBuilder IO.stdout
-            $ Argo.encodeWith (Settings.indent settings) value
-
-getFlags :: [String] -> (([String], [String]), [Flag.Flag])
-getFlags arguments =
-    let
-        (flags, xs, ys, errors) =
-            Console.getOpt' Console.Permute options arguments
-        warnings =
-            fmap (mappend "unknown argument " . quote) xs
-                <> fmap (mappend "unknown option " . quote) ys
-    in ((warnings, errors), flags)
-
-withSettings :: String -> [Flag.Flag] -> (Settings.Settings -> IO ()) -> IO ()
-withSettings name flags callback = do
-    settings <- either fail pure
-        $ Monad.foldM Settings.applyFlag Settings.initial flags
-    if Settings.help settings
-        then putStr
-            $ Console.usageInfo (name <> " version " <> version) options
-        else if Settings.version settings
-            then putStrLn version
-            else callback settings
-
-quote :: String -> String
-quote x = "`" <> x <> "'"
-
-version :: String
-version = Version.showVersion This.version
-
-options :: [Console.OptDescr Flag.Flag]
-options =
-    [ Console.Option
-        ['h', '?']
-        ["help"]
-        (Console.NoArg Flag.Help)
-        "shows this help message and exits"
-    , Console.Option
-        ['v']
-        ["version"]
-        (Console.NoArg Flag.Version)
-        "shows the version number and exits"
-    , Console.Option
-        ['s']
-        ["spaces"]
-        (Console.ReqArg Flag.Spaces "INT")
-        "pretty-prints the output using INT sapces"
-    , Console.Option
-        ['t']
-        ["tab"]
-        (Console.NoArg Flag.Tab)
-        "pretty-prints the output using tabs"
-    ]
diff --git a/source/library/Argo/Pattern.hs b/source/library/Argo/Pattern.hs
deleted file mode 100644
--- a/source/library/Argo/Pattern.hs
+++ /dev/null
@@ -1,46 +0,0 @@
-{-# LANGUAGE PatternSynonyms #-}
-
-module Argo.Pattern where
-
-import qualified Argo.Json.Array as Array
-import qualified Argo.Json.Boolean as Boolean
-import qualified Argo.Json.Member as Member
-import qualified Argo.Json.Name as Name
-import qualified Argo.Json.Null as Null
-import qualified Argo.Json.Number as Number
-import qualified Argo.Json.Object as Object
-import qualified Argo.Json.String as String
-import qualified Argo.Json.Value as Value
-import qualified Argo.Type.Decimal as Decimal
-import qualified Argo.Vendor.Text as Text
-
-pattern Null :: Value.Value
-pattern Null = Value.Null (Null.Null ())
-
-pattern Boolean :: Bool -> Value.Value
-pattern Boolean x = Value.Boolean (Boolean.Boolean x)
-
-pattern Number :: Decimal.Decimal -> Value.Value
-pattern Number x = Value.Number (Number.Number x)
-
-pattern String :: Text.Text -> Value.Value
-pattern String x = Value.String (String.String x)
-
-pattern Array :: [Value.Value] -> Value.Value
-pattern Array x = Value.Array (Array.Array x)
-
-pattern Object :: [Member.Member Value.Value] -> Value.Value
-pattern Object x = Value.Object (Object.Object x)
-
-{-# COMPLETE Null, Boolean, Number, String, Array, Object #-}
-
-pattern Name :: Text.Text -> Name.Name
-pattern Name x = Name.Name (String.String x)
-
-{-# COMPLETE Name #-}
-
-pattern Decimal :: Integer -> Integer -> Decimal.Decimal
-pattern Decimal s e <- Decimal.Decimal s e where
-    Decimal s e = Decimal.decimal s e
-
-{-# COMPLETE Decimal #-}
diff --git a/source/library/Argo/Pointer/Pointer.hs b/source/library/Argo/Pointer/Pointer.hs
deleted file mode 100644
--- a/source/library/Argo/Pointer/Pointer.hs
+++ /dev/null
@@ -1,87 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Pointer.Pointer where
-
-import qualified Argo.Json.Array as Array
-import qualified Argo.Json.Member as Member
-import qualified Argo.Json.Name as Name
-import qualified Argo.Json.Object as Object
-import qualified Argo.Json.String as String
-import qualified Argo.Json.Value as Value
-import qualified Argo.Literal as Literal
-import qualified Argo.Pointer.Token as Token
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Text as Text
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Applicative as Applicative
-import qualified Data.List as List
-import qualified GHC.Generics as Generics
-import qualified Text.Read as Read
-
--- | A JSON pointer, as described by RFC 6901.
--- <https://datatracker.ietf.org/doc/html/rfc6901>
-newtype Pointer
-    = Pointer [Token.Token]
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-fromList :: [Token.Token] -> Pointer
-fromList = Pointer
-
-toList :: Pointer -> [Token.Token]
-toList (Pointer x) = x
-
-decode :: Decoder.Decoder Pointer
-decode = fromList <$> Applicative.many decodeToken
-
-decodeToken :: Decoder.Decoder Token.Token
-decodeToken = do
-    Decoder.word8 Literal.solidus
-    Token.decode
-
-encode :: Pointer -> Encoder.Encoder ()
-encode = mapM_ encodeToken . toList
-
-encodeToken :: Token.Token -> Encoder.Encoder ()
-encodeToken x = do
-    Trans.lift . Trans.tell $ Builder.word8 Literal.solidus
-    Token.encode x
-
-evaluate :: Pointer -> Value.Value -> Either String Value.Value
-evaluate p v = case toList p of
-    [] -> pure v
-    t : ts -> do
-        w <- case v of
-            Value.Array a -> atIndex t a
-            Value.Object o -> atKey t o
-            _ -> Left "not indexable"
-        evaluate (fromList ts) w
-
-atIndex :: Token.Token -> Array.Array value -> Either String value
-atIndex t a = do
-    i <- tokenToIndex t
-    case drop i $ Array.toList a of
-        [] -> Left $ "missing index: " <> show t
-        e : _ -> pure e
-
-tokenToIndex :: Token.Token -> Either String Int
-tokenToIndex token = do
-    let text = Token.toText token
-        invalid = "invalid index: " <> show token
-    case Text.uncons text of
-        Just ('0', rest) -> if Text.null rest then pure 0 else Left invalid
-        _ -> maybe (Left invalid) pure . Read.readMaybe $ Text.unpack text
-
-atKey :: Token.Token -> Object.Object value -> Either String value
-atKey t =
-    maybe (Left $ "missing key: " <> show t) (\(Member.Member _ v) -> pure v)
-        . List.find
-              (\(Member.Member k _) ->
-                  String.toText (Name.toString k) == Token.toText t
-              )
-        . Object.toList
diff --git a/source/library/Argo/Pointer/Token.hs b/source/library/Argo/Pointer/Token.hs
deleted file mode 100644
--- a/source/library/Argo/Pointer/Token.hs
+++ /dev/null
@@ -1,72 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Pointer.Token where
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Decoder as Decoder
-import qualified Argo.Type.Encoder as Encoder
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.ByteString as ByteString
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Text as Text
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Data.String as String
-import qualified Data.Word as Word
-import qualified GHC.Generics as Generics
-
-newtype Token
-    = Token Text.Text
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-instance String.IsString Token where
-    fromString = fromText . String.fromString
-
-fromText :: Text.Text -> Token
-fromText = Token
-
-toText :: Token -> Text.Text
-toText (Token x) = x
-
-decode :: Decoder.Decoder Token
-decode = do
-    x <- Decoder.takeWhile $ (/=) Literal.solidus
-    y <- either (Trans.lift . Trans.throwE) pure $ unescape x
-    case Text.decodeUtf8' y of
-        Left e -> Trans.lift . Trans.throwE $ show e
-        Right z -> pure $ fromText z
-
-unescape :: ByteString.ByteString -> Either String ByteString.ByteString
-unescape = fmap ByteString.pack . unescapeHelper . ByteString.unpack
-
-unescapeHelper :: [Word.Word8] -> Either String [Word.Word8]
-unescapeHelper xs = case xs of
-    [] -> pure xs
-    x : ys -> if x == Literal.tilde
-        then case ys of
-            y : zs
-                | y == Literal.digitZero
-                -> (:) Literal.tilde <$> unescapeHelper zs
-                | y == Literal.digitOne
-                -> (:) Literal.solidus <$> unescapeHelper zs
-            _ -> Left "invalid escape"
-        else (:) x <$> unescapeHelper ys
-
-encode :: Token -> Encoder.Encoder ()
-encode =
-    Trans.lift . Trans.tell . Text.encodeUtf8BuilderEscaped encodeChar . toText
-
-encodeChar :: Builder.BoundedPrim Word.Word8
-encodeChar =
-    Builder.condB (== Literal.tilde) (encodeEscape Literal.digitZero)
-        . Builder.condB (== Literal.solidus) (encodeEscape Literal.digitOne)
-        $ Builder.liftFixedToBounded Builder.word8F
-
-encodeEscape :: Word.Word8 -> Builder.BoundedPrim a
-encodeEscape x =
-    Builder.liftFixedToBounded
-        $ const (Literal.tilde, x)
-        Builder.>$< Builder.word8F
-        Builder.>*< Builder.word8F
diff --git a/source/library/Argo/QuasiQuoter.hs b/source/library/Argo/QuasiQuoter.hs
deleted file mode 100644
--- a/source/library/Argo/QuasiQuoter.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-module Argo.QuasiQuoter where
-
-import qualified Argo.Decode as Decode
-import qualified Argo.Json.Value as Value
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Text as Text
-
-pointer :: TH.QuasiQuoter
-pointer = defaultQuasiQuoter
-    { TH.quoteExp =
-        either fail TH.lift
-        . Decode.decodePointer
-        . Text.encodeUtf8
-        . Text.pack
-    }
-
-value :: TH.QuasiQuoter
-value = defaultQuasiQuoter
-    { TH.quoteExp =
-        either fail (TH.lift . asValue)
-        . Decode.decode
-        . Text.encodeUtf8
-        . Text.pack
-    }
-
-asValue :: Value.Value -> Value.Value
-asValue = id
-
-defaultQuasiQuoter :: TH.QuasiQuoter
-defaultQuasiQuoter = TH.QuasiQuoter
-    { TH.quoteDec = const $ fail "cannot be used as a declaration"
-    , TH.quoteExp = const $ fail "cannot be used as an expression"
-    , TH.quotePat = const $ fail "cannot be used as a pattern"
-    , TH.quoteType = const $ fail "cannot be used as a type"
-    }
diff --git a/source/library/Argo/Schema/Identifier.hs b/source/library/Argo/Schema/Identifier.hs
deleted file mode 100644
--- a/source/library/Argo/Schema/Identifier.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Schema.Identifier where
-
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Text as Text
-import qualified Data.String as String
-import qualified GHC.Generics as Generics
-
-newtype Identifier
-    = Identifier Text.Text
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Ord, Show)
-
-instance String.IsString Identifier where
-    fromString = fromText . String.fromString
-
-instance Semigroup Identifier where
-    x <> y = fromText $ toText x <> toText y
-
-fromText :: Text.Text -> Identifier
-fromText = Identifier
-
-toText :: Identifier -> Text.Text
-toText (Identifier x) = x
diff --git a/source/library/Argo/Schema/Schema.hs b/source/library/Argo/Schema/Schema.hs
deleted file mode 100644
--- a/source/library/Argo/Schema/Schema.hs
+++ /dev/null
@@ -1,68 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Schema.Schema where
-
-import qualified Argo.Json.Member as Member
-import qualified Argo.Json.Name as Name
-import qualified Argo.Json.String as String
-import qualified Argo.Json.Value as Value
-import qualified Argo.Schema.Identifier as Identifier
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Argo.Vendor.Text as Text
-import qualified Data.List.NonEmpty as NonEmpty
-import qualified GHC.Generics as Generics
-import qualified Numeric.Natural as Natural
-
--- | A JSON Schema.
--- <https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-01>
-data Schema
-    = Array
-        (Maybe Natural.Natural)
-        (Maybe Natural.Natural)
-        (Either Schema (NonEmpty.NonEmpty Schema))
-        (Maybe Schema)
-    | Boolean
-    | Const Value.Value
-    | False
-    | Integer (Maybe Integer) (Maybe Integer)
-    | Null
-    | Number
-    | Object [(Name.Name, Schema)] [Name.Name] (Maybe Schema)
-    | OneOf [Schema]
-    | Ref Identifier.Identifier
-    | String (Maybe Natural.Natural) (Maybe Natural.Natural)
-    | True
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-instance Semigroup Schema where
-    x <> y = case (x, y) of
-        (OneOf xs, OneOf ys) -> OneOf $ xs <> ys
-        (OneOf xs, _) -> OneOf $ y : xs
-        (_, OneOf ys) -> OneOf $ x : ys
-        (_, _) -> OneOf [x, y]
-
-instance Monoid Schema where
-    mempty = Argo.Schema.Schema.True
-
-member :: String -> a -> Member.Member a
-member k v =
-    Member.fromTuple (Name.fromString . String.fromText $ Text.pack k, v)
-
-maybeRef :: (Maybe Identifier.Identifier, Schema) -> Schema
-maybeRef (m, s) = maybe s Ref m
-
-false :: Schema
-false = Argo.Schema.Schema.False
-
-true :: Schema
-true = Argo.Schema.Schema.True
-
-unidentified :: Schema -> (Maybe Identifier.Identifier, Schema)
-unidentified s = (Nothing, s)
-
-withIdentifier
-    :: Identifier.Identifier -> Schema -> (Maybe Identifier.Identifier, Schema)
-withIdentifier i s = (Just i, s)
diff --git a/source/library/Argo/Type/Config.hs b/source/library/Argo/Type/Config.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Config.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-module Argo.Type.Config where
-
-import qualified Argo.Type.Indent as Indent
-
-data Config = Config
-    { indent :: Indent.Indent
-    , level :: Int
-    }
-    deriving (Eq, Show)
-
-initial :: Config
-initial = Config { indent = Indent.Spaces 0, level = 0 }
-
-hasIndent :: Config -> Bool
-hasIndent x = case indent x of
-    Indent.Spaces y -> y > 0
-    Indent.Tab -> True
-
-increaseLevel :: Config -> Config
-increaseLevel x = x { level = level x + 1 }
diff --git a/source/library/Argo/Type/Decimal.hs b/source/library/Argo/Type/Decimal.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Decimal.hs
+++ /dev/null
@@ -1,77 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Type.Decimal where
-
-import Data.Ratio ((%))
-
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified Data.List as List
-import qualified Data.Ratio as Ratio
-import qualified GHC.Generics as Generics
-import qualified Numeric
-
-data Decimal = Decimal Integer Integer
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-negate :: Decimal -> Decimal
-negate (Decimal s e) = Decimal (-s) e
-
-decimal :: Integer -> Integer -> Decimal
-decimal s = normalize . Decimal s
-
-normalize :: Decimal -> Decimal
-normalize (Decimal s e) = if s == 0
-    then Decimal 0 0
-    else
-        let (q, r) = quotRem s 10
-        in if r == 0 then normalize $ Decimal q (e + 1) else Decimal s e
-
-fromInteger :: Integer -> Decimal
-fromInteger = flip decimal 0
-
-toInteger :: Decimal -> Maybe Integer
-toInteger (Decimal s e) = if e < 0 then Nothing else Just $ s * 10 ^ e
-
-fromRealFloat :: RealFloat a => a -> Maybe Decimal
-fromRealFloat x = if isNaN x || isInfinite x
-    then Nothing
-    else
-        Just
-        . (if x < 0 then Argo.Type.Decimal.negate else id)
-        . uncurry fromDigits
-        . Numeric.floatToDigits 10
-        $ abs x
-
-toRealFloat :: RealFloat a => Decimal -> a
-toRealFloat = Prelude.fromRational . Argo.Type.Decimal.toRational
-
-fromDigits :: [Int] -> Int -> Decimal
-fromDigits ds e = uncurry decimal $ List.foldl'
-    (\(a, n) d -> (a * 10 + Prelude.toInteger d, n - 1))
-    (0, Prelude.toInteger e)
-    ds
-
-toRational :: Decimal -> Rational
-toRational d@(Decimal s e) =
-    maybe (s % (10 ^ (-e))) fromIntegral $ Argo.Type.Decimal.toInteger d
-
-fromRational :: Rational -> Maybe Decimal
-fromRational r =
-    let
-        n = Ratio.numerator r
-        d1 = Ratio.denominator r
-        (t, d2) = factor 2 (0 :: Integer) d1
-        (f, d3) = factor 5 (0 :: Integer) d2
-        p = max t f
-    in if d3 == 1
-        then Just $ decimal (n * 2 ^ (p - t) * 5 ^ (p - f)) (-p)
-        else Nothing
-
--- factor d 0 x = (p, y) <=> x = (d ^ p) * y
-factor :: (Num a, Integral b) => b -> a -> b -> (a, b)
-factor d n x =
-    let (q, r) = quotRem x d
-    in if x /= 0 && r == 0 then factor d (n + 1) q else (n, x)
diff --git a/source/library/Argo/Type/Decoder.hs b/source/library/Argo/Type/Decoder.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Decoder.hs
+++ /dev/null
@@ -1,92 +0,0 @@
-module Argo.Type.Decoder where
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Vendor.ByteString as ByteString
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Applicative as Applicative
-import qualified Control.Monad as Monad
-import qualified Data.Functor.Identity as Identity
-import qualified Data.Word as Word
-
-type Decoder
-    = Trans.StateT
-          ByteString.ByteString
-          (Trans.ExceptT String Identity.Identity)
-
-unwrap
-    :: Decoder a
-    -> ByteString.ByteString
-    -> Either String (a, ByteString.ByteString)
-unwrap d = Identity.runIdentity . Trans.runExceptT . Trans.runStateT d
-
-run :: Decoder a -> ByteString.ByteString -> Either String a
-run d = fmap fst . unwrap (d <* eof)
-
-list :: Decoder a -> Decoder [a]
-list f = listWith f []
-
-listWith :: Decoder a -> [a] -> Decoder [a]
-listWith f xs = do
-    m <- Applicative.optional $ do
-        Monad.unless (null xs) $ do
-            word8 Literal.comma
-            spaces
-        f
-    case m of
-        Nothing -> pure $ reverse xs
-        Just x -> listWith f $ x : xs
-
-byteString :: ByteString.ByteString -> Decoder ()
-byteString x = do
-    b1 <- Trans.get
-    case ByteString.stripPrefix x b1 of
-        Nothing -> Trans.lift . Trans.throwE $ "byteString: " <> show x
-        Just b2 -> Trans.put b2
-
-dropWhile :: (Word.Word8 -> Bool) -> Decoder ()
-dropWhile f = do
-    b <- Trans.get
-    Trans.put $ ByteString.dropWhile f b
-
-eof :: Decoder ()
-eof = do
-    b <- Trans.get
-    Monad.unless (ByteString.null b) . Trans.lift $ Trans.throwE "eof"
-
-isDigit :: Word.Word8 -> Bool
-isDigit x = Literal.digitZero <= x && x <= Literal.digitNine
-
-isSpace :: Word.Word8 -> Bool
-isSpace x =
-    (x == Literal.space)
-        || (x == Literal.horizontalTabulation)
-        || (x == Literal.newLine)
-        || (x == Literal.carriageReturn)
-
-satisfy :: (Word.Word8 -> Bool) -> Decoder Word.Word8
-satisfy f = do
-    b1 <- Trans.get
-    case ByteString.uncons b1 of
-        Just (x, b2) | f x -> do
-            Trans.put b2
-            pure x
-        _ -> Trans.lift $ Trans.throwE "satisfy"
-
-spaces :: Decoder ()
-spaces = Argo.Type.Decoder.dropWhile isSpace
-
-takeWhile :: (Word.Word8 -> Bool) -> Decoder ByteString.ByteString
-takeWhile f = do
-    b1 <- Trans.get
-    let (x, b2) = ByteString.span f b1
-    Trans.put b2
-    pure x
-
-takeWhile1 :: (Word.Word8 -> Bool) -> Decoder ByteString.ByteString
-takeWhile1 f = do
-    x <- Argo.Type.Decoder.takeWhile f
-    Monad.when (ByteString.null x) . Trans.lift $ Trans.throwE "takeWhile1"
-    pure x
-
-word8 :: Word.Word8 -> Decoder ()
-word8 = Monad.void . satisfy . (==)
diff --git a/source/library/Argo/Type/Encoder.hs b/source/library/Argo/Type/Encoder.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Encoder.hs
+++ /dev/null
@@ -1,61 +0,0 @@
-module Argo.Type.Encoder where
-
-import qualified Argo.Literal as Literal
-import qualified Argo.Type.Config as Config
-import qualified Argo.Type.Indent as Indent
-import qualified Argo.Vendor.Builder as Builder
-import qualified Argo.Vendor.Transformers as Trans
-import qualified Control.Monad as Monad
-import qualified Data.Functor.Identity as Identity
-import qualified Data.Semigroup as Semigroup
-
-type Encoder
-    = Trans.ReaderT
-          Config.Config
-          (Trans.WriterT Builder.Builder Identity.Identity)
-
-unwrap :: Config.Config -> Encoder a -> (a, Builder.Builder)
-unwrap c = Identity.runIdentity . Trans.runWriterT . flip Trans.runReaderT c
-
-run :: Config.Config -> Encoder a -> Builder.Builder
-run c = snd . unwrap c
-
-list
-    :: Encoder ()
-    -> Encoder ()
-    -> Encoder ()
-    -> (a -> Encoder ())
-    -> [a]
-    -> Encoder ()
-list left right separator encode xs = do
-    left
-    case xs of
-        [] -> pure ()
-        x : ys -> do
-            Trans.local Config.increaseLevel $ do
-                maybeIndent <- makeIndent <$> Trans.ask
-                withNewLine maybeIndent
-                encode x
-                Monad.forM_ ys $ \y -> do
-                    separator
-                    withNewLine maybeIndent
-                    encode y
-            maybeIndent <- makeIndent <$> Trans.ask
-            withNewLine maybeIndent
-    right
-
-makeIndent :: Config.Config -> Maybe Builder.Builder
-makeIndent config = case Config.indent config of
-    Indent.Spaces spaces -> if spaces <= 0
-        then Nothing
-        else
-            Just
-            . Semigroup.stimesMonoid (Config.level config * spaces)
-            $ Builder.word8 Literal.space
-    Indent.Tab ->
-        Just . Semigroup.stimesMonoid (Config.level config) $ Builder.word8
-            Literal.horizontalTabulation
-
-withNewLine :: Maybe Builder.Builder -> Encoder ()
-withNewLine = maybe (pure ()) $ Trans.lift . Trans.tell . mappend
-    (Builder.word8 Literal.newLine)
diff --git a/source/library/Argo/Type/Flag.hs b/source/library/Argo/Type/Flag.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Flag.hs
+++ /dev/null
@@ -1,8 +0,0 @@
-module Argo.Type.Flag where
-
-data Flag
-    = Help
-    | Spaces String
-    | Tab
-    | Version
-    deriving (Eq, Show)
diff --git a/source/library/Argo/Type/Indent.hs b/source/library/Argo/Type/Indent.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Indent.hs
+++ /dev/null
@@ -1,6 +0,0 @@
-module Argo.Type.Indent where
-
-data Indent
-    = Spaces Int
-    | Tab
-    deriving (Eq, Show)
diff --git a/source/library/Argo/Type/Nullable.hs b/source/library/Argo/Type/Nullable.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Nullable.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-module Argo.Type.Nullable where
-
-newtype Nullable a
-    = Nullable (Maybe a)
-    deriving (Eq, Show)
-
-fromMaybe :: Maybe a -> Nullable a
-fromMaybe = Nullable
-
-toMaybe :: Nullable a -> Maybe a
-toMaybe (Nullable x) = x
-
-nothing :: Nullable a
-nothing = fromMaybe Nothing
-
-just :: a -> Nullable a
-just = fromMaybe . Just
diff --git a/source/library/Argo/Type/Optional.hs b/source/library/Argo/Type/Optional.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Optional.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-module Argo.Type.Optional where
-
-newtype Optional a
-    = Optional (Maybe a)
-    deriving (Eq, Show)
-
-fromMaybe :: Maybe a -> Optional a
-fromMaybe = Optional
-
-toMaybe :: Optional a -> Maybe a
-toMaybe (Optional x) = x
-
-nothing :: Optional a
-nothing = fromMaybe Nothing
-
-just :: a -> Optional a
-just = fromMaybe . Just
diff --git a/source/library/Argo/Type/Permission.hs b/source/library/Argo/Type/Permission.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Permission.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DeriveLift #-}
-
-module Argo.Type.Permission where
-
-import qualified Argo.Vendor.DeepSeq as DeepSeq
-import qualified Argo.Vendor.TemplateHaskell as TH
-import qualified GHC.Generics as Generics
-
-data Permission
-    = Allow
-    | Forbid
-    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)
-
-toBool :: Permission -> Bool
-toBool x = case x of
-    Allow -> True
-    Forbid -> False
diff --git a/source/library/Argo/Type/Settings.hs b/source/library/Argo/Type/Settings.hs
deleted file mode 100644
--- a/source/library/Argo/Type/Settings.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module Argo.Type.Settings where
-
-import qualified Argo.Type.Flag as Flag
-import qualified Argo.Type.Indent as Indent
-import qualified Text.Read as Read
-
-data Settings = Settings
-    { help :: Bool
-    , indent :: Indent.Indent
-    , version :: Bool
-    }
-    deriving (Eq, Show)
-
-initial :: Settings
-initial = Settings { help = False, indent = Indent.Spaces 0, version = False }
-
-applyFlag :: Settings -> Flag.Flag -> Either String Settings
-applyFlag settings flag = case flag of
-    Flag.Help -> pure settings { help = True }
-    Flag.Spaces string -> do
-        int <- Read.readEither string
-        pure settings { indent = Indent.Spaces int }
-    Flag.Tab -> pure settings { indent = Indent.Tab }
-    Flag.Version -> pure settings { version = True }
diff --git a/source/test-suite/Main.hs b/source/test-suite/Main.hs
--- a/source/test-suite/Main.hs
+++ b/source/test-suite/Main.hs
@@ -7,23 +7,23 @@
 import Test.Tasty.QuickCheck ((===))
 
 import qualified Argo
-import qualified Argo.Class.HasCodec as HasCodec
-import qualified Argo.Codec.Codec as Codec
-import qualified Argo.Codec.Object as Codec
-import qualified Argo.Codec.Value as Codec
-import qualified Argo.Json.Array as Array
-import qualified Argo.Json.Boolean as Boolean
-import qualified Argo.Json.Name as Name
-import qualified Argo.Json.Null as Null
-import qualified Argo.Json.Number as Number
-import qualified Argo.Json.Object as Object
-import qualified Argo.Json.String as String
-import qualified Argo.Schema.Identifier as Identifier
-import qualified Argo.Schema.Schema as Schema
-import qualified Argo.Type.Decimal as Decimal
-import qualified Argo.Type.Nullable as Nullable
-import qualified Argo.Type.Optional as Optional
-import qualified Argo.Type.Permission as Permission
+import qualified Argo.Internal.Class.HasCodec as HasCodec
+import qualified Argo.Internal.Codec.Codec as Codec
+import qualified Argo.Internal.Codec.Object as Codec
+import qualified Argo.Internal.Codec.Value as Codec
+import qualified Argo.Internal.Json.Array as Array
+import qualified Argo.Internal.Json.Boolean as Boolean
+import qualified Argo.Internal.Json.Name as Name
+import qualified Argo.Internal.Json.Null as Null
+import qualified Argo.Internal.Json.Number as Number
+import qualified Argo.Internal.Json.Object as Object
+import qualified Argo.Internal.Json.String as String
+import qualified Argo.Internal.Schema.Identifier as Identifier
+import qualified Argo.Internal.Schema.Schema as Schema
+import qualified Argo.Internal.Type.Decimal as Decimal
+import qualified Argo.Internal.Type.Nullable as Nullable
+import qualified Argo.Internal.Type.Optional as Optional
+import qualified Argo.Internal.Type.Permission as Permission
 import qualified Control.Monad.Trans.Accum as Accum
 import qualified Data.ByteString as ByteString
 import qualified Data.ByteString.Builder as Builder
