packages feed

hashmap-io-0.1.0.0: src/Data/IOHashMap/STM.hs

module Data.IOHashMap.STM
  ( IOHashMap
  , newIOHashMap
  , readIOHashMap
  , modifyIOHashMap
  , empty
  , singleton
  , null
  , size
  , member
  , lookup
  , (!?)
  , findWithDefault
  , (!)
  , insert
  , insertWith
  , delete
  , adjust
  , update
  , alter
  , foldMapWithKey
  , foldr
  , foldl
  , foldr'
  , foldl'
  , foldrWithKey'
  , foldlWithKey'
  , foldrWithKey
  , foldlWithKey
  , keys
  , elems
  , toList
  , fromList
  ) where

import           Control.Concurrent.STM (STM, TVar, modifyTVar', newTVar,
                                         readTVar)
import           Data.HashMap.Strict    (HashMap)
import qualified Data.HashMap.Strict    as HM
import           Data.Hashable
import           Prelude                hiding (foldl, foldr, lookup, null)

newtype IOHashMap k v = IOHashMap (TVar (HashMap k v))

-- start helpers
newIOHashMap :: HashMap k v -> STM (IOHashMap k v)
newIOHashMap h = IOHashMap <$> newTVar h

readIOHashMap :: (HashMap k v -> a) -> IOHashMap k v -> STM a
readIOHashMap f (IOHashMap h) = f <$> readTVar h

modifyIOHashMap :: (HashMap k v -> HashMap k v) -> IOHashMap k v -> STM ()
modifyIOHashMap f (IOHashMap h) = modifyTVar' h f
-- end helpers

------------------------------------------------------------------------
-- * Construction

-- | /O(1)/ Construct an empty map.
empty :: STM (IOHashMap k v)
empty = newIOHashMap HM.empty

-- | /O(1)/ Construct a map with a single element.
singleton :: Hashable k => k -> v -> STM (IOHashMap k v)
singleton k = newIOHashMap . HM.singleton k

------------------------------------------------------------------------
-- * Basic interface

-- | /O(1)/ Return 'True' if this map is empty, 'False' otherwise.
null :: IOHashMap k v -> STM Bool
null = readIOHashMap HM.null

-- | /O(n)/ Return the number of key-value mappings in this map.
size :: IOHashMap k v -> STM Int
size = readIOHashMap HM.size

-- | /O(log n)/ Return 'True' if the specified key is present in the
-- map, 'False' otherwise.
member :: (Eq k, Hashable k) => k -> IOHashMap k a -> STM Bool
member = readIOHashMap . HM.member

-- | /O(log n)/ Return the value to which the specified key is mapped,
-- or 'Nothing' if this map contains no mapping for the key.
lookup :: (Eq k, Hashable k) => k -> IOHashMap k v -> STM (Maybe v)
lookup = readIOHashMap . HM.lookup

-- | /O(log n)/ Return the value to which the specified key is mapped,
-- or 'Nothing' if this map contains no mapping for the key.
--
-- This is a flipped version of 'lookup'.
--
-- @since 0.2.11
(!?) :: (Eq k, Hashable k) => IOHashMap k v -> k -> STM (Maybe v)
(!?) m k = lookup k m

-- | /O(log n)/ Return the value to which the specified key is mapped,
-- or the default value if this map contains no mapping for the key.
--
-- @since 0.2.11
findWithDefault :: (Eq k, Hashable k)
              => v          -- ^ Default value to return.
              -> k -> IOHashMap k v -> STM v
findWithDefault v = readIOHashMap . HM.findWithDefault v



-- | /O(log n)/ Return the value to which the specified key is mapped.
-- Calls 'error' if this map contains no mapping for the key.
(!) :: (Eq k, Hashable k) => IOHashMap k v -> k -> STM v
(!) m k = readIOHashMap (flip (HM.!) k) m

infixl 9 !

------------------------------------------------------------------------
-- * Basic interface

-- | /O(log n)/ Associate the specified value with the specified
-- key in this map.  If this map previously contained a mapping for
-- the key, the old value is replaced.
insert :: (Eq k, Hashable k) => k -> v -> IOHashMap k v -> STM ()
insert k = modifyIOHashMap . HM.insert k


-- | /O(log n)/ Associate the value with the key in this map.  If
-- this map previously contained a mapping for the key, the old value
-- is replaced by the result of applying the given function to the new
-- and old value.  Example:
--
-- > insertWith f k v map
-- >   where f new old = new + old
insertWith :: (Eq k, Hashable k) => (v -> v -> v) -> k -> v -> IOHashMap k v
           -> STM ()
insertWith f k = modifyIOHashMap . HM.insertWith f k


-- | /O(log n)/ Remove the mapping for the specified key from this map
-- if present.
delete :: (Eq k, Hashable k) => k -> IOHashMap k v -> STM ()
delete = modifyIOHashMap . HM.delete


-- | /O(log n)/ Adjust the value tied to a given key in this map only
-- if it is present. Otherwise, leave the map alone.
adjust :: (Eq k, Hashable k) => (v -> v) -> k -> IOHashMap k v -> STM ()
adjust f = modifyIOHashMap . HM.adjust f

-- | /O(log n)/  The expression @('update' f k map)@ updates the value @x@ at @k@
-- (if it is in the map). If @(f x)@ is 'Nothing', the element is deleted.
-- If it is @('Just' y)@, the key @k@ is bound to the new value @y@.
update :: (Eq k, Hashable k) => (a -> Maybe a) -> k -> IOHashMap k a -> STM ()
update f = modifyIOHashMap . HM.update f

-- | /O(log n)/  The expression @('alter' f k map)@ alters the value @x@ at @k@, or
-- absence thereof.
--
-- 'alter' can be used to insert, delete, or update a value in a map. In short:
--
-- @
-- 'lookup' k ('alter' f k m) = f ('lookup' k m)
-- @
alter :: (Eq k, Hashable k) => (Maybe v -> Maybe v) -> k -> IOHashMap k v -> STM ()
alter f = modifyIOHashMap . HM.alter f

------------------------------------------------------------------------
-- * Folds

-- | /O(n)/ Reduce the map by applying a function to each element
-- and combining the results with a monoid operation.
foldMapWithKey :: Monoid m => (k -> v -> m) -> IOHashMap k v -> STM m
foldMapWithKey = readIOHashMap . HM.foldMapWithKey

-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- right-identity of the operator).
foldr :: (v -> a -> a) -> a -> IOHashMap k v -> STM a
foldr f = readIOHashMap . HM.foldr f


-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- left-identity of the operator).
foldl :: (a -> v -> a) -> a -> IOHashMap k v -> STM a
foldl f = readIOHashMap . HM.foldl f


-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- right-identity of the operator).  Each application of the operator
-- is evaluated before using the result in the next application.
-- This function is strict in the starting value.
foldr' :: (v -> a -> a) -> a -> IOHashMap k v -> STM a
foldr' f = readIOHashMap . HM.foldr' f

-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- left-identity of the operator).  Each application of the operator
-- is evaluated before using the result in the next application.
-- This function is strict in the starting value.
foldl' :: (a -> v -> a) -> a -> IOHashMap k v -> STM a
foldl' f = readIOHashMap . HM.foldl' f


-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- right-identity of the operator).  Each application of the operator
-- is evaluated before using the result in the next application.
-- This function is strict in the starting value.
foldrWithKey' :: (k -> v -> a -> a) -> a -> IOHashMap k v -> STM a
foldrWithKey' f = readIOHashMap . HM.foldrWithKey' f

-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- left-identity of the operator).  Each application of the operator
-- is evaluated before using the result in the next application.
-- This function is strict in the starting value.
foldlWithKey' :: (a -> k -> v -> a) -> a -> IOHashMap k v -> STM a
foldlWithKey' f = readIOHashMap . HM.foldlWithKey' f

-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- right-identity of the operator).
foldrWithKey :: (k -> v -> a -> a) -> a -> IOHashMap k v -> STM a
foldrWithKey f = readIOHashMap . HM.foldrWithKey f


-- | /O(n)/ Reduce this map by applying a binary operator to all
-- elements, using the given starting value (typically the
-- left-identity of the operator).
foldlWithKey :: (a -> k -> v -> a) -> a -> IOHashMap k v -> STM a
foldlWithKey f = readIOHashMap . HM.foldlWithKey f


------------------------------------------------------------------------
-- * Conversions

-- TODO: Improve fusion rules by modelled them after the Prelude ones
-- on lists.

-- | /O(n)/ Return a list of this map's keys.  The list is produced
-- lazily.
keys :: IOHashMap k v -> STM [k]
keys = readIOHashMap HM.keys

-- | /O(n)/ Return a list of this map's values.  The list is produced
-- lazily.
elems :: IOHashMap k v -> STM [v]
elems = readIOHashMap HM.elems

------------------------------------------------------------------------
-- ** Lists

-- | /O(n)/ Return a list of this map's elements.  The list is
-- produced lazily. The order of its elements is unspecified.
toList :: IOHashMap k v -> STM [(k, v)]
toList = readIOHashMap HM.toList

-- | /O(n)/ Construct a map with the supplied mappings.  If the list
-- contains duplicate mappings, the later mappings take precedence.
fromList :: (Eq k, Hashable k) => [(k, v)] -> STM (IOHashMap k v)
fromList = newIOHashMap . HM.fromList