packages feed

packed-data-0.1.0.1: src/Data/Packed/TH/Write.hs

module Data.Packed.TH.Write (genWrite, writeFName) where

import Data.Packed.Needs
import Data.Packed.Packable
import Data.Packed.TH.Flag (PackingFlag)
import Data.Packed.TH.Utils
import Data.Packed.TH.WriteCon
import Language.Haskell.TH

-- For a data type 'Tree', will generate the function name 'writeTree'
writeFName :: Name -> Name
writeFName tyName = mkName $ "write" ++ sanitizeConName tyName

-- | Generates a function that serialises and writes a value into a 'Needs'
--
-- The function simply calls the functions generated by 'Data.Packed.TH.genConWrite'
--
-- __Example:__
--
-- For the 'Tree' data type, it generates the following function
--
-- @
-- writeTree :: ('Packable' a) => Tree a -> 'NeedsWriter' (Tree a) r t
-- writeTree (Leaf n) = writeConLeaf n
-- writeTree (Node l r) = writeConNode l r
-- @
genWrite ::
    [PackingFlag] ->
    -- | The name of the type to generate the function for
    Name ->
    Q [Dec]
genWrite flags tyName = do
    (TyConI (DataD _ _ _ _ cs _)) <- reify tyName
    -- For each data constructor, we generate the corresponding clause
    clauses <-
        mapM
            ( \con -> do
                let (conName, types) = getNameAndBangTypesFromCon con
                -- Generate names for each variable in the constructor
                paramNames <- mapM (const $ newName "t") types
                -- We apply each parameter of the constructor and the 'Needs' to the 'writeConXXX' function
                body <- foldl (\f arg -> [|$f $(varE arg)|]) (varE $ conWriteFName conName) paramNames
                return $ Clause [ConP conName [] (VarP <$> paramNames)] (NormalB body) []
            )
            cs
    -- For each of the data constructor of the type, we generate the corresponding `writeConXXX`
    -- We define the Tag using the index of the data constructor
    conWriter <-
        mapM
            ( \(index, constructor) ->
                let (conName, types) = getNameAndBangTypesFromCon constructor
                 in genConWrite flags conName index types
            )
            $ zip [0 ..] cs
    signature <- genWriteSignature tyName
    return $ concat conWriter ++ [signature, FunD (writeFName tyName) clauses]

-- Generates the following function signature for a data type 'Tree'
-- writeTree :: ('Packable' a) => Tree a -> 'NeedsWriter' (Tree a) r t
genWriteSignature :: Name -> Q Dec
genWriteSignature tyName = do
    (sourceType, typeParameterNames) <- resolveAppliedType tyName
    let fName = writeFName tyName
        -- Type variables for Needs
        r = varT $ mkName "r"
        t = varT $ mkName "t"
        -- Define Packable constraints on each of the type parameters
        constraints = mapM (\tyVarName -> [t|Packable $(varT tyVarName)|]) typeParameterNames
        signature = [t|$(return sourceType) -> NeedsWriter $(return sourceType) $r $t|]
    sigD fName (forallT [] constraints signature)