{-|
    This module provides support for optionality records.

    Optionality records are used to specify interfaces with optional input data. Compared to an
    ordinary record type, an optionality record type states for every field whether it is required
    or optional. This is done by a slight abuse of field names. A field name @/name/@ is replaced by
    either @'Req' /name/@ or @'Opt' /name/@. Optionality record types are never used directly in
    types of actual values. Instead, they are converted into ordinary record types with the type
    functions 'All' and 'Required'.
-}
module Data.Record.Optionality (

    Req,
    Opt,
    OptRecord (type All, type Required)

) where

    -- Data
    import Data.Record as Record

    -- |A marker for required fields.
    data Req name

    -- |A marker for optional fields.
    data Opt name

    -- |The class of all optionality record types.
    class OptRecord (optRecord :: * -> *) where

        {-|
            Converts an optionality record type into an ordinary record type by dropping all 'Req'
            and 'Opt' annotations.
        -}
        type All optRecord :: * -> *

        {-|
            Extracts all required fields from an optionality record type and drops their 'Req'
            annotations.
        -}
        type Required optRecord :: * -> *

    instance OptRecord X where

        type All X = X

        type Required X = X

    instance (OptRecord optRecord) => OptRecord (optRecord :& Req name ::: sort) where

        type All (optRecord :& Req name ::: sort) = All optRecord :& name ::: sort

        type Required (optRecord :& Req name ::: sort) = Required optRecord :& name ::: sort

    instance (OptRecord optRecord) => OptRecord (optRecord :& Opt name ::: sort) where

        type All (optRecord :& Opt name ::: sort) = All optRecord :& name ::: sort

        type Required (optRecord :& Opt name ::: sort) = Required optRecord