packages feed

monoidmap-0.0.3.0: src/examples/Examples/RecoveredMap.hs

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- |
-- Copyright: © 2022–2025 Jonathan Knowles
-- License: Apache-2.0
--
-- An ordinary left-biased map similar to 'Map', implemented in terms of
-- 'MonoidMap'.
--
module Examples.RecoveredMap
    ( Map
    , empty
    , singleton
    , fromList
    , toList
    , delete
    , insert
    , keysSet
    , lookup
    , member
    , map
    , mapWithKey
    , mapAccumL
    , mapAccumLWithKey
    , mapAccumR
    , mapAccumRWithKey
    )
    where

import Prelude hiding
    ( lookup, map )

import Control.DeepSeq
    ( NFData )
import Data.Coerce
    ( coerce )
import Data.Maybe
    ( mapMaybe )
import Data.Monoid
    ( First (..) )
import Data.MonoidMap
    ( MonoidMap )
import Data.Semigroup
    ( Semigroup (stimes), stimesIdempotentMonoid )
import Data.Set
    ( Set )

import qualified Data.MonoidMap as MonoidMap

newtype Map k v = Map
    --  'First' is used to mimic the left-biased nature of 'Data.Map':
    {unMap :: MonoidMap k (First v)}
    deriving newtype (Eq, NFData, Monoid)

instance Ord k => Semigroup (Map k v) where
    (<>) = coerce @(MonoidMap k (First v) -> _ -> _) (<>)
    stimes = stimesIdempotentMonoid

instance (Show k, Show v) => Show (Map k v) where
    show = ("fromList " <>) . show . toList

instance Functor (Map k) where
    fmap = map

empty :: Map k v
empty = Map MonoidMap.empty

singleton :: Ord k => k -> v -> Map k v
singleton k = Map . MonoidMap.singleton k . pure

fromList :: Ord k => [(k, v)] -> Map k v
fromList = Map . MonoidMap.fromListWith (const id) . fmap (fmap pure)

toList :: Map k v -> [(k, v)]
toList = mapMaybe (getFirst . sequenceA) . MonoidMap.toList . unMap

delete :: Ord k => k -> Map k v -> Map k v
delete k = Map . MonoidMap.nullify k . unMap

insert :: Ord k => k -> v -> Map k v -> Map k v
insert k v = Map . MonoidMap.set k (pure v) . unMap

keysSet :: Map k v -> Set k
keysSet = MonoidMap.nonNullKeys . unMap

lookup :: Ord k => k -> Map k v -> Maybe v
lookup k = getFirst . MonoidMap.get k . unMap

member :: Ord k => k -> Map k v -> Bool
member k = MonoidMap.nonNullKey k . unMap

map :: (v1 -> v2) -> Map k v1 -> Map k v2
map f = Map . MonoidMap.map (fmap f) . unMap

mapWithKey :: (k -> v1 -> v2) -> Map k v1 -> Map k v2
mapWithKey f = Map . MonoidMap.mapWithKey (fmap . f) . unMap

mapAccumL :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
mapAccumL f s m = Map <$> MonoidMap.mapAccumL (accum f) s (unMap m)

mapAccumR :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
mapAccumR f s m = Map <$> MonoidMap.mapAccumR (accum f) s (unMap m)

mapAccumLWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
mapAccumLWithKey f s m =
    Map <$> MonoidMap.mapAccumLWithKey (accumWithKey f) s (unMap m)

mapAccumRWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
mapAccumRWithKey f s m =
    Map <$> MonoidMap.mapAccumRWithKey (accumWithKey f) s (unMap m)

--------------------------------------------------------------------------------
-- Utilities
--------------------------------------------------------------------------------

accum :: (s -> v1 -> (s, v2)) -> s -> First v1 -> (s, First v2)
accum f s1 (First mv1) = case mv1 of
    Just v1 -> let (s2, v2) = f s1 v1 in (s2, First (Just v2))
    Nothing -> (s1, First Nothing)

accumWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> k -> First v1 -> (s, First v2)
accumWithKey f s1 k (First mv1) = case mv1 of
    Just v1 -> let (s2, v2) = f s1 k v1 in (s2, First (Just v2))
    Nothing -> (s1, First Nothing)