content: {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE GADTs #-}
  
  -- From https://www.haskell.org/haskellwiki/GHC/Type_families#An_associated_data_type_example
  
  import qualified Data.IntMap
  import Prelude hiding (lookup)
  import Data.Char (ord)
  
  class GMapKey k where
    data GMap k :: * -> *
    empty       :: GMap k v
    lookup      :: k -> GMap k v -> Maybe v
    insert      :: k -> v -> GMap k v -> GMap k v
  
  -- An Int instance
  instance GMapKey Int where
    data GMap Int v        = GMapInt (Data.IntMap.IntMap v)
    empty                  = GMapInt Data.IntMap.empty
    lookup k   (GMapInt m) = Data.IntMap.lookup k m
    insert k v (GMapInt m) = GMapInt (Data.IntMap.insert k v m)
  
  -- A Char instance
  instance GMapKey Char where
    data GMap Char v        = GMapChar (GMap Int v)
    empty                   = GMapChar empty
    lookup k (GMapChar m)   = lookup (ord k) m
    insert k v (GMapChar m) = GMapChar (insert (ord k) v m)
  
  -- A Unit instance
  instance GMapKey () where
    data GMap () v           = GMapUnit (Maybe v)
    empty                    = GMapUnit Nothing
    lookup () (GMapUnit v)   = v
    insert () v (GMapUnit _) = GMapUnit $ Just v
  
  -- Product and sum instances
  instance (GMapKey a, GMapKey b) => GMapKey (a, b) where
    data GMap (a, b) v            = GMapPair (GMap a (GMap b v))
    empty                         = GMapPair empty
    lookup (a, b) (GMapPair gm)   = lookup a gm >>= lookup b
    insert (a, b) v (GMapPair gm) = GMapPair $ case lookup a gm of
                                      Nothing  -> insert a (insert b v empty) gm
                                      Just gm2 -> insert a (insert b v gm2  ) gm
  
  instance (GMapKey a, GMapKey b) => GMapKey (Either a b) where
    data GMap (Either a b) v                = GMapEither (GMap a v) (GMap b v)
    empty                                   = GMapEither empty empty
    lookup (Left  a) (GMapEither gm1  _gm2) = lookup a gm1
    lookup (Right b) (GMapEither _gm1 gm2 ) = lookup b gm2
    insert (Left  a) v (GMapEither gm1 gm2) = GMapEither (insert a v gm1) gm2
    insert (Right b) v (GMapEither gm1 gm2) = GMapEither gm1 (insert b v gm2)
  
  myGMap :: GMap (Int, Either Char ()) String
  myGMap = insert (5, Left 'c') "(5, Left 'c')"    $
           insert (4, Right ()) "(4, Right ())"    $
           insert (5, Right ()) "This is the one!" $
           insert (5, Right ()) "This is the two!" $
           insert (6, Right ()) "(6, Right ())"    $
           insert (5, Left 'a') "(5, Left 'a')"    $
           empty
  
  main = putStrLn $ maybe "Couldn't find key!" id $ lookup (5, Right ()) myGMap
  
  -- (Type) Synonym Family
  
  type family Elem c
  
  type instance Elem [e] = e
  
  -- type instance Elem BitSet = Char
  
  
  data family T a
  data    instance T Int  = T1 Int | T2 Bool
  newtype instance T Char = TC Bool
  
  data family G a b
  data instance G [a] b where
     G1 :: c -> G [Int] b
     G2 :: G [a] Bool
  