{-# LANGUAGE StandaloneDeriving, FlexibleContexts, UndecidableInstances #-}

-- | Like 'Map' but with a 'Monoid' instance that respects the value type's
-- 'Semigroup' instance.

module Data.Yoko.MinCtors.MMap where

import Data.Monoid

import Data.Semigroup (Semigroup)
import qualified Data.Semigroup as Semigroup

import Data.Map (Map)
import qualified Data.Map as Map

import qualified Data.Foldable as F



newtype MMap k f v = MMap {unMMap :: Map k (f v)}

deriving instance Eq (Map k (f v)) => Eq (MMap k f v)
deriving instance Show (Map k (f v)) => Show (MMap k f v)

map f (MMap m) = MMap $ Map.map f m

singleton k v = MMap $ Map.singleton k v

null (MMap m) = Map.null m

empty :: MMap k f v
empty = MMap Map.empty

instance (Ord k, Semigroup (f v)) => Monoid (MMap k f v) where
  mempty = MMap Map.empty
  MMap x `mappend` MMap y = MMap $ Map.unionWith (Semigroup.<>) x y

foldMap :: Monoid m => (k -> f v -> m) -> MMap k f v -> m
foldMap f (MMap m) = F.foldMap (uncurry f) $ Map.toList m

mapWithMonoKeys :: (k -> k1) -> (f v -> g v1) -> MMap k f v -> MMap k1 g v1
mapWithMonoKeys fk fv (MMap m) =
  MMap $ Map.mapKeysMonotonic fk $ Map.map fv m

lookup :: Ord k => k -> MMap k f v -> Maybe (f v)
lookup k (MMap m) = Map.lookup k m