packages feed

monoidal-containers (empty) → 0.1.0.0

raw patch · 5 files changed

+373/−0 lines, 5 filesdep +basedep +containersdep +deepseqsetup-changed

Dependencies added: base, containers, deepseq, hashable, lens, newtype, unordered-containers

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Ben Gamari++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Ben Gamari nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ monoidal-containers.cabal view
@@ -0,0 +1,35 @@+name:                monoidal-containers+version:             0.1.0.0+synopsis:            Containers with monoidal accumulation+description:         Containers with monoidal accumulation+homepage:            http://github.com/bgamari/monoidal-containers+license:             BSD3+license-file:        LICENSE+author:              Ben Gamari+maintainer:          ben@smart-cactus.org+copyright:           (c) 2014 Ben Gamari+category:            Data+build-type:          Simple+cabal-version:       >=1.10++source-repository head+  type:                git+  location:            git://github.com/bgamari/monoidal-containers++library+  exposed-modules:     Data.Map.Monoidal+                       Data.HashMap.Monoidal+  other-extensions:    CPP,+                       MultiParamTypeClasses,+                       GeneralizedNewtypeDeriving,+                       DeriveTraversable,+                       DeriveDataTypeable+  build-depends:       base >=4.7 && <4.9,+                       deepseq >=1.3 && <1.5,+                       containers >=0.5 && <0.6,+                       unordered-containers >= 0.2 && < 0.3,+                       hashable >= 1.2 && < 1.3,+                       lens >=4.4 && <4.8,+                       newtype >=0.2 && <0.3+  hs-source-dirs:      src+  default-language:    Haskell2010
+ src/Data/HashMap/Monoidal.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This module provides a 'Data.HashMap' variant which uses the value's+-- 'Monoid' instance to accumulate conflicting entries when merging+-- 'Map's.+--+-- While some functions mirroring those of 'Data.HashMap' are provided+-- here for convenience, more specialized needs will likely want to use+-- either the @Newtype@ or @Wrapped@ instances to manipulate the+-- underlying 'Map'.++module Data.HashMap.Monoidal+    ( MonoidalHashMap+      -- * Often-needed functions+    , singleton+    , size+    , member+    , notMember+    , lookup+    , elems+    , keys+    ) where++import Prelude hiding (lookup)+import Data.Monoid+import Data.Foldable (Foldable)+import Data.Traversable (Traversable)+import Control.Applicative (Applicative, pure)+import Data.Data (Data)+import Data.Typeable (Typeable)++#if MIN_VERSION_base(4,7,0)+import GHC.Exts (IsList(..))+#endif++import Control.DeepSeq+import qualified Data.HashMap.Strict as M+import Data.Hashable (Hashable)+import Control.Lens+import Control.Newtype++-- | A 'HashMap' with monoidal accumulation+newtype MonoidalHashMap k a = MM (M.HashMap k a)+    deriving (Show, Read, Functor, Eq, NFData,+              Foldable, Traversable,+              Data, Typeable)++type instance Index (MonoidalHashMap k a) = k+type instance IxValue (MonoidalHashMap k a) = a+instance (Eq k, Hashable k) => Ixed (MonoidalHashMap k a) where+    ix k f (MM m) = case M.lookup k m of+      Just v  -> f v <&> \v' -> MM (M.insert k v' m)+      Nothing -> pure (MM m)+    {-# INLINE ix #-}++instance (Eq k, Hashable k) => At (MonoidalHashMap k a) where+    at k f (MM m) = f mv <&> \r -> case r of+      Nothing -> maybe (MM m) (const (MM $ M.delete k m)) mv+      Just v' -> MM $ M.insert k v' m+      where mv = M.lookup k m+    {-# INLINE at #-}++instance Each (MonoidalHashMap k a) (MonoidalHashMap k b) a b++instance (Eq k, Hashable k) => FunctorWithIndex k (MonoidalHashMap k)+instance (Eq k, Hashable k) => FoldableWithIndex k (MonoidalHashMap k)+instance (Eq k, Hashable k) => TraversableWithIndex k (MonoidalHashMap k) where+    itraverse f (MM m) = fmap MM $ itraverse f m+    {-# INLINE itraverse #-}++instance AsEmpty (MonoidalHashMap k a) where+    _Empty = nearly (MM M.empty) (M.null . unpack)+    {-# INLINE _Empty #-}++instance Wrapped (MonoidalHashMap k a) where+    type Unwrapped (MonoidalHashMap k a) = M.HashMap k a+    _Wrapped' = iso unpack pack+    {-# INLINE _Wrapped' #-}++instance (Eq k, Hashable k, Monoid a) => Monoid (MonoidalHashMap k a) where+    mempty = MM mempty+    {-# INLINE mempty #-}+    MM a `mappend` MM b = MM $ M.unionWith mappend a b+    {-# INLINE mappend #-}++instance Newtype (MonoidalHashMap k a) (M.HashMap k a) where+    pack = MM+    {-# INLINE pack #-}+    unpack (MM a) = a+    {-# INLINE unpack #-}++#if MIN_VERSION_base(4,7,0)+instance (Eq k, Hashable k) => IsList (MonoidalHashMap k a) where+    type Item (MonoidalHashMap k a) = (k, a)+    fromList = MM . M.fromList+    {-# INLINE fromList #-}+    toList = M.toList . unpack+    {-# INLINE toList #-}+#endif++-- | /O(1)/. A map with a single element.+singleton :: (Eq k, Hashable k) => k -> a -> MonoidalHashMap k a+singleton k a = MM $ M.singleton k a+{-# INLINE singleton #-}++-- | /O(1)/. The number of elements in the map.+size :: MonoidalHashMap k a -> Int+size = M.size . unpack+{-# INLINE size #-}++-- | /O(log n)/. Is the key a member of the map? See also 'notMember'.+member :: (Eq k, Hashable k) => k -> MonoidalHashMap k a -> Bool+member k = M.member k . unpack+{-# INLINE member #-}++-- | /O(log n)/. Is the key not a member of the map? See also 'member'.+notMember :: (Eq k, Hashable k) => k -> MonoidalHashMap k a -> Bool+notMember k = not . M.member k . unpack+{-# INLINE notMember #-}++-- | /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 -> MonoidalHashMap k v -> Maybe v+lookup k = M.lookup k . unpack+{-# INLINE lookup #-}++-- | /O(log n)/. Delete a key and its value from the map. When the key is not+-- a member of the map, the original map is returned.+delete :: (Eq k, Hashable k) => k -> MonoidalHashMap k a -> MonoidalHashMap k a+delete k = _Wrapping' MM %~ M.delete k+{-# INLINE delete #-}++-- | /O(n)/.+-- Return all elements of the map in the ascending order of their keys.+-- Subject to list fusion.+elems :: MonoidalHashMap k a -> [a]+elems = M.elems . unpack+{-# INLINE elems #-}++-- | /O(n)/. Return all keys of the map in ascending order. Subject to list+-- fusion.+keys :: MonoidalHashMap k a -> [k]+keys = M.keys . unpack+{-# INLINE keys #-}
+ src/Data/Map/Monoidal.hs view
@@ -0,0 +1,156 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This module provides a 'Data.Map' variant which uses the value's+-- 'Monoid' instance to accumulate conflicting entries when merging+-- 'Map's.+--+-- While some functions mirroring those of 'Data.Map' are provided+-- here for convenience, more specialized needs will likely want to use+-- either the @Newtype@ or @Wrapped@ instances to manipulate the+-- underlying 'Map'.++module Data.Map.Monoidal+    ( MonoidalMap+      -- * Often-needed functions+    , singleton+    , size+    , member+    , notMember+    , findWithDefault+    , elems+    , keys+    ) where++import Data.Monoid+import Data.Foldable (Foldable)+import Data.Traversable (Traversable)+import Control.Applicative (Applicative, pure)+import Data.Data (Data)+import Data.Typeable (Typeable)++#if MIN_VERSION_base(4,7,0)+import GHC.Exts (IsList(..))+#endif++import Control.DeepSeq+import qualified Data.Map as M+import Control.Lens+import Control.Newtype++-- | A 'Map' with monoidal accumulation+newtype MonoidalMap k a = MM (M.Map k a)+    deriving (Show, Read, Functor, Eq, Ord, NFData,+              Foldable, Traversable,+              Data, Typeable)++type instance Index (MonoidalMap k a) = k+type instance IxValue (MonoidalMap k a) = a+instance Ord k => Ixed (MonoidalMap k a) where+    ix k f (MM m) = case M.lookup k m of+      Just v  -> f v <&> \v' -> MM (M.insert k v' m)+      Nothing -> pure (MM m)+    {-# INLINE ix #-}++instance Ord k => At (MonoidalMap k a) where+    at k f (MM m) = f mv <&> \r -> case r of+      Nothing -> maybe (MM m) (const (MM $ M.delete k m)) mv+      Just v' -> MM $ M.insert k v' m+      where mv = M.lookup k m+    {-# INLINE at #-}++instance Each (MonoidalMap k a) (MonoidalMap k b) a b++instance Ord k => FunctorWithIndex k (MonoidalMap k)+instance Ord k => FoldableWithIndex k (MonoidalMap k)+instance Ord k => TraversableWithIndex k (MonoidalMap k) where+    itraverse f (MM m) = fmap MM $ itraverse f m+    {-# INLINE itraverse #-}++instance Ord k => TraverseMin k (MonoidalMap k) where+    traverseMin f (MM m) = fmap MM $ traverseMin f m+    {-# INLINE traverseMin #-}+instance Ord k => TraverseMax k (MonoidalMap k) where+    traverseMax f (MM m) = fmap MM $ traverseMax f m+    {-# INLINE traverseMax #-}++instance AsEmpty (MonoidalMap k a) where+    _Empty = nearly (MM M.empty) (M.null . unpack)+    {-# INLINE _Empty #-}++instance Wrapped (MonoidalMap k a) where+    type Unwrapped (MonoidalMap k a) = M.Map k a+    _Wrapped' = iso unpack pack+    {-# INLINE _Wrapped' #-}++instance (Ord k, Monoid a) => Monoid (MonoidalMap k a) where+    mempty = MM mempty+    {-# INLINE mempty #-}+    MM a `mappend` MM b = MM $ M.unionWith mappend a b+    {-# INLINE mappend #-}++instance Newtype (MonoidalMap k a) (M.Map k a) where+    pack = MM+    {-# INLINE pack #-}+    unpack (MM a) = a+    {-# INLINE unpack #-}++#if MIN_VERSION_base(4,7,0)+instance Ord k => IsList (MonoidalMap k a) where+    type Item (MonoidalMap k a) = (k, a)+    fromList = MM . M.fromList+    {-# INLINE fromList #-}+    toList = M.toList . unpack+    {-# INLINE toList #-}+#endif++-- | /O(1)/. A map with a single element.+singleton :: Ord k => k -> a -> MonoidalMap k a+singleton k a = MM $ M.singleton k a+{-# INLINE singleton #-}++-- | /O(1)/. The number of elements in the map.+size :: MonoidalMap k a -> Int+size = M.size . unpack+{-# INLINE size #-}++-- | /O(log n)/. Is the key a member of the map? See also 'notMember'.+member :: Ord k => k -> MonoidalMap k a -> Bool+member k = M.member k . unpack+{-# INLINE member #-}++-- | /O(log n)/. Is the key not a member of the map? See also 'member'.+notMember :: Ord k => k -> MonoidalMap k a -> Bool+notMember k = not . M.member k . unpack+{-# INLINE notMember #-}++-- | /O(log n)/. The expression @('findWithDefault' def k map)@ returns+-- the value at key @k@ or returns default value @def@+-- when the key is not in the map.+findWithDefault :: Ord k => a -> k -> MonoidalMap k a -> a+findWithDefault def k = M.findWithDefault def k . unpack+{-# INLINE findWithDefault #-}++-- | /O(log n)/. Delete a key and its value from the map. When the key is not+-- a member of the map, the original map is returned.+delete :: Ord k => k -> MonoidalMap k a -> MonoidalMap k a+delete k = _Wrapping' MM %~ M.delete k+{-# INLINE delete #-}++-- | /O(n)/.+-- Return all elements of the map in the ascending order of their keys.+-- Subject to list fusion.+elems :: MonoidalMap k a -> [a]+elems = M.elems . unpack      +{-# INLINE elems #-}++-- | /O(n)/. Return all keys of the map in ascending order. Subject to list+-- fusion.+keys :: MonoidalMap k a -> [k]+keys = M.keys . unpack+{-# INLINE keys #-}