content: {-# LANGUAGE ExistentialQuantification, ConstraintKinds, KindSignatures, GADTs, ScopedTypeVariables, Rank2Types #-}
  
  module General.TypeMap(
      Map, empty, singleton, insert, map, lookup, unionWith, toList, size
      ) where
  
  import qualified Data.HashMap.Strict as Map
  import Data.Typeable
  import Unsafe.Coerce
  import Data.Functor
  import qualified Prelude
  import Prelude hiding (lookup, map)
  
  
  data F f = forall a . F !(f a)
  
  unF :: F f -> f a
  unF x = case x of F x -> unsafeCoerce x
  
  newtype Map f = Map (Map.HashMap TypeRep (F f))
  
  empty :: Map f
  empty = Map Map.empty
  
  singleton :: Typeable a => f a -> Map f
  singleton x = Map $ Map.singleton (typeRep x) (F x)
  
  insert :: Typeable a => f a -> Map f -> Map f
  insert x (Map mp) = Map $ Map.insert (typeRep x) (F x) mp
  
  lookup :: forall a f . Typeable a => Map f -> Maybe (f a)
  lookup (Map mp) = unF <$> Map.lookup (typeRep (Proxy :: Proxy a)) mp
  
  unionWith :: (forall a . f a -> f a -> f a) -> Map f -> Map f -> Map f
  unionWith f (Map mp1) (Map mp2) = Map $ Map.unionWith (\x1 x2 -> F $ f (unF x1) (unF x2)) mp1 mp2
  
  map :: (forall a . f1 a -> f2 a) -> Map f1 -> Map f2
  map f (Map mp) = Map $ Map.map (\(F a) -> F $ f a) mp
  
  toList :: (forall a . f a -> b) -> Map f -> [b]
  toList f (Map mp) = Prelude.map (\(F a) -> f a) $ Map.elems mp
  
  size :: Map f -> Int
  size (Map mp) = Map.size mp
  