packages feed

packed-data-0.1.0.0: src/Data/Packed/TH/PackCon.hs

module Data.Packed.TH.PackCon (genConstructorPackers) where

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

-- | Generates a function that serialises an applied data constructor
--
-- The function calls the functions generated by 'Data.Packed.TH.genConWrite'
--
-- __Example:__
--
-- For the 'Tree' data type, it generates the following functions
--
-- @
-- packLeaf :: ('Packable' a) => a -> 'Data.Packed' '[Tree a]
-- packLeaf n = 'finish' ('withEmptyNeeds' (writeLeaf n))
--
-- packNode :: ('Packable' a) => Tree a -> Tree a -> 'Data.Packed' '[Tree a]
-- packNode t1 t2 = 'finish' ('withEmptyNeeds' (writeNode t1 t2))
-- @
genConstructorPackers :: [PackingFlag] -> Name -> Q [Dec]
genConstructorPackers flags tyName = do
    (TyConI (DataD _ _ _ _ cs _)) <- reify tyName
    packers <-
        mapM
            ( \con ->
                let (conName, bt) = getNameAndBangTypesFromCon con
                 in genConstructorPacker flags conName (snd <$> bt)
            )
            cs
    return $ concat packers

packConFName :: Name -> Name
packConFName conName = mkName $ "pack" ++ sanitizeConName conName

genConstructorPacker :: [PackingFlag] -> Name -> [Type] -> Q [Dec]
genConstructorPacker flags conName argTypes = do
    varNames <- mapM (\_ -> newName "t") argTypes
    writeExp <- (foldl (\rest p -> appE rest (varE p)) (varE $ conWriteFName conName) varNames)
    body <- [|finish (withEmptyNeeds $(return writeExp))|]
    signature <- genConstructorPackerSig flags conName argTypes
    return
        [ signature
        , FunD (packConFName conName) [Clause (VarP <$> varNames) (NormalB body) []]
        ]

genConstructorPackerSig :: [PackingFlag] -> Name -> [Type] -> Q Dec
genConstructorPackerSig _ conName argTypes = do
    (DataConI _ _ tyName) <- reify conName
    (ty, typeParameterNames) <- resolveAppliedType tyName
    constraints <- mapM (\tyVarName -> [t|Packable $(varT tyVarName)|]) typeParameterNames
    signature <- foldr (\p rest -> [t|$(return p) -> $rest|]) [t|Packed '[$(return ty)]|] argTypes
    return $ SigD (packConFName conName) $ ForallT [] constraints signature