    {-# LANGUAGE TypeFamilies, FlexibleInstances #-}
    module Data.Interned.Internal.String
      ( InternedString(..)
      ) where

    import Data.String
    import Data.Interned
    import Data.Hashable
    import Data.Foldable

    data InternedString = IS
      { internedStringId :: {-# UNPACK #-} !Id
      , uninternString :: String
      }

    instance IsString InternedString where
      fromString = intern

    instance Eq InternedString where
      IS i _ == IS j _ = i == j

    instance Ord InternedString where
      compare (IS i _) (IS j _) = compare i j

    instance Show InternedString where
      showsPrec d (IS _ b) = showsPrec d b

    instance Interned InternedString where
      type Uninterned InternedString = String
      data Description InternedString = Cons {-# UNPACK #-} !Char String | Nil
        deriving (Eq)
      describe (c:cs) = Cons c cs
      describe []     = Nil
      identify = IS
      cache = stringCache

    instance Uninternable InternedString where
      unintern = uninternString

    instance Hashable (Description InternedString) where
      hashWithSalt s (Cons c cs) = foldl' hashWithSalt (hashWithSalt s c) cs
      hashWithSalt s Nil         = s `hashWithSalt` (0 :: Int)

    stringCache :: Cache InternedString
    stringCache = mkCache
    {-# NOINLINE stringCache #-}