th-serde-0.1.3.0: A serialization/deserialization framework for Haskell
Safe HaskellNone
LanguageGHC2021

Data.Serde.QQ

Description

The main stable interface for the th-serde library, providing QuasiQuoters for defining data types with separated serialization and validation logic. Other modules are considered internal and may change without notice.

Overview

This library helps separate your core data models from their serialization and validation logic. While newtypes are commonly used for this purpose, they become unwieldy with complex data types, especially when dealing with multiple fields that need validation or custom serialization.

How It Works

Given a data type definition with 'via' annotations, th-serde:

  1. Generates your core data type with clean, simple fields
  2. Creates a "shadow" type with the validation/serialization wrappers
  3. Provides machinery to convert between them using coerce

Important: Shadow types are generated without any type class implementations. You must implement all needed type classes for shadow types using runuserprep. The QuasiQuoter never automatically derives any instances for shadow types.

Example

Here's a complete example:

[serde|
.derive
  Eq Ord Show Read

-- Your core data type with validation annotations
-- Suppose Age, VerifyLength, and VerifyEmail are defined elsewhere
data Person
  age :: Int32 via Age                     -- Validate using Age newtype
  name :: String via VerifyLength 1 10     -- Must be 1-10 chars
  email :: String via VerifyEmail          -- Must be valid email
|]

This generates:

-- Your clean business model
data Person = Person
  { age :: Int32    -- Clean types without validation wrappers
  , name :: String
  , email :: String
  } deriving (Eq, Ord, Show, Read)

-- Auto-generated shadow type for validation
data Person__ = Person__
  { age__ :: Age               -- Fields use validation wrappers
  , name__ :: VerifyLength 1 10
  , email__ :: VerifyEmail
  }

Usage Pattern

  1. Define your types using the serde QuasiQuoter
  2. Call runusercoercion to implement validation/serialization

Syntax Reference

Deriving Classes

Use .derive at the start to specify which classes to derive:

.derive
  Eq Ord Show Read  -- These will be derived for all types except shadow types

Data Types

data Person                    -- Regular data type
  field :: Type via Validator  -- Field with validation
  plain :: Type                -- Field without validation

Newtypes

Unlike data types, newtypes using via (either in field or type position) will use GHC's DerivingVia mechanism directly instead of creating shadow types. This requires the DerivingVia language extension to be enabled.

newtype Age        -- Simple newtype without via
  value :: Int32   -- Regular field, no shadow type created

newtype Number Double

newtype Validated Int32 via Check  -- With validation
-- ^ Generates:
--   newtype Validated = Validated Int32
--     deriving (...) via Check

newtype Name       -- With record syntax + via
  getName :: String via Verify
-- ^ Generates:
--   newtype Name = Name { getName :: String }
--     deriving (...) via Verify

Type Aliases

Type aliases do not participate in the derivation process and are not shadowed.

type EmailStr String

Examples

Examples can be found in the test suite.

Synopsis

Documentation

serde :: QuasiQuoter Source #

quasi-quoter for th-serde

data RunUserCoercion Source #

arguments to user code that generates coercions

Constructors

RunUserCoercion 

Fields

runusercoercion Source #

Arguments

:: (RunUserCoercion -> Q [Dec])

generate coercions between shadow and regular data types

-> (Name -> Q [Dec])

derive coercions for shadow data, regular data with no shadows, and newtypes

-> [Name]

preparations for shadow types

-> Q [Dec]

generated coercions

using the stored state (from last quasi-quote run), run user code to generate coercions