diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+# Revision history for mmzk-env
+
+
+## 0.1.0.0 -- 2025-08-03
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Yitang Chen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+# mmzk-env
+
+mmzk-env is a library for reading environment variables into a user-defined data
+type. It provides a type-safe way to parse and validate environment variables,
+ensuring that they conform to the expected types.
+
+## Quick Start
+
+```Haskell
+module Data.Env where
+
+import           Control.Monad.IO.Class
+import           Data.Env.ExtractFields
+import           Data.Env.RecordParser
+import           GHC.Generics
+
+-- | Example: Define an environment schema
+data Config = Config
+    { port :: Int
+    , name :: String
+    , mainHost :: String
+    , debug :: Maybe Bool }
+    deriving (Show, Generic, EnvSchema)
+
+-- | Run the validation
+main :: IO ()
+main = do
+  errOrEnv <- validateEnv @Config
+  case errOrEnv of
+    Left err  -> putStrLn $ "Validation failed: " ++ err
+    Right cfg -> putStrLn $ "Config loaded successfully: " ++ show cfg
+```
+
+With this setup, it requires the environment variables `PORT`, `NAME`, `MAIN_HOST`, and `DEBUG` to be set according to the types defined in the `Config` data type. The library will automatically parse these variables and validate them against the schema.
+
+If any variable is missing or has an incorrect type, the validation will fail, and an error message will be printed.
diff --git a/mmzk-env.cabal b/mmzk-env.cabal
new file mode 100644
--- /dev/null
+++ b/mmzk-env.cabal
@@ -0,0 +1,76 @@
+cabal-version:      2.4
+name:               mmzk-env
+version:            0.1.0.0
+
+synopsis:           Read environment variables into a user-defined data type
+description:
+    mmzk-env is a Haskell library that provides functionality to read environment
+    variables into user-defined data types, allowing for flexible and type-safe
+    configuration management.
+
+homepage:           
+bug-reports:        
+license:            MIT
+author:             Yitang Chen <mmzk1526@outlook.com>
+maintainer:         Yitang Chen <mmzk1526@outlook.com>
+category:            Data, Environment
+extra-source-files:
+    CHANGELOG.md
+    LICENSE
+    README.md
+
+
+common settings
+    ghc-options: -Wall
+    default-extensions:
+        BlockArguments
+        DeriveAnyClass
+        DeriveGeneric
+        FlexibleContexts
+        FlexibleInstances
+        InstanceSigs
+        LambdaCase
+        MultiWayIf
+        NoFieldSelectors
+        OverloadedRecordDot
+        OverloadedStrings
+        PolyKinds
+        ScopedTypeVariables
+        TupleSections
+        TypeApplications
+        TypeFamilies
+        TypeOperators
+    default-language: Haskell2010
+
+
+library
+    import:           settings
+    exposed-modules:
+        Data.Env
+        Data.Env.ExtractFields
+        Data.Env.RecordParser
+        Data.Env.TypeParser
+    build-depends:
+        base >=4.16 && <5,
+        containers >= 0.6.7 && < 0.7,
+        gigaparsec ^>=0.3.1,
+    hs-source-dirs:   src
+
+
+test-suite test
+    import:           settings
+    main-is:          Spec.hs
+    type:             exitcode-stdio-1.0
+    other-modules:
+        Data.Env
+        Data.Env.ExtractFields
+        Data.Env.RecordParser
+        Data.Env.TypeParser
+    build-depends:
+        base >=4.16 && <5,
+        containers,
+        gigaparsec,
+        hspec ^>=2.11,
+    hs-source-dirs:
+        src
+        test
diff --git a/src/Data/Env.hs b/src/Data/Env.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Env.hs
@@ -0,0 +1,28 @@
+-- |
+-- Module      : Data.Env
+-- Description : Environment schema validation
+--
+-- This module provides functionality to validate environment variables against
+-- a schema (a type that implements the `EnvSchema` class).
+module Data.Env (EnvSchema(..)) where
+
+import           Control.Monad.IO.Class
+import           Data.Env.ExtractFields
+import           Data.Env.RecordParser
+
+-- | Type class for validating environment schemas.
+class (ExtractFields a, RecordParser a) => EnvSchema a where
+  -- | Validate the environment variables against the schema, transforming field
+  -- names from camelCase to UPPER_SNAKE_CASE.
+  validateEnv :: MonadIO m => m (Either String a)
+  validateEnv = do
+    envRaw <- getEnvRawCamelCaseToUpperSnake @a
+    return $ parseRecord envRaw
+
+  -- | Validate the environment variables against the schema, allowing for a
+  -- cusutom transformation function to be applied to the field names to match
+  -- with the environment variable names.
+  validateEnvWith :: MonadIO m => (String -> String) -> m (Either String a)
+  validateEnvWith transform = do
+    envRaw <- getEnvRaw @a transform
+    return $ parseRecord envRaw
diff --git a/src/Data/Env/ExtractFields.hs b/src/Data/Env/ExtractFields.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Env/ExtractFields.hs
@@ -0,0 +1,113 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+-- |
+-- Module: Data.Env.ExtractFields
+-- Description: Type class for extracting fields from a record type.
+--
+-- This module provides a type class 'ExtractFields' that extracts field names
+-- from a record type. It also provides functions to retrieve environment
+-- variables based on these field names, with options for different naming
+-- conventions.
+module Data.Env.ExtractFields (
+  ExtractFields,
+  extractFields,
+  getEnvRaw,
+  getEnvRawLowerToUpperSnake,
+  getEnvRawCamelCaseToUpperSnake,
+) where
+
+import           Control.Monad
+import           Control.Monad.IO.Class
+import           Data.Char
+
+import           Data.Maybe
+import           Data.Map (Map)
+import           Data.Proxy
+import           GHC.Generics
+import qualified Data.Map as M
+import           System.Environment
+
+-- | Type class for extracting field names from a record type.
+class ExtractFields a where
+  extractFields' :: Proxy a -> [String]
+
+-- | Extract field names from a record type.
+extractFields :: forall a. ExtractFields a => [String]
+extractFields = extractFields' (Proxy :: Proxy a)
+{-# INLINE extractFields #-}
+
+-- | Retrieve environment variables based on field names, applying a mapping
+-- function to the field names.
+getEnvRaw :: forall a m. (MonadIO m, ExtractFields a)
+          => (String -> String)
+          -> m (Map String String)
+getEnvRaw mapper = liftIO $ M.fromList <$> forM (extractFields @a) \field -> do
+  value <- lookupEnv (mapper field)
+  return (field, fromMaybe "" value)
+{-# INLINE getEnvRaw #-}
+
+-- | Retrieve environment variables based on field names, converting them to
+-- upper case.
+getEnvRawLowerToUpperSnake :: forall a m. (MonadIO m, ExtractFields a)
+                           => m (Map String String)
+getEnvRawLowerToUpperSnake = getEnvRaw @a (map toUpper)
+{-# INLINE getEnvRawLowerToUpperSnake #-}
+
+
+--------------------------------------------------------------------------------
+-- Generic instances
+--------------------------------------------------------------------------------
+
+-- | Retrieve environment variables based on field names, converting them from
+-- camel case (record field naming) to upper snake case (environment variable
+-- naming).
+getEnvRawCamelCaseToUpperSnake :: forall a m. (MonadIO m, ExtractFields a)
+                               => m (Map String String)
+getEnvRawCamelCaseToUpperSnake = getEnvRaw @a camelToUpperSnake
+{-# INLINE getEnvRawCamelCaseToUpperSnake #-}
+
+class GExtractFields f where
+  gExtractFields :: Proxy f -> [String]
+
+instance GExtractFields f => GExtractFields (M1 D c f) where
+  gExtractFields :: Proxy (M1 D c f) -> [String]
+  gExtractFields _ = gExtractFields (Proxy :: Proxy f)
+  {-# INLINE gExtractFields #-}
+
+instance GExtractFields f => GExtractFields (M1 C c f) where
+  gExtractFields :: Proxy (M1 C c f) -> [String]
+  gExtractFields _ = gExtractFields (Proxy :: Proxy f)
+  {-# INLINE gExtractFields #-}
+
+instance (GExtractFields f, GExtractFields g) => GExtractFields (f :*: g) where
+  gExtractFields :: Proxy (f :*: g) -> [String]
+  gExtractFields _ = gExtractFields (Proxy :: Proxy f)
+                  ++ gExtractFields (Proxy :: Proxy g)
+  {-# INLINE gExtractFields #-}
+
+instance (Selector s) => GExtractFields (M1 S s (K1 i a)) where
+  gExtractFields :: Proxy (M1 S s (K1 i a)) -> [String]
+  gExtractFields _ = [selName (undefined :: M1 S s (K1 i a) p)]
+  {-# INLINE gExtractFields #-}
+
+instance (Generic a, GExtractFields (Rep a)) => ExtractFields a where
+  extractFields' :: Proxy a -> [String]
+  extractFields' _ = gExtractFields (Proxy :: Proxy (Rep a))
+  {-# INLINE extractFields' #-}
+
+
+--------------------------------------------------------------------------------
+-- Helpers
+--------------------------------------------------------------------------------
+
+camelToUpperSnake :: String -> String
+camelToUpperSnake = go False
+  where
+    go _ [] = []
+    go prevUpper (x:xs)
+      | x == '_'  = '_' : go True xs
+      | isUpper x =
+          let rest = go True xs
+          in  if prevUpper then toUpper x : rest else '_' : toUpper x : rest
+      | otherwise = toUpper x : go False xs
diff --git a/src/Data/Env/RecordParser.hs b/src/Data/Env/RecordParser.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Env/RecordParser.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+-- |
+-- Module: Data.Env.RecordParser
+-- Description: Type class that provides parsers for records.
+--
+-- This module provides a type class 'RecordParser' that provides parsers for
+-- records. The parsers are used to parse environment variables into records
+-- based on their string representation.
+module Data.Env.RecordParser (
+  RecordParser (..),
+) where
+
+import           Data.Env.TypeParser
+import           Data.Map (Map)
+import           GHC.Generics
+import qualified Data.Map as M
+import           Data.Maybe
+
+-- | Type class for validating environment schemas.
+class RecordParser a where
+  parseRecord :: Map String String -> Either String a
+
+instance (Generic a, GRecordParser (Rep a)) => RecordParser a where
+
+  parseRecord :: (Generic a, GRecordParser (Rep a))
+              => Map String String -> Either String a
+  parseRecord env = to <$> gParseRecord env
+
+
+--------------------------------------------------------------------------------
+-- Generic instances
+--------------------------------------------------------------------------------
+
+-- | Generic validation class.
+class GRecordParser f where
+  gParseRecord :: Map String String -> Either String (f p)
+
+-- | Handle metadata (wrapping fields in `M1`)
+instance GRecordParser f => GRecordParser (M1 D c f) where
+  gParseRecord :: Map String String -> Either String (M1 D c f p)
+  gParseRecord env = M1 <$> gParseRecord env
+
+-- | Handle metadata (wrapping fields in `M1`)
+instance GRecordParser f => GRecordParser (M1 C c f) where
+  gParseRecord :: Map String String -> Either String (M1 C c f p)
+  gParseRecord env = M1 <$> gParseRecord env
+
+-- | Handle multiple fields in a record
+instance (GRecordParser f, GRecordParser g) => GRecordParser (f :*: g) where
+  gParseRecord :: Map String String -> Either String ((f :*: g) p)
+  gParseRecord env = (:*:) <$> gParseRecord env <*> gParseRecord env
+
+-- | Handle individual fields
+instance (TypeParser a, Selector s) => GRecordParser (M1 S s (K1 i a)) where
+  gParseRecord :: Map String String -> Either String (M1 S s (K1 i a) p)
+  gParseRecord env =
+    let key = selName (undefined :: M1 S s (K1 i a) p)
+    in  M1 . K1 <$> case parseType (fromMaybe "" $ M.lookup key env) of
+        Left err  -> Left $ "Field " ++ show key ++ " parsing error:\n" ++ err
+        Right val -> Right val
diff --git a/src/Data/Env/TypeParser.hs b/src/Data/Env/TypeParser.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Env/TypeParser.hs
@@ -0,0 +1,183 @@
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+-- |
+-- Module: Data.Env.TypeParser
+-- Description: Type class that provides parsers for types.
+--
+-- This module provides a type class 'TypeParser' that provides parsers for
+-- different types. The parsers are used to parse environment variables from
+-- their string representation.
+module Data.Env.TypeParser (
+  TypeParser (..),
+) where
+
+import           Data.Int (Int8, Int16, Int32, Int64)
+import           Data.Word (Word8, Word16, Word32, Word64)
+import           GHC.Generics
+import qualified Text.Gigaparsec as P
+import qualified Text.Gigaparsec.Char as P
+import qualified Text.Gigaparsec.Combinator as P
+import qualified Text.Gigaparsec.Errors.ErrorGen as P
+import qualified Text.Gigaparsec.Errors.Combinator as P
+import qualified Text.Gigaparsec.Token.Descriptions as L
+import qualified Text.Gigaparsec.Token.Lexer as L
+
+-- | Type class for parsers associated with types.
+class TypeParser a where
+  -- | parse a value by its string representation.
+  parseType :: String -> Either String a
+
+  default parseType
+    :: (Generic a, GTypeParser (Rep a)) => String -> Either String a
+  parseType s = to <$> gTypeParser s
+  {-# INLINE parseType #-}
+
+-- | Required (non-empty) String field.
+--
+-- in POSIX systems, an empty env variable is equivalent to an undefined env
+-- variable. To ensure consistency across platforms, we require that all
+-- environment variables are non-empty.
+instance TypeParser String where
+  parseType :: String -> Either String String
+  parseType = parse (P.some P.item)
+  {-# INLINE parseType #-}
+
+-- | Required @Integer@ field (parsed from String).
+instance TypeParser Integer where
+  parseType :: String -> Either String Integer
+  parseType = parse (L.decimal integerParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Int@ field (parsed from String).
+instance TypeParser Int where
+  parseType :: String -> Either String Int
+  parseType = (fromInteger <$>) . parse do
+      P.filterSWith (simpleErrorGen "Int out of bound")
+                    validateInt
+                    (L.decimal integerParser)
+    where
+      validateInt n = n >= fromIntegral @Int minBound
+                   && n <= fromIntegral @Int maxBound
+  {-# INLINE parseType #-}
+
+-- | Required @Word@ field (parsed from String).
+instance TypeParser Word where
+  parseType :: String -> Either String Word
+  parseType = (fromInteger <$>) . parse do
+      P.filterSWith (simpleErrorGen "Word out of bound")
+                    validateWord
+                    (L.decimal naturalParser)
+    where
+      validateWord n = n <= fromIntegral @Word maxBound
+  {-# INLINE parseType #-}
+
+-- | Required @Bool@ field (parsed from String).
+instance TypeParser Bool where
+  parseType :: String -> Either String Bool
+  parseType = parse do
+    P.choice [P.string "True" P.$> True, P.string "False" P.$> False]
+  {-# INLINE parseType #-}
+
+-- | Required @Int8@ field (parsed from String).
+instance TypeParser Int8 where
+  parseType :: String -> Either String Int8
+  parseType = parse (L.decimal8 integerParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Int16@ field (parsed from String).
+instance TypeParser Int16 where
+  parseType :: String -> Either String Int16
+  parseType = parse (L.decimal16 integerParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Int32@ field (parsed from String).
+instance TypeParser Int32 where
+  parseType :: String -> Either String Int32
+  parseType = parse (L.decimal32 integerParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Int64@ field (parsed from String).
+instance TypeParser Int64 where
+  parseType :: String -> Either String Int64
+  parseType = parse (L.decimal64 integerParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Word8@ field (parsed from String).
+instance TypeParser Word8 where
+  parseType :: String -> Either String Word8
+  parseType = parse (L.decimal8 naturalParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Word16@ field (parsed from String).
+instance TypeParser Word16 where
+  parseType :: String -> Either String Word16
+  parseType = parse (L.decimal16 naturalParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Word32@ field (parsed from String).
+instance TypeParser Word32 where
+  parseType :: String -> Either String Word32
+  parseType = parse (L.decimal32 naturalParser)
+  {-# INLINE parseType #-}
+
+-- | Required @Word64@ field (parsed from String).
+instance TypeParser Word64 where
+  parseType :: String -> Either String Word64
+  parseType = parse (L.decimal64 naturalParser)
+  {-# INLINE parseType #-}
+
+-- | Required @()@ field (parsed from String).
+instance TypeParser () where
+  parseType :: String -> Either String ()
+  parseType = parse (P.string "()" P.$> ())
+  {-# INLINE parseType #-}
+
+-- | Optional fields (@Maybe a@).
+instance TypeParser a => TypeParser (Maybe a) where
+  parseType :: String -> Either String (Maybe a)
+  parseType "" = Right Nothing
+  parseType s  = Just <$> parseType s
+  {-# INLINE parseType #-}
+
+
+--------------------------------------------------------------------------------
+-- Generic instances
+--------------------------------------------------------------------------------
+
+-- | Generic validation class.
+class GTypeParser f where
+  gTypeParser :: String -> Either String (f p)
+
+
+--------------------------------------------------------------------------------
+-- Helpers
+--------------------------------------------------------------------------------
+
+simpleLexeme :: L.Lexeme
+simpleLexeme = L.nonlexeme (L.mkLexer L.plain)
+{-# INLINE simpleLexeme #-}
+
+integerParser :: L.IntegerParsers L.CanHoldSigned
+integerParser = L.integer simpleLexeme
+{-# INLINE integerParser #-}
+
+naturalParser :: L.IntegerParsers L.CanHoldUnsigned
+naturalParser = L.natural simpleLexeme
+{-# INLINE naturalParser #-}
+
+parseResultToEither :: P.Result String a -> Either String a
+parseResultToEither (P.Failure e) = Left (show e)
+parseResultToEither (P.Success a) = Right a
+{-# INLINE parseResultToEither #-}
+
+parse :: P.Parsec a -> String -> Either String a
+parse parser = parseResultToEither . P.parse (parser >>= (P.eof P.$>))
+{-# INLINE parse #-}
+
+simpleErrorGen :: String -> P.ErrorGen a
+simpleErrorGen msg = case P.vanillaGen of
+  P.VanillaGen {..} -> P.VanillaGen { reason = const (Just msg), .. }
+  impossible        -> impossible
+{-# INLINE simpleErrorGen #-}
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,165 @@
+import           Data.Either (isLeft)
+import           Data.Env.RecordParser
+import           Data.Env.TypeParser
+import           Data.Int (Int8, Int16, Int32, Int64)
+import qualified Data.Map as M
+import           Data.Word (Word8, Word16, Word32, Word64)
+import           GHC.Generics
+import           Test.Hspec
+
+main :: IO ()
+main = do
+  hspec typeParserSpecSpec
+  hspec recordParserSpec
+
+typeParserSpecSpec :: Spec
+typeParserSpecSpec = describe "parseType" do
+  describe "parse String" do
+    it "parses non-empty string" do
+      parseType @String "hello" `shouldBe` Right "hello"
+    it "does not parse empty string" do
+      parseType @String "" `shouldSatisfy` isLeft
+  describe "parse Bool" do
+    it "parses True" do
+      parseType @Bool "True" `shouldBe` Right True
+    it "parses False" do
+      parseType @Bool "False" `shouldBe` Right False
+    it "fails to parse other values" do
+      parseType @Bool "1" `shouldSatisfy` isLeft
+      parseType @Bool "true" `shouldSatisfy` isLeft
+      parseType @Bool "false" `shouldSatisfy` isLeft
+  describe "parse ()" do
+    it "parses ()" do
+      parseType @() "()" `shouldBe` Right ()
+    it "fails to parse other values" do
+      parseType @() "" `shouldSatisfy` isLeft
+      parseType @() "1" `shouldSatisfy` isLeft
+  describe "parse Int8" do
+    it "parses 0" do
+      parseType @Int8 "0" `shouldBe` Right 0
+    it "parses -128" do
+      parseType @Int8 "-128" `shouldBe` Right (-128)
+    it "parses 127" do
+      parseType @Int8 "127" `shouldBe` Right 127
+    it "fails to parse other values" do
+      parseType @Int8 "128" `shouldSatisfy` isLeft
+      parseType @Int8 "-129" `shouldSatisfy` isLeft
+      parseType @Int8 "1.0" `shouldSatisfy` isLeft
+  describe "parse Int16" do
+    it "parses 0" do
+      parseType @Int16 "0" `shouldBe` Right 0
+    it "parses -32768" do
+      parseType @Int16 "-32768" `shouldBe` Right (-32768)
+    it "parses 32767" do
+      parseType @Int16 "32767" `shouldBe` Right 32767
+    it "fails to parse other values" do
+      parseType @Int16 "32768" `shouldSatisfy` isLeft
+      parseType @Int16 "-32769" `shouldSatisfy` isLeft
+      parseType @Int16 "1.0" `shouldSatisfy` isLeft
+  describe "parse Int32" do
+    it "parses 0" do
+      parseType @Int32 "0" `shouldBe` Right 0
+    it "parses -2147483648" do
+      parseType @Int32 "-2147483648" `shouldBe` Right (-2147483648)
+    it "parses 2147483647" do
+      parseType @Int32 "2147483647" `shouldBe` Right 2147483647
+    it "fails to parse other values" do
+      parseType @Int32 "2147483648" `shouldSatisfy` isLeft
+      parseType @Int32 "-2147483649" `shouldSatisfy` isLeft
+      parseType @Int32 "1.0" `shouldSatisfy` isLeft
+  describe "parse Int64" do
+    it "parses 0" do
+      parseType @Int64 "0" `shouldBe` Right 0
+    it "parses -9223372036854775808" do
+      parseType @Int64 "-9223372036854775808" `shouldBe` Right (-9223372036854775808)
+    it "parses 9223372036854775807" do
+      parseType @Int64 "9223372036854775807" `shouldBe` Right 9223372036854775807
+    it "fails to parse other values" do
+      parseType @Int64 "9223372036854775808" `shouldSatisfy` isLeft
+      parseType @Int64 "-9223372036854775809" `shouldSatisfy` isLeft
+      parseType @Int64 "1.0" `shouldSatisfy` isLeft
+  describe "parse Int" do
+    it "parses 0" do
+      parseType @Int "0" `shouldBe` Right 0
+    it "parses min Int" do
+      parseType @Int (show (minBound :: Int)) `shouldBe` Right (minBound :: Int)
+    it "parses max Int" do
+      parseType @Int (show (maxBound :: Int)) `shouldBe` Right (maxBound :: Int)
+    it "fails to parse other values" do
+      parseType @Int (show ((fromIntegral (maxBound :: Int) :: Integer) + 1)) `shouldSatisfy` isLeft
+      parseType @Int (show ((fromIntegral (minBound :: Int) :: Integer) - 1)) `shouldSatisfy` isLeft
+      parseType @Int "1.0" `shouldSatisfy` isLeft
+  describe "parse Word8" do
+    it "parses 0" do
+      parseType @Word8 "0" `shouldBe` Right 0
+    it "parses 255" do
+      parseType @Word8 "255" `shouldBe` Right 255
+    it "fails to parse other values" do
+      parseType @Word8 "256" `shouldSatisfy` isLeft
+      parseType @Word8 "-1" `shouldSatisfy` isLeft
+      parseType @Word8 "1.0" `shouldSatisfy` isLeft
+  describe "parse Word16" do
+    it "parses 0" do
+      parseType @Word16 "0" `shouldBe` Right 0
+    it "parses 65535" do
+      parseType @Word16 "65535" `shouldBe` Right 65535
+    it "fails to parse other values" do
+      parseType @Word16 "65536" `shouldSatisfy` isLeft
+      parseType @Word16 "-1" `shouldSatisfy` isLeft
+      parseType @Word16 "1.0" `shouldSatisfy` isLeft
+  describe "parse Word32" do
+    it "parses 0" do
+      parseType @Word32 "0" `shouldBe` Right 0
+    it "parses 4294967295" do
+      parseType @Word32 "4294967295" `shouldBe` Right 4294967295
+    it "fails to parse other values" do
+      parseType @Word32 "4294967296" `shouldSatisfy` isLeft
+      parseType @Word32 "-1" `shouldSatisfy` isLeft
+      parseType @Word32 "1.0" `shouldSatisfy` isLeft
+  describe "parse Word64" do
+    it "parses 0" do
+      parseType @Word64 "0" `shouldBe` Right 0
+    it "parses 18446744073709551615" do
+      parseType @Word64 "18446744073709551615" `shouldBe` Right 18446744073709551615
+    it "fails to parse other values" do
+      parseType @Word64 "18446744073709551616" `shouldSatisfy` isLeft
+      parseType @Word64 "-1" `shouldSatisfy` isLeft
+      parseType @Word64 "1.0" `shouldSatisfy` isLeft
+  describe "parse Word" do
+    it "parses 0" do
+      parseType @Word "0" `shouldBe` Right 0
+    it "parses max Word" do
+      parseType @Word (show (maxBound :: Word)) `shouldBe` Right (maxBound :: Word)
+    it "fails to parse other values" do
+      parseType @Word (show ((fromIntegral (maxBound :: Word) :: Integer) + 1)) `shouldSatisfy` isLeft
+      parseType @Word "-1" `shouldSatisfy` isLeft
+      parseType @Word "1.0" `shouldSatisfy` isLeft
+  describe "parse Maybe" do
+    it "should success" do
+      parseType @(Maybe Int) "1" `shouldBe` Right (Just 1)
+      parseType @(Maybe Int) "" `shouldBe` Right Nothing
+      parseType @(Maybe String) "hello" `shouldBe` Right (Just "hello")
+      parseType @(Maybe String) "" `shouldBe` Right Nothing
+    it "should fail" do
+      parseType @(Maybe Int) "hello" `shouldSatisfy` isLeft
+      parseType @(Maybe Bool) "fALSE" `shouldSatisfy` isLeft
+
+data Person = Person
+  { name :: String
+  , age  :: Int
+  } deriving (Show, Eq, Generic)
+
+recordParserSpec :: Spec
+recordParserSpec = describe "parseRecord" do
+  it "parses a valid record" do
+    let env = M.fromList [("name", "Alice"), ("age", "30")]
+    parseRecord @Person env `shouldBe` Right (Person "Alice" 30)
+  it "allows extra fields" do
+    let env = M.fromList [("name", "Alice"), ("age", "30"), ("extra", "value")]
+    parseRecord @Person env `shouldBe` Right (Person "Alice" 30)
+  it "fails to parse an invalid record" do
+    let env = M.fromList [("name", "Alice"), ("age", "thirty")]
+    parseRecord @Person env `shouldSatisfy` isLeft
+  it "fails to parse a missing field" do
+    let env = M.fromList [("name", "Alice")]
+    parseRecord @Person env `shouldSatisfy` isLeft
