packages feed

large-records-0.1.0.0: src/Data/Record/Internal/CodeGen.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Code generation shared by TH and QQ
--
-- Since these can also be used by QQ, these functions cannot take 'Options'.
module Data.Record.Internal.CodeGen (
    -- * Records
    recordTypeE
  , recordConstrE
  , recordTypeT
  , recordToVectorE
  , recordFromVectorDontForceE
  , recordIndexedAccessorE
  , recordIndexedOverwriteE
  , recordUndefinedValueE
    -- * Fields
  , fieldNameE
  , fieldNameT
  , fieldTypeT
  , fieldIndexE
  , fieldUntypedAccessorE
  , fieldUntypedOverwriteE
  ) where

import Language.Haskell.TH

import Data.Record.Internal.Naming
import Data.Record.Internal.Record
import Data.Record.Internal.TH.Util

import qualified Data.Record.Internal.TH.Name as N hiding (unqualified)

{-------------------------------------------------------------------------------
  Records
-------------------------------------------------------------------------------}

-- | Name of the record as a term-level literal
recordTypeE :: Record a -> Q Exp
recordTypeE = stringE . recordType

-- | Name of the constructor as a term-level literal
recordConstrE :: Record a -> Q Exp
recordConstrE = stringE . recordConstr

-- | The saturated type of the record (that is, with all type vars applied)
recordTypeT :: N.Qualifier -> Record a -> Q Type
recordTypeT qual Record{..} =
    appsT (N.conT (N.qualify qual recordType)) $ map tyVarType recordTVars

-- | Coerce the record to the underlying @Vector Any@
recordToVectorE :: N.Qualifier -> Record a -> Q Exp
recordToVectorE qual =
    N.varE . N.qualify qual . nameRecordInternalField . recordType

-- | Construct record from the underlying @Vector Any@
--
-- This doesn't force any elements in the vector, so this can be used if
--
-- * the record has lazy fields, or
-- * we know through other means that all values are already forced.
--
-- See also 'recordFromVectorForceE'.
recordFromVectorDontForceE :: N.Qualifier -> Record a -> Q Exp
recordFromVectorDontForceE qual =
    N.conE . N.qualify qual . nameRecordInternalConstr . recordConstr

-- | The (unsafe) indexed field accessor
recordIndexedAccessorE :: N.Qualifier -> Record a -> Q Exp
recordIndexedAccessorE qual =
    N.varE . N.qualify qual . nameRecordIndexedAccessor . recordType

-- | The (unsafe) indexed field overwrite
recordIndexedOverwriteE :: N.Qualifier -> Record a -> Q Exp
recordIndexedOverwriteE qual =
    N.varE . N.qualify qual . nameRecordIndexedOverwrite . recordType

recordUndefinedValueE :: N.Qualifier -> Record a -> Q Exp
recordUndefinedValueE qual r =
    [| $(recordFromVectorDontForceE qual r) undefined |]

{-------------------------------------------------------------------------------
  Record fields
-------------------------------------------------------------------------------}

-- | Name of the field as a term-level literal
fieldNameE :: Field a -> Q Exp
fieldNameE = stringE . fieldName

-- | Name of the field as a type-level literal
fieldNameT :: Field a -> Q Type
fieldNameT = litT . strTyLit . fieldName

-- | Type of the field
fieldTypeT :: Field a -> Q Type
fieldTypeT Field{..} = return fieldType

-- | Index of the field
fieldIndexE :: Field a -> Q Exp
fieldIndexE Field{..} = litE . integerL $ fromIntegral fieldIndex

-- | The indexed field accessor, applied to this field
fieldUntypedAccessorE :: N.Qualifier -> Record a -> Field a -> Q Exp
fieldUntypedAccessorE qual r f =
    [| $(recordIndexedAccessorE qual r) $(fieldIndexE f) |]

-- | The indexed field overwrite, applied to this field
fieldUntypedOverwriteE :: N.Qualifier -> Record a -> Field a -> Q Exp
fieldUntypedOverwriteE qual r f =
    [| $(recordIndexedOverwriteE qual r) $(fieldIndexE f) |]