{-# LANGUAGE ConstraintKinds,CPP,ScopedTypeVariables #-} module Data.Factory where import Control.Lens import Control.Monad import Data.Existential import Data.HashMap.Strict as M (HashMap,fromList,lookup,member) import Data.Proxy import Data.Proxy.TH import Data.Serialize import Data.Typeable import Data.Serialize.Instances () import Language.Haskell.TH import Language.Haskell.TH.Lens import Text.Printf.TH -- newtype Factory constr = Factory -- { runFactory :: forall r. TypeRep -> (forall a. (constr a, Typeable a) => Proxy a -> r) -> Maybe r } type Factory constr = HashMap TypeRep (Cell1 Proxy constr) runFactory :: Factory constr -> TypeRep -> Maybe (Cell1 Proxy constr) runFactory m trep = M.lookup trep m class HasFactory constr where factory :: Proxy constr -> Factory constr makeFactory :: Name -> DecsQ makeFactory n = do t <- varT $ mkName "a" ts <- reifyInstances n [t] tableName <- newName $ "table_" ++ nameBase n let ts' = filter (null.view _1) (ts^.instances) ^. types instances = partsOf (traverse._InstanceD) #if MIN_VERSION_template_haskell(2,11,0) types = partsOf (traverse._3._AppT._2) #else types = partsOf (traverse._2._AppT._2) #endif sig = sigD tableName [t|Factory $(conT n)|] table = [e| fromList $(listE $ map pair ts') |] proxy :: Type -> ExpQ proxy t = [e| Proxy :: Proxy $(return t) |] pair :: Type -> ExpQ pair t = [e| (typeRep $(proxy t),Cell $ $(proxy t)) |] -- _ = ts' :: _ dec = valD (varP tableName) (normalB table) [] concat <$> sequenceA [ sequenceA [sig,dec] , [d| instance HasFactory $(conT n) where factory Proxy = $(varE tableName) |] ] -- [d| $(listE $ map (stringE . pprint) ts) |] -- | -- = Serialize Cells putCell1 :: forall constr f. HasFactory constr => (forall a. constr a => Putter (f a)) -> Putter (Cell1 f constr) putCell1 putA (Cell x) = do let tr = [pr|constr|] unless (typeRep x `M.member` factory tr) $ fail $ [s|%s is not available in Factory (%s)|] (show $ typeRep x) (show tr) put (typeRep x) putA x getCell1 :: forall constr f. HasFactory constr => (forall a. constr a => Get (f a)) -> Get (Cell1 f constr) getCell1 f = do t <- get let _ = t :: TypeRep f' :: forall a. constr a => Proxy a -> Get (f a) f' _ = f maybe mzero (readCell1 $ fmap Cell . f') $ runFactory (factory [pr|constr|]) t