{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE TypeFamilies #-} -- ---------------------------------------------------------------------------- {- | Heterogeneous indexes using @ExistentialQuantification@. Indexes have to implement the 'Index' type class as well as obey other constraints defined in 'IndexImplCon'. /Note/: Due to the nature of @ExistentialQuantification@, deserialization is tricky because the concrete implementation is not known. This is circumvented by storing the type representation of the index. Deserialization is possible if there is a matching type representation in the set of available types. This requires the use of the custom get functions. -} -- ---------------------------------------------------------------------------- module Hunt.Index.IndexImpl where import Control.Applicative ((<$>)) import Control.DeepSeq import Control.Monad import Data.Binary import qualified Data.List as L import Data.Text (Text) import Data.Text.Binary () import Data.Typeable import Data.Typeable.Binary () import Hunt.Common.BasicTypes import Hunt.Common.IntermediateValue import Hunt.Index -- ------------------------------------------------------------ -- | Constraint for index implementations. type IndexImplCon i = ( Index i , Show i , ICon i , IndexValue (IVal i) , Binary i , Typeable i , IKey i ~ Text ) -- ------------------------------------------------------------ -- | Index using @ExistentialQuantification@ to allow heterogeneous index containers. data IndexImpl = forall i. IndexImplCon i => IndexImpl { ixImpl :: i } -- ------------------------------------------------------------ instance Show IndexImpl where show (IndexImpl v) = show v -- FIXME: not 'rnf v `seq` ()'. is it supposed to be that way? instance NFData IndexImpl where rnf (IndexImpl v) = v `seq` () -- ------------------------------------------------------------ -- Serialization -- | FIXME: actually implement instance instance Binary IndexImpl where put (IndexImpl i) = put (typeOf i) >> put i get = error "existential types cannot be deserialized this way. Use special get' functions" -- TODO: refactor -- | Deserialize a set of 'IndexImpl's. Requires a set of available index implementations. -- -- /Note/: This will fail if a used index implementation is not provided. gets' :: [IndexImpl] -> Get [(Context, IndexImpl)] gets' ts = do n <- get :: Get Int go [] n where go xs 0 = return $! reverse xs go xs i = do x <- liftM2 (,) get (get' ts) x `seq` go (x:xs) (i-1) -- | Deserialize an 'IndexImpl'. Requires a set of available index implementations. -- -- /Note/: This will fail if a used index implementation is not provided. get' :: [IndexImpl] -> Get (IndexImpl) get' ts = do t <- get :: Get TypeRep case L.find (\(IndexImpl i) -> t == typeOf i) ts of Just (IndexImpl x) -> IndexImpl <$> get `asTypeOf` return x Nothing -> error $ "Unable to load index of type: " -- ++ show t -- ------------------------------------------------------------ -- | Wrap an index using @ExistentialQuantification@ to allow heterogeneous containers. mkIndex :: IndexImplCon i => i -> IndexImpl mkIndex i = IndexImpl $! i -- ------------------------------------------------------------