packages feed

refined-containers (empty) → 0.1.0.0

raw patch · 18 files changed

+5808/−0 lines, 18 filesdep +adjunctionsdep +basedep +constraints

Dependencies added: adjunctions, base, constraints, containers, deepseq, distributive, hashable, indexed-traversable, mtl, refined, reflection, unordered-containers

Files

+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2023 Typeable++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ refined-containers.cabal view
@@ -0,0 +1,98 @@+cabal-version: 3.0+name: refined-containers+category: Data+synopsis:+    Type-checked proof that a key exists in a container and can be safely+    indexed.+description:+    This package defines ways to prove that a key exists in an associative+    container such as a 'Map', 'IntMap', or 'HashMap'; so that the key can be+    used to index into the map without a 'Maybe' or manually handling the+    \"impossible\" case with 'error' or other partial functions.++    To do this, the containers are tagged with a type parameter that identifies+    their set of keys, so that if you have another container with the same+    parameter, you know it has the same keys.++    There is also a type of keys that have been proven to exist in such+    containers -- a refinement type. They are also tagged with a type parameter.+    If the type parameter of the key matches that of the container, indexing is+    guaranteed to proceed without failure.++license: MIT+license-file: LICENSE+author: mniip@typeable.io+maintainer: mniip@typeable.io+version: 0.1.0.0+build-type: Simple++tested-with:+    , GHC == 9.6.3+    , GHC == 9.4.8+    , GHC == 9.2.8+    , GHC == 9.0.2+    , GHC == 8.10.7+    , GHC == 8.6.5++source-repository head+    type: git+    location: https://github.com/typeable/refined-containers/++library+    build-depends:+        , base >= 4.12 && < 4.20+        , adjunctions >= 4.4 && < 4.5+        , constraints >= 0.11 && < 0.15+        , containers >= 0.5.7 && < 0.8+        , deepseq >= 1.4 && < 1.6+        , distributive >= 0.5.3 && < 0.7+        , hashable >= 1.2.7 && < 1.5+        , indexed-traversable >= 0.1 && < 0.2+        , mtl >= 2.2.2 && < 2.4+        , refined >= 0.5 && < 0.9+        , reflection >= 2 && < 2.2+        , unordered-containers >= 0.2.11 && < 0.3+    exposed-modules:+        Data.HashMap.Refined+        Data.HashMap.Strict.Refined+        Data.HashSet.Refined+        Data.IntMap.Refined+        Data.IntMap.Strict.Refined+        Data.IntSet.Refined+        Data.Map.Refined+        Data.Map.Strict.Refined+        Data.Set.Refined+    other-modules:+        Data.Container.Refined.Conversion+        Data.Container.Refined.Hashable+        Data.Container.Refined.Proofs+        Data.Container.Refined.Unsafe+        Data.HashMap.Common.Refined+        Data.IntMap.Common.Refined+        Data.Map.Common.Refined+    hs-source-dirs: src+    default-language: Haskell2010+    default-extensions:+        BangPatterns+        BlockArguments+        ConstraintKinds+        DataKinds+        DeriveTraversable+        DerivingStrategies+        FlexibleContexts+        FlexibleInstances+        GADTs+        GeneralizedNewtypeDeriving+        MagicHash+        MultiParamTypeClasses+        MultiWayIf+        OverloadedStrings+        RankNTypes+        PatternSynonyms+        RoleAnnotations+        ScopedTypeVariables+        TupleSections+        TypeApplications+        TypeFamilies+        TypeOperators+    ghc-options: -Wall -Wredundant-constraints
+ src/Data/Container/Refined/Conversion.hs view
@@ -0,0 +1,79 @@+module Data.Container.Refined.Conversion where++import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Hashable+import           Data.Container.Refined.Proofs+import qualified Data.HashMap.Lazy as HashMap+import           Data.HashMap.Common.Refined+import qualified Data.HashSet as HashSet+import qualified Data.IntMap as IntMap+import           Data.IntMap.Common.Refined+import qualified Data.IntSet as IntSet+import qualified Data.Map as Map+import           Data.Map.Common.Refined+import           Data.Proxy+import           Data.Reflection+import qualified Data.Set as Set+import           Data.Type.Equality ((:~:)(..))+import           Unsafe.Coerce+++unsafeWrapSet :: forall r r' a. KnownSet r' a => Proxy r' -> Set r a+unsafeWrapSet _ = case unsafeCoerce Refl :: r :~: r' of Refl -> Dict+{-# INLINE unsafeWrapSet #-}++unsafeWrapIntSet :: forall r r'. KnownIntSet r' => Proxy r' -> IntSet r+unsafeWrapIntSet _ = case unsafeCoerce Refl :: r :~: r' of Refl -> Dict+{-# INLINE unsafeWrapIntSet #-}++unsafeWrapHashSet :: forall r r' a. KnownHashSet r' a => Proxy r' -> HashSet r a+unsafeWrapHashSet _ = case unsafeCoerce Refl :: r :~: r' of Refl -> Dict+{-# INLINE unsafeWrapHashSet #-}++set2IntSet :: forall s. KnownSet s Int => IntSet s+set2IntSet = reify+  (IntSet.fromDistinctAscList $ Set.toAscList $ reflect $ Proxy @s)+  unsafeWrapIntSet++map2IntMap :: forall s a. Map s Int a -> IntMap s a+map2IntMap (Map m) = IntMap $ IntMap.fromDistinctAscList $ Map.toAscList m++set2HashSet :: forall s a. (Hashable a, KnownSet s a) => HashSet s a+set2HashSet = reify+  (HashSet.fromList $ Set.toList $ reflect $ Proxy @s)+  unsafeWrapHashSet++map2HashMap :: forall s k a. Hashable k => Map s k a -> HashMap s k a+map2HashMap (Map m) = HashMap $ HashMap.fromList $ Map.toList m++intSet2Set :: forall s. KnownIntSet s => Set s Int+intSet2Set = reify+  (Set.fromDistinctAscList $ IntSet.toAscList $ reflect $ Proxy @s)+  unsafeWrapSet++intMap2Map :: forall s a. IntMap s a -> Map s Int a+intMap2Map (IntMap m) = Map $ Map.fromDistinctAscList $ IntMap.toAscList m++intSet2HashSet :: forall s. KnownIntSet s => HashSet s Int+intSet2HashSet = reify+  (HashSet.fromList $ IntSet.toList $ reflect $ Proxy @s)+  unsafeWrapHashSet++intMap2HashMap :: forall s a. IntMap s a -> HashMap s Int a+intMap2HashMap (IntMap m) = HashMap $ HashMap.fromList $ IntMap.toList m++hashSet2Set :: forall s a. (Ord a, KnownHashSet s a) => Set s a+hashSet2Set = reify+  (Set.fromList $ HashSet.toList $ reflect $ Proxy @s)+  unsafeWrapSet++hashMap2Map :: forall s k a. Ord k => HashMap s k a -> Map s k a+hashMap2Map (HashMap m) = Map $ Map.fromList $ HashMap.toList m++hashSet2IntSet :: forall s. KnownHashSet s Int => IntSet s+hashSet2IntSet = reify+  (IntSet.fromList $ HashSet.toList $ reflect $ Proxy @s)+  unsafeWrapIntSet++hashMap2IntMap :: forall s a. HashMap s Int a -> IntMap s a+hashMap2IntMap (HashMap m) = IntMap $ IntMap.fromList $ HashMap.toList m
+ src/Data/Container/Refined/Hashable.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE CPP #-}+module Data.Container.Refined.Hashable+  ( Hashable+  ) where++#if MIN_VERSION_hashable(1, 4, 0)+import           Data.Hashable (Hashable)+#else+import qualified Data.Hashable as Hashable+++type Hashable a = (Eq a, Hashable.Hashable a)+#endif
+ src/Data/Container/Refined/Proofs.hs view
@@ -0,0 +1,250 @@+{-# LANGUAGE CPP #-}+#if MIN_VERSION_refined(0, 7, 0)+#else+{-# LANGUAGE UndecidableInstances #-}+#endif+module Data.Container.Refined.Proofs where++import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Hashable+import           Data.Kind+import           Data.Reflection+import qualified Data.HashSet as HashSet+import qualified Data.IntSet as IntSet+import qualified Data.Set as Set+import           Data.Type.Coercion+import           Data.Typeable+import           Refined+++-- | A constraint evidencing that we know the contents of the set @s@ at+-- runtime. Whenever you see this constraint on a function, there is an actual+-- @'Data.Set.Set' a@ being passed around at runtime.+--+-- Given this constraint, to obtain a regular @'Data.Set.Set' a@ you can use+-- 'reflect'.+type KnownSet s a = Reifies s (Set.Set a)++-- | A 'Data.Set.Set' whose contents are tracked by the type parameter @s@. This+-- is a \"singleton\": for a given @s@ there's only one value of this type.+--+-- Since this is just a 'Dict', you can freely convert between the value ('Set')+-- and the constraint ('KnownSet'). This library prefers to use the constraint.+type Set s a = Dict (KnownSet s a)++-- | A constraint evidencing that we know the contents of the set @s@ at+-- runtime. Whenever you see this constraint on a function, there is an actual+-- 'Data.IntSet.IntSet' being passed around at runtime.+--+-- Given this constraint, to obtain a regular 'Data.IntSet.IntSet' you can use+-- 'reflect'.+type KnownIntSet s = Reifies s IntSet.IntSet++-- | A 'Data.IntSet.IntSet' whose contents are tracked by the type parameter+-- @s@. This is a \"singleton\": for a given @s@ there's only one value of this+-- type.+--+-- Since this is just a 'Dict', you can freely convert between the value+-- ('IntSet') and the constraint ('KnownIntSet'). This library prefers to use+-- the constraint.+type IntSet s = Dict (KnownIntSet s)++-- | A constraint evidencing that we know the contents of the set @s@ at+-- runtime. Whenever you see this constraint on a function, there is an actual+-- @'Data.HashSet.HashSet' a@ being passed around at runtime.+--+-- Given this constraint, to obtain a regular @'Data.HashSet.HashSet' a@ you can+-- use 'reflect'.+type KnownHashSet s a = Reifies s (HashSet.HashSet a)++-- | A 'Data.HashSet.HashSet' whose contents are tracked by the type parameter+-- @s@. This is a \"singleton\": for a given @s@ there's only one value of this+-- type.+--+-- Since this is just a 'Dict', you can freely convert between the value+-- ('HashSet') and the constraint ('KnownHashSet'). This library prefers to use+-- the constraint.+type HashSet s a = Dict (KnownHashSet s a)++-- | Disambiguate the choice of implementation for sets and maps.+data Flavor+  = Regular -- ^ 'Data.Set.Set' and 'Data.Map.Map'+  | Int -- ^ 'Data.IntSet.IntSet' and 'Data.IntMap.IntMap'+  | Hashed -- ^ 'Data.HashSet.HashSet' and 'Data.HashMap.Lazy.HashMap'++-- | A predicate for use with "Refined", verifying that a value is an element of+-- the set @s@.+data InSet (f :: Flavor) (s :: Type) = InSet++-- | See 'Data.Set.Refined.revealPredicate'.+instance (Ord a, Typeable s, KnownSet s a)+  => Predicate (InSet 'Regular s) a where+  validate p x+    | x `Set.member` reflect (Proxy @s) = success+    | otherwise = throwRefineOtherException+      (typeRep p)+      "Value is not in the Set"++-- | See 'Data.IntSet.Refined.revealPredicate'.+instance (a ~ Int, Typeable s, KnownIntSet s)+  => Predicate (InSet 'Int s) a where+  validate p x+    | x `IntSet.member` reflect (Proxy @s) = success+    | otherwise = throwRefineOtherException+      (typeRep p)+      "Value is not in the IntSet"++-- | See 'Data.HashSet.Refined.revealPredicate'.+instance (Hashable a, Typeable s, KnownHashSet s a)+  => Predicate (InSet 'Hashed s) a where+  validate p x+    | x `HashSet.member` reflect (Proxy @s) = success+    | otherwise = throwRefineOtherException+      (typeRep p)+      "Value is not in the HashSet"++-- | A proof that values satisfying @p@ can be cast into values satisfying @q@.+--+-- For example, @'InSet' s ':->' 'InSet' r@ proves that \(s \subseteq r\).+type p :-> q = forall x. Refined p x -> Refined q x+infix 1 :->++-- | Proof that the set @r@ is empty.+newtype EmptyProof f r = EmptyProof+  (forall s. InSet f r :-> InSet f s)+  -- ^ \(\forall s, r \subseteq s\), which is equivalent to+  -- \(r \subseteq \varnothing\)++-- | Proof that @r@ contains an element of type @a@.+newtype SingletonProof f a r = SingletonProof+  (Refined (InSet f r) a) -- ^ The element that is guaranteed to be in @r@++-- | Proof that elements of @t a@ are contained in @r@.+newtype FromTraversableProof f (t :: Type -> Type) a r = FromTraversableProof+  (t (Refined (InSet f r) a))+  -- ^ The original traversable, with all elements refined with a guarantee of+  -- being in @r@++-- | Proof that @r@ is @s@ with @a@ inserted.+data InsertProof f a s r = InsertProof+  (Refined (InSet f r) a)+  -- ^ The element that was inserted and is guaranteed to be in @r@.+  (InSet f s :-> InSet f r) -- ^ \(s \subseteq r \)++-- | Proof that @s@ is a subset of the set @r@.+newtype SubsetProof f s r = SubsetProof+  (InSet f s :-> InSet f r) -- ^ \(s \subseteq r\)++-- | Proof that @s@ is a superset of the set @r@.+newtype SupersetProof f s r = SupersetProof+  (InSet f r :-> InSet f s) -- ^ \(r \subseteq s\)++-- | Proof that @s@ and @r@ are disjoint.+newtype DisjointProof f s r = DisjointProof+  (forall t. InSet f t :-> InSet f s+    -> InSet f t :-> InSet f r+    -> forall u. InSet f t :-> InSet f u)+  -- ^ \(\forall t,(t\subseteq s)\land(t\subseteq r)\implies\forall u,t\subseteq u\),+  -- which is equivalent to \(s \cap r \subseteq \varnothing\)++-- | Proof that unioning @s@ and @t@ gives @r@.+data UnionProof f s t r = UnionProof+  (InSet f s || InSet f t :-> InSet f r) -- ^ \(s \cup t \subseteq r\)+  (forall u. InSet f s :-> InSet f u+    -> InSet f t :-> InSet f u+    -> InSet f r :-> InSet f u)+  -- ^ \(\forall u,(s\subseteq u)\land(t\subseteq u)\implies r\subseteq u\),+  -- which is equivalent to \(r \subseteq s \cup u\)++-- | Proof that if from @s@ you subtract @t@, then you get @r@.+data DifferenceProof f s t r = DifferenceProof+  (InSet f r :-> InSet f s) -- ^ \(r \subseteq s\)+  (forall u. InSet f u :-> InSet f r+    -> InSet f u :-> InSet f t+    -> forall v. InSet f u :-> InSet f v)+  -- ^ \(\forall u,(u\subseteq r)\land(u\subseteq t)\implies\forall v,u\subseteq v\),+  -- which is equivalent to \(r \cap t \subseteq \varnothing\)+  (InSet f s :-> InSet f t || InSet f r) -- ^ \(s \subseteq t \cup r\)++-- | Proof that @r@ is obtained by removing some of @t@'s elements from @s@.+data PartialDifferenceProof f s t r = PartialDifferenceProof+  (InSet f r :-> InSet f s) -- ^ \(r \subseteq s\)+  (InSet f s :-> InSet f t || InSet f r) -- ^ \(s \subseteq t \cup r\)++-- | Proof that intersecting @s@ and @t@ gives @r@.+data IntersectionProof f s t r = IntersectionProof+  (InSet f r :-> InSet f s && InSet f t) -- ^ \(r \subseteq s \cap t\)+  (forall u. InSet f u :-> InSet f s+    -> InSet f u :-> InSet f t+    -> InSet f u :-> InSet f r)+  -- ^ \(\forall u,(u\subseteq s)\land(u\subseteq t)\implies u\subseteq r\),+  -- which is equivalent to \(s \cap t \subseteq r\)++-- | Proof that the cartesian product of @s@ and @t@ is @r@.+newtype ProductProof f s t r = ProductProof+  (forall a b. Coercion+    (Refined (InSet f s) a, Refined (InSet f t) b)+    (Refined (InSet f r) (a, b)))+  -- ^ A pair of elements from @s@ and @t@ respectively can be converted into an+  -- element of @r@ and back. You can use @'coerceWith' co@ and+  -- @'coerceWith' ('sym' co)@.++-- | Proof that the tagged disjoint union of @s@ and @t@ is @r@.+newtype CoproductProof f s t r = CoproductProof+  (forall a b. Coercion+    (Either (Refined (InSet f s) a) (Refined (InSet f t) b))+    (Refined (InSet f r) (Either a b)))+  -- ^ Coproduct of elements of @s@ and @t@ can be converted into an element of+  -- @r@ and back. You can use @'coerceWith' co@ and @'coerceWith' ('sym' co)@.++-- | Proof that @s@ is the union of disjoint subsets @r@ and @q@, together with+-- a procedure that decides which of the two an element belongs to.+data PartitionProof f s a r q = PartitionProof+  (Refined (InSet f s) a+    -> Either (Refined (InSet f r) a) (Refined (InSet f q) a))+  -- ^ Decide whether a given element of @s@ belongs to @r@ or to @q@+  (InSet f r || InSet f q :-> InSet f s) -- ^ \(r \cup q \subseteq s\)+  (forall t. InSet f r :-> InSet f t+    -> InSet f q :-> InSet f t+    -> InSet f s :-> InSet f t)+  -- ^ \(\forall t,(r\subseteq t)\land(q\subseteq t)\implies s\subseteq t\),+  -- which is equivalent to \(s \subseteq r \cup q\)+  (forall t. InSet f t :-> InSet f r+    -> InSet f t :-> InSet f q+    -> forall u. InSet f t :-> InSet f u)+  -- ^ \(\forall t,(t\subseteq r)\land(t\subseteq q)\implies\forall u,t\subseteq u\),+  -- which is equivalent to \(r \cap q \subseteq \varnothing\)++-- | Proof that @s@ is the union of disjoint subsets @r@ and @q@, but without a+-- deciding procedure.+data PartialPartitionProof f s r q = PartialPartitionProof+  (InSet f r || InSet f q :-> InSet f s) -- ^ \(r \cup q \subseteq s\)+  (forall t. InSet f r :-> InSet f t+    -> InSet f q :-> InSet f t+    -> InSet f s :-> InSet f t)+  -- ^ \(\forall t,(r\subseteq t)\land(q\subseteq t)\implies s\subseteq t\),+  -- which is equivalent to \(s \subseteq r \cup q\)+  (forall t. InSet f t :-> InSet f r+    -> InSet f t :-> InSet f q+    -> forall u. InSet f t :-> InSet f u)+  -- ^ \(\forall t,(t\subseteq r)\land(t\subseteq q)\implies\forall u,t\subseteq u\),+  -- which is equivalent to \(r \cap q \subseteq \varnothing\)++-- | Proof that @s@ contains disjoint subsets @r@ and @q@, along with an+-- optional element between them.+data SplitProof f s e r q = SplitProof+  !(Maybe e) -- ^ The element between @r@ and @q@+  (InSet f r || InSet f q :-> InSet f s) -- ^ \(r \cup q \subseteq s\)+  (forall t. InSet f t :-> InSet f r+    -> InSet f t :-> InSet f q+    -> forall u. InSet f t :-> InSet f u)+  -- ^ \(\forall t,(t\subseteq r)\land(t\subseteq q)\implies\forall u,t\subseteq u\),+  -- which is equivalent to \(r \cap q \subseteq \varnothing\)++-- | Proof that @r@ is the direct image of @s@ under some mapping @f :: a -> b@.+data MapProof f s a b r = MapProof+  (Refined (InSet f s) a -> Refined (InSet f r) b)+  -- ^ Compute the image of an element of @s@ (the image is then an element of+  -- @r@)+  (Refined (InSet f r) b -> Refined (InSet f s) a)+  -- ^ For an element of @r@, return an arbitrary preimage from @s@
+ src/Data/Container/Refined/Unsafe.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE ImpredicativeTypes #-}+module Data.Container.Refined.Unsafe where++import Data.Container.Refined.Proofs+import Data.Type.Coercion+import Refined+import Refined.Unsafe+++unsafeSubset :: p :-> q+unsafeSubset = reallyUnsafeRefine . unrefine+{-# INLINE unsafeSubset #-}++unsafeSubsetWith2 :: p' :-> q' -> p'' :-> q'' -> p :-> q+unsafeSubsetWith2 f g = reallyUnsafeRefine . unrefine+  . f . reallyUnsafeRefine . unrefine+  . g . reallyUnsafeRefine . unrefine+{-# INLINE unsafeSubsetWith2 #-}++-- Because `Refined p x` is a newtype over `x`, by parametricity a `p :-> q`+-- can either diverge or be `id`. This ensures that it does not diverge.+rnfProof :: (p :-> q) -> ()+rnfProof f = unrefine $ f $ reallyUnsafeRefine ()+{-# INLINE rnfProof #-}++-- | This function can be used to freely convert between @Element@ and @Key@+-- types of various flavors ('Regular', v'Int', 'Hashed'), corresponding to the+-- different implementations of sets and maps.+castFlavor+  :: forall (f :: Flavor) (g :: Flavor) s a. Coercion+    (Refined (InSet f s) a)+    (Refined (InSet g s) a)+castFlavor = sym (reallyUnsafeUnderlyingRefined @a @(InSet f s))+  `trans` reallyUnsafeUnderlyingRefined @a @(InSet g s)+{-# INLINE castFlavor #-}++castRefined+  :: forall a p q. (p :-> q)+  -> (q :-> p)+  -> Coercion (Refined p a) (Refined q a)+castRefined f g+  | () <- rnfProof f+  , () <- rnfProof g+  = sym (reallyUnsafeUnderlyingRefined @a @p)+    `trans` reallyUnsafeUnderlyingRefined @a @q+{-# INLINE castRefined #-}
+ src/Data/HashMap/Common/Refined.hs view
@@ -0,0 +1,426 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE UndecidableInstances #-}+module Data.HashMap.Common.Refined where++import           Control.Monad.Reader+import           Control.DeepSeq+import           Data.Coerce+import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Hashable+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Distributive+import           Data.Foldable.WithIndex+import           Data.Functor.Rep+import           Data.Functor.WithIndex+import qualified Data.Hashable as Hashable+import qualified Data.HashMap.Lazy as HashMap+import qualified Data.HashMap.Strict as HashMapStrict+import qualified Data.HashSet as HashSet+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable.WithIndex+import           Data.Type.Coercion+import           Data.Type.Equality ((:~:)(..))+import           Refined+import           Refined.Unsafe+import           Unsafe.Coerce++#if MIN_VERSION_unordered_containers(0, 2, 12)+#else+import           Data.Monoid (All(..))+#endif+++-- | A wrapper around a regular 'Data.HashMap.HashMap' with a type parameter @s@+-- identifying the set of keys present in the map.+--+-- A key of type @k@ may not be present in the map, but a @'Key' s k@ is+-- guaranteed to be present (if the @s@ parameters match). Thus the map is+-- isomorphic to a (total) function @'Key' s k -> a@, which motivates many of+-- the instances below.+--+-- A 'HashMap' always knows its set of keys, so given @'HashMap' s k a@ we can+-- always derive @'KnownHashSet' s k@ by pattern matching on the 'Dict' returned+-- by 'keysSet'.+newtype HashMap s k a = HashMap (HashMap.HashMap k a)+  deriving newtype (Eq, Ord, Show, Functor, Foldable, Hashable.Hashable, NFData)+  deriving stock (Traversable)+type role HashMap nominal nominal representational++-- | Convert to a regular 'Data.HashMap.HashMap', forgetting its set of keys.+toMap :: forall s k a. HashMap s k a -> HashMap.HashMap k a+toMap (HashMap m) = m++-- | @'Key' s k@ is a key of type @k@ that has been verified to be an element+-- of the set @s@, and thus verified to be present in all @'HashMap' s k@ maps.+--+-- Thus, @'Key' s k@ is a \"refinement\" type of @k@, and this library+-- integrates with an implementation of refimenement types in "Refined", so+-- the machinery from there can be used to manipulate 'Key's (however see+-- 'Data.Set.Refined.revealPredicate').+--+-- The underlying @k@ value can be obtained with 'unrefine'. A @k@ can be+-- validated into an @'Key' s k@ with 'member'.+type Key s = Refined (InSet 'Hashed s)++unsafeCastKey :: forall s k. Coercion k (Key s k)+unsafeCastKey = reallyUnsafeUnderlyingRefined++unsafeKey :: k -> Key s k+unsafeKey = coerceWith unsafeCastKey++-- | An existential wrapper for a 'HashMap' with an as-yet-unknown set of keys.+-- Pattern maching on it gives you a way to refer to the set (the parameter+-- @s@), e.g.+--+-- @+-- case 'fromHashMap' ... of+--   'SomeHashMap' \@s m -> doSomethingWith \@s+--+-- case 'fromHashMap' ... of+--   'SomeHashMap' (m :: 'HashMap' s k a) -> doSomethingWith \@s+-- @+data SomeHashMap k a where+  SomeHashMap :: forall s k a. !(HashMap s k a) -> SomeHashMap k a++-- | Apply a map with an unknown set of keys to a continuation that can accept+-- a map with any set of keys. This gives you a way to refer to the set (the+-- parameter @s@), e.g.:+--+-- @+-- 'withHashMap' ('fromHashMap' ...+--   $ \(m :: 'HashMap' s k a) -> doSomethingWith \@s+-- @+withHashMap+  :: forall k a r. SomeHashMap k a -> (forall s. HashMap s k a -> r) -> r+withHashMap (SomeHashMap m) k = k m++-- | Construct a map from a regular 'Data.HashMap.Lazy.HashMap'.+fromHashMap :: forall k a. HashMap.HashMap k a -> SomeHashMap k a+fromHashMap m = SomeHashMap (HashMap m)++-- | An existential wrapper for a 'HashMap' with an as-yet-unknown set of keys,+-- together with a proof of some fact @p@ about the set. Pattern matching on it+-- gives you a way to refer to the set (the parameter @s@). Functions that+-- change the set of keys in a map will return the map in this way, together+-- with a proof that somehow relates the keys set to the function's inputs.+data SomeHashMapWith p k a where+  SomeHashMapWith+    :: forall s k a p. !(HashMap s k a) -> !(p s) -> SomeHashMapWith p k a++-- | Apply a map with proof for an unknown set of keys to a continuation that+-- can accept a map with any set of keys satisfying the proof. This gives you a+-- way to refer to the set (the parameter @s@).+withHashMapWith+  :: forall k a r p. SomeHashMapWith p k a+  -> (forall s. HashMap s k a -> p s -> r)+  -> r+withHashMapWith (SomeHashMapWith m p) k = k m p++-- | An existential wrapper for a pair of maps with as-yet-unknown sets of keys,+-- together with a proof of some fact @p@ relating them.+data Some2HashMapWith p k a b where+  Some2HashMapWith+    :: forall s t k a b p. !(HashMap s k a)+    -> !(HashMap t k b)+    -> !(p s t)+    -> Some2HashMapWith p k a b++-- | Apply a pair of maps with proof for unknown sets of keys to a continuation+-- that can accept any pair of maps with any sets of keys satisfying the proof.+-- This gives you a way to refer to the sets (the parameters @s@ and @t@).+with2HashMapWith+  :: forall k a b r p. Some2HashMapWith p k a b+  -> (forall s t. HashMap s k a -> HashMap t k b -> p s t -> r)+  -> r+with2HashMapWith (Some2HashMapWith m1 m2 p) k = k m1 m2 p++-- | An empty map.+empty :: forall k a. SomeHashMapWith (EmptyProof 'Hashed) k a+empty = SomeHashMapWith (HashMap HashMap.empty) $ EmptyProof unsafeSubset++-- | Create a map from a set of keys, and a function that for each key computes+-- the corresponding value.+fromSet :: forall s k a. KnownHashSet s k => (Key s k -> a) -> HashMap s k a+fromSet f = HashMap $ HashMap.mapWithKey (\k _ -> f $ unsafeKey k)+  $ HashSet.toMap (reflect $ Proxy @s)++-- | Delete a key and its value from the map if present, returning a potentially+-- smaller map.+delete+  :: forall s k a. Hashable k+  => k -> HashMap s k a -> SomeHashMapWith (SupersetProof 'Hashed s) k a+delete k (HashMap m) = SomeHashMapWith (HashMap $ HashMap.delete k m)+  $ SupersetProof unsafeSubset++-- | If the key is in the map, return the proof of this, and the associated+-- value; otherwise return 'Nothing'.+lookup :: forall s k a. Hashable k => k -> HashMap s k a -> Maybe (Key s k, a)+lookup k (HashMap m) = (unsafeKey k,) <$> HashMap.lookup k m++-- | Given a key that is proven to be in the map, return the associated value.+--+-- Unlike 'Data.HashMap.!' from "Data.HashMap.Lazy", this function is total, as+-- it is impossible to obtain a @'Key' s k@ for a key that is not in the map+-- @'HashMap' s k a@.+(!) :: forall s k a. Hashable k => HashMap s k a -> Key s k -> a+(!) (HashMap m) k = case HashMap.lookup (unrefine k) m of+  Nothing -> error "(!): bug: Data.HashMap.Refined has been subverted"+  Just x -> x++-- | If a key is in the map, return the proof that it is.+member :: forall s k a. Hashable k => k -> HashMap s k a -> Maybe (Key s k)+member k (HashMap m)+  | k `HashMap.member` m = Just (unsafeKey k)+  | otherwise = Nothing++-- | If a map is empty, return a proof that it is.+null :: forall s k a. HashMap s k a -> Maybe (EmptyProof 'Hashed s)+null (HashMap m)+  | HashMap.null m = Just $ EmptyProof unsafeSubset+  | otherwise = Nothing++-- | If all keys of the first map are also present in the second map, and the+-- given function returns 'True' for their associated values, return a proof+-- that the keys form a subset.+isSubmapOfBy+  :: forall s t k a b. Hashable k+  => (a -> b -> Bool)+  -> HashMap s k a+  -> HashMap t k b+  -> Maybe (SubsetProof 'Hashed s t)+isSubmapOfBy f (HashMap m1) (HashMap m2)+#if MIN_VERSION_unordered_containers(0, 2, 12)+  | HashMap.isSubmapOfBy f m1 m2+#else+  | All True <- flip HashMap.foldMapWithKey m1+    \k v1 -> case HashMap.lookup k m2 of+      Just v2 | f v1 v2 -> mempty+      _ -> All False+#endif+  = Just $ SubsetProof unsafeSubset+  | otherwise = Nothing++-- | If two maps are disjoint (i.e. their intersection is empty), return a proof+-- of that.+disjoint+  :: forall s t k a b. Hashable k+  => HashMap s k a -> HashMap t k b -> Maybe (DisjointProof 'Hashed s t)+disjoint (HashMap m1) (HashMap m2)+  | HashMap.null $ HashMapStrict.intersectionWith (\_ _ -> ()) m1 m2+  = Just $ DisjointProof \f g -> unsafeSubsetWith2 f g+  | otherwise = Nothing++-- | Given two maps proven to have the same keys, for each key apply the+-- function to the associated values, to obtain a new map with the same keys.+zipWithKey+  :: forall s k a b c. Hashable k+  => (Key s k -> a -> b -> c) -> HashMap s k a -> HashMap s k b -> HashMap s k c+zipWithKey f (HashMap m1) (HashMap m2) = HashMap+  $ HashMap.intersectionWithKey (f . unsafeKey) m1 m2++-- | Remove the keys that appear in the second map from the first map.+difference+  :: forall s t k a b. Hashable k+  => HashMap s k a+  -> HashMap t k b+  -> SomeHashMapWith (DifferenceProof 'Hashed s t) k a+difference (HashMap m1) (HashMap m2)+  = SomeHashMapWith (HashMap $ HashMap.difference m1 m2)+    $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, that are proven to be in the map. The set of keys remains the same.+mapWithKey+  :: forall s k a b. (Key s k -> a -> b) -> HashMap s k a -> HashMap s k b+mapWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.mapWithKey @k @a @b++-- | Map an 'Applicative' transformation with access to each value's+-- corresponding key and a proof that it is in the map. The set of keys remains+-- unchanged.+traverseWithKey+  :: forall s f k a b. Applicative f+  => (Key s k -> a -> f b) -> HashMap s k a -> f (HashMap s k b)+traverseWithKey f (HashMap m)+  = HashMap <$> HashMap.traverseWithKey (f . unsafeKey) m++-- | Map each key-value pair of a map into a monoid (with proof that the key was+-- in the map), and combine the results using '<>'.+foldMapWithKey+  :: forall s k a m. Monoid m => (Key s k -> a -> m) -> HashMap s k a -> m+foldMapWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.foldMapWithKey @m @k @a++-- | Right associative fold with a lazy accumulator.+foldrWithKey+  :: forall s k a b. (Key s k -> a -> b -> b) -> b -> HashMap s k a -> b+foldrWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.foldrWithKey @k @a @b++-- | Left associative fold with a lazy accumulator.+foldlWithKey+  :: forall s k a b. (b -> Key s k -> a -> b) -> b -> HashMap s k a -> b+foldlWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.foldlWithKey @b @k @a++-- | Right associative fold with a strict accumulator.+foldrWithKey'+  :: forall s k a b. (Key s k -> a -> b -> b) -> b -> HashMap s k a -> b+foldrWithKey' = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.foldrWithKey' @k @a @b++-- | Left associative fold with a strict accumulator.+foldlWithKey'+  :: forall s k a b. (b -> Key s k -> a -> b) -> b -> HashMap s k a -> b+foldlWithKey' = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.foldlWithKey' @b @k @a++-- | Return the set of keys in the map, with the contents of the set still+-- tracked by the @s@ parameter. See "Data.HashSet.Refined".+keysSet :: forall s k a. HashMap s k a -> HashSet s k+keysSet (HashMap m) = reify (HashMap.keysSet m)+  \(_ :: Proxy s') -> case unsafeCoerce Refl :: s :~: s' of+    Refl -> Dict++-- | Convert to a list of key-value pairs.+toList :: forall s k a. HashMap s k a -> [(Key s k, a)]+toList = gcoerceWith (unsafeCastKey @s @k) $ coerce $ HashMap.toList @k @a++-- | Retain only the key-value pairs that satisfy the predicate, returning a+-- potentially smaller map.+filterWithKey+  :: forall s k a. (Key s k -> a -> Bool)+  -> HashMap s k a+  -> SomeHashMapWith (SupersetProof 'Hashed s) k a+filterWithKey p (HashMap m)+  = SomeHashMapWith (HashMap $ HashMap.filterWithKey (p . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Restrict a map to only those keys that are elements of @t@.+restrictKeys+  :: forall s t k a. (Hashable k, KnownHashSet t k)+  => HashMap s k a -> SomeHashMapWith (IntersectionProof 'Hashed s t) k a+restrictKeys (HashMap m) = SomeHashMapWith+  (HashMap $ HashMap.intersectionWith const m+    $ HashSet.toMap $ reflect $ Proxy @t)+  $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Remove all keys that are elements of @t@ from the map.+withoutKeys+  :: forall s t k a. (Hashable k, KnownHashSet t k)+  => HashMap s k a -> SomeHashMapWith (DifferenceProof 'Hashed s t) k a+withoutKeys (HashMap m) = SomeHashMapWith+  (HashMap $ HashMap.difference m $ HashSet.toMap $ reflect $ Proxy @t)+  $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Partition a map into two disjoint submaps: those whose key-value pairs+-- satisfy the predicate, and those whose don't.+partitionWithKey+  :: forall s k a. Hashable k -- TODO: this is only used in the proof+  => (Key s k -> a -> Bool)+  -> HashMap s k a+  -> Some2HashMapWith (PartitionProof 'Hashed s k) k a a+partitionWithKey p (HashMap m) = Some2HashMapWith+  (HashMap $ HashMap.filterWithKey (p . unsafeKey) m)+  (HashMap $ HashMap.filterWithKey ((not .) . p . unsafeKey) m)+  $ PartitionProof+    do \k -> case HashMap.lookup (unrefine k) m of+        Nothing -> error+          "partitionWithKey: bug: Data.HashMap.Refined has been subverted"+        Just x -> if p k x+          then Left $ unsafeKey $ unrefine k+          else Right $ unsafeKey $ unrefine k+    unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | If elements of @s@ can be weakened to elements of @t@ and vice versa, then+-- @s@ and @t@ actually stand for the same set and @'Key' s@ can be safely+-- interconverted with @'Key' t@.+--+-- The requirement that the weakenings are natural transformations ensures that+-- they don't actually alter the keys. To build these you can compose ':->''s+-- from proofs returned by functions in this module, or "Refined" functions like+-- 'andLeft' or 'leftOr'.+castKey+  :: forall s t k. (forall x. Key s x -> Key t x)+  -> (forall x. Key t x -> Key s x)+  -> Coercion (Key s k) (Key t k)+castKey = castRefined++-- | If keys can be interconverted (e.g. as proved by 'castKey'), then the maps+-- can be interconverted too. For example, 'zipWithKey' can be implemented via+-- 'Data.HashMap.Refined.intersectionWithKey' by proving that the set of keys+-- remains unchanged:+--+-- @+-- 'zipWithKey'+--   :: forall s k a b c. 'Hashable' k+--   => ('Key' s k -> a -> b -> c) -> 'HashMap' s k a -> 'HashMap' s k b -> 'HashMap' s k c+-- 'zipWithKey' f m1 m2+--   | v'SomeHashMapWith' @r m proof <- 'Data.HashMap.Refined.intersectionWithKey' (f . 'andLeft') m1 m2+--   , v'IntersectionProof' p1 p2 <- proof+--   , ( v'Coercion' :: t'Coercion' ('HashMap' r k c) ('HashMap' s k c))+--     <- app $ 'cast' $ 'castKey' ('andLeft' . p1) (p2 'id' 'id')+--   = 'coerce' m+--   where+--     app :: t'Coercion' f g -> t'Coercion' (f x) (g x)+--     app v'Coercion' = v'Coercion'+-- @+cast+  :: forall s t k. (forall x. Coercion (Key s x) (Key t x))+  -> Coercion (HashMap s k) (HashMap t k)+cast Coercion = Coercion++instance FunctorWithIndex (Key s k) (HashMap s k) where+  imap = mapWithKey++instance FoldableWithIndex (Key s k) (HashMap s k) where+  ifoldMap = foldMapWithKey++instance TraversableWithIndex (Key s k) (HashMap s k) where+  itraverse = traverseWithKey++-- | Similar to the instance for functions -- zip corresponding keys. To use+-- '<*>'/'liftA2' without 'KnownSet' see 'zipWithKey'.+instance (Hashable k, KnownHashSet s k) => Applicative (HashMap s k) where+  pure x = fromSet \_ -> x+  (<*>) = zipWithKey (const id)++-- | @'bind' m f@ is a map that for each key @k :: 'Key' s k@, contains the+-- value @f (m '!' k) '!' k@, similar to @'>>='@ for functions.+bind+  :: forall s k a b. Hashable k+  => HashMap s k a -> (a -> HashMap s k b) -> HashMap s k b+bind m f = mapWithKey (\k x -> f x ! k) m++-- | Similar to the instance for functions. To use '>>=' without 'KnownSet' see+-- 'bind'.+instance (Hashable k, KnownHashSet s k) => Monad (HashMap s k) where+  (>>=) = bind++-- | Similar to the instance for functions. See also+-- 'Data.HashMap.Refined.backpermuteKeys'.+instance (Hashable k, KnownHashSet s k)+  => MonadReader (Key s k) (HashMap s k) where+  ask = fromSet id+  local f m = mapWithKey (\k _ -> m ! f k) m++-- | Append the values at the corresponding keys+instance (Hashable k, Semigroup a) => Semigroup (HashMap s k a) where+  (<>) = zipWithKey (const (<>))++instance (Hashable k, KnownHashSet s k, Monoid a)+  => Monoid (HashMap s k a) where+  mempty = fromSet \_ -> mempty++-- | Similar to the instance for functions+instance (Hashable k, KnownHashSet s k) => Distributive (HashMap s k) where+  collect = collectRep+  distribute = distributeRep++-- | Witness isomorphism with functions from @'Key' s k@+instance (Hashable k, KnownHashSet s k) => Representable (HashMap s k) where+  type Rep (HashMap s k) = Key s k+  index = (!)+  tabulate = fromSet
+ src/Data/HashMap/Refined.hs view
@@ -0,0 +1,359 @@+-- | This module defines a way to prove that a key exists in a map, so that the+-- key can be used to index into the map without using a 'Maybe', or manually+-- handling the \"impossible\" case with 'error' or other partial functions.+--+-- To do this, @'HashMap' s k v@ has a type parameter @s@ that identifies its+-- set of keys, so that if another map has the same type parameter, you know+-- that map has the same set of keys. There is @'Key' s k@, a type of keys that+-- have been validated to belong to the set identified by @s@, and for which the+-- operation of indexing into a @'HashMap' s k v@ (only for the same @s@) can+-- proceed without failure (see '!'). The type @s@ itself has no internal+-- structure, rather it is merely a skolem type variable (rank-2 polymorphism+-- 'Control.Monad.ST.runST' trick) introduced by "Data.Reflection".+--+-- Like "Data.HashMap.Lazy", functions in this module are strict in the keys but+-- lazy in the values. The "Data.HashMap.Strict.Refined" module reuses the same+-- 'HashMap' type but provides functions that operate strictly on the values.+--+-- = Warning+-- This module together with "Data.HashMap.Lazy" rely on 'Eq' and 'Hashable'+-- instances being lawful: that '==' is an equivalence relation, and that+-- 'Data.Hashable.hashWithSalt' is defined on the quotient by this equivalence+-- relation; at least for the subset of values that are actually encountered at+-- runtime. If this assumption is violated, this module may not be able to+-- uphold its invariants and may throw errors. In particular beware of NaN in+-- 'Float' and 'Double', and, if using @hashable < 1.3@, beware of @0@ and @-0@.+module Data.HashMap.Refined+  (+  -- * Map type+    Common.HashMap+  , Common.Key+  -- * Existentials and common proofs+  , Common.SomeHashMap(..)+  , Common.withHashMap+  , Common.SomeHashMapWith(..)+  , Common.withHashMapWith+  , Common.Some2HashMapWith(..)+  , Common.with2HashMapWith+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , Common.empty+  , singleton+  , SingletonProof(..)+  , fromSet+  , Common.fromHashMap+  , fromTraversableWithKey+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  , reinsert+  , insertLookupWithKey+  -- * Deletion/Update+  , Common.delete+  , adjust+  , adjustWithKey+  , update+  , updateLookupWithKey+  -- * Query+  , Common.lookup+  , (Common.!)+  , Common.member+  , Common.null+  , Common.isSubmapOfBy+  , SubsetProof(..)+  , Common.disjoint+  , DisjointProof(..)+  -- * Combine+  , zipWithKey+  , bind+  , unionWithKey+  , UnionProof(..)+  , Common.difference+  , DifferenceProof(..)+  , differenceWithKey+  , PartialDifferenceProof(..)+  , intersectionWithKey+  , IntersectionProof(..)+  -- * Traversal+  , mapWithKey+  , traverseWithKey+  , mapAccumLWithKey+  , mapAccumRWithKey+  , mapKeysWith+  , MapProof(..)+  , backpermuteKeys+  -- * Folds+  , Common.foldMapWithKey+  , Common.foldrWithKey+  , Common.foldlWithKey+  , Common.foldrWithKey'+  , Common.foldlWithKey'+  -- * Conversion+  , Common.toMap+  , Common.keysSet+  , Common.toList+  -- * Filter+  , Common.restrictKeys+  , Common.withoutKeys+  , Common.filterWithKey+  , Common.partitionWithKey+  , PartitionProof(..)+  , mapMaybeWithKey+  , mapEitherWithKey+  -- * Casts+  , Common.castKey+  , Common.cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Container.Refined.Hashable+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Functor+import           Data.HashMap.Common.Refined+  ( HashMap(..), Key, unsafeCastKey, unsafeKey, SomeHashMapWith(..)+  , Some2HashMapWith(..), fromSet, (!), zipWithKey, mapWithKey, traverseWithKey+  , bind+  )+import qualified Data.HashMap.Common.Refined as Common+import qualified Data.HashMap.Lazy as HashMap+import           Data.Traversable+import           Data.Traversable.WithIndex+import           Data.Type.Coercion+import           Prelude hiding (lookup, null)+import           Refined+import           Refined.Unsafe+++-- | Create a map with a single key-value pair, and return a proof that the key+-- is in the resulting map.+singleton+  :: forall k a. Hashable k+  => k -> a -> SomeHashMapWith (SingletonProof 'Hashed k) k a+singleton k v = SomeHashMapWith (HashMap $ HashMap.singleton k v)+  $ SingletonProof (unsafeKey k)++-- | Create a map from an arbitrary traversable of key-value pairs.+fromTraversableWithKey+  :: forall t k a. (Traversable t, Hashable k)+  => (k -> a -> a -> a)+  -> t (k, a)+  -> SomeHashMapWith (FromTraversableProof 'Hashed t k) k a+fromTraversableWithKey f xs+  = SomeHashMapWith (HashMap m) $ FromTraversableProof proof+  where+    (m, proof) = mapAccumL+      (\s (k, v)+        -> let !s' = HashMap.insertWith (f k) k v s in (s', unsafeKey k))+      HashMap.empty+      xs++-- | Insert a key-value pair into the map to obtain a potentially larger map,+-- guaranteed to contain the given key. If the key was already present, the+-- associated value is replaced with the supplied value.+insert+  :: forall s k a. Hashable k+  => k -> a -> HashMap s k a -> SomeHashMapWith (InsertProof 'Hashed k s) k a+insert k v (HashMap m) = SomeHashMapWith (HashMap $ HashMap.insert k v m)+  $ InsertProof (unsafeKey k) unsafeSubset++-- | Overwrite a key-value pair that is known to already be in the map. The set+-- of keys remains the same.+reinsert+  :: forall s k a. Hashable k+  => Key s k -> a -> HashMap s k a -> HashMap s k a+reinsert = gcoerceWith (unsafeCastKey @s @k) $ coerce $ HashMap.insert @k @a++-- | Insert a key-value pair into the map using a combining function, and if+-- the key was already present, the old value is returned along with the proof+-- that the key was present.+insertLookupWithKey+  :: forall s k a. Hashable k+  => (Key s k -> a -> a -> a)+  -> k+  -> a+  -> HashMap s k a+  -> (Maybe (Key s k, a), SomeHashMapWith (InsertProof 'Hashed k s) k a)+insertLookupWithKey f k v (HashMap m) =+  ( (unsafeKey k,) <$> HashMap.lookup k m+  , SomeHashMapWith (HashMap $ HashMap.insertWith (f $ unsafeKey k) k v m)+    $ InsertProof (unsafeKey k) unsafeSubset+  )++-- | Update the value at a specific key known the be in the map using the given+-- function. The set of keys remains the same.+adjust+  :: forall s k a. Hashable k+  => (a -> a) -> Key s k -> HashMap s k a -> HashMap s k a+adjust = gcoerceWith (unsafeCastKey @s @k) $ coerce $ HashMap.adjust @k @a++-- | If the given key is in the map, update the associated value using the given+-- function with a proof that the key was in the map; otherwise return the map+-- unchanged. In any case the set of keys remains the same.+adjustWithKey+  :: forall s k a. Hashable k+  => (Key s k -> a -> a) -> k -> HashMap s k a -> HashMap s k a+adjustWithKey f k (HashMap m) = HashMap $ HashMap.adjust (f $ unsafeKey k) k m++-- | Update or delete a key known to be in the map using the given function,+-- returning a potentially smaller map.+update+  :: forall s k a. Hashable k+  => (a -> Maybe a)+  -> Key s k+  -> HashMap s k a+  -> SomeHashMapWith (SupersetProof 'Hashed s) k a+update f k (HashMap m)+  = SomeHashMapWith (HashMap $ HashMap.update f (unrefine k) m)+    $ SupersetProof unsafeSubset++-- | If the given key is in the map, update or delete it using the given+-- function with a proof that the key was in the map; otherwise the map is+-- unchanged. Alongside return the new value if it was updated, or the old value+-- if it was deleted, and a proof that the key was in the map.+updateLookupWithKey+  :: forall s k a. Hashable k+  => (Key s k -> a -> Maybe a)+  -> k+  -> HashMap s k a+  -> (Maybe (Key s k, a), SomeHashMapWith (SupersetProof 'Hashed s) k a)+updateLookupWithKey f k (HashMap m) =+  ( (unsafeKey k,) <$> HashMap.lookup k m+  , SomeHashMapWith (HashMap $ HashMap.update (f $ unsafeKey k) k m)+    $ SupersetProof unsafeSubset+  )++-- | Return the union of two maps, with a given combining function for keys that+-- exist in both maps simultaneously.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+unionWithKey+  :: forall s t k a. Hashable k+  => (Refined (InSet 'Hashed s && InSet 'Hashed t) k -> a -> a -> a)+  -> HashMap s k a+  -> HashMap t k a+  -> SomeHashMapWith (UnionProof 'Hashed s t) k a+unionWithKey f (HashMap m1) (HashMap m2) = SomeHashMapWith+  (HashMap $ HashMap.unionWithKey (f . reallyUnsafeRefine) m1 m2)+  $ UnionProof unsafeSubset unsafeSubsetWith2++-- | For keys that appear in both maps, the given function decides whether the+-- key is removed from the first map.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+differenceWithKey+  :: forall s t k a b. Hashable k+  => (Refined (InSet 'Hashed s && InSet 'Hashed t) k -> a -> b -> Maybe a)+  -> HashMap s k a+  -> HashMap t k b+  -> SomeHashMapWith (PartialDifferenceProof 'Hashed s t) k a+differenceWithKey f (HashMap m1) (HashMap m2) = SomeHashMapWith+  (HashMap $ HashMap.differenceWith+    (\x (k, y) -> f (reallyUnsafeRefine k) x y)+    m1+    (HashMap.mapWithKey (,) m2))+  $ PartialDifferenceProof unsafeSubset unsafeSubset++-- | Return the intersection of two maps with the given combining function.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+intersectionWithKey+  :: forall s t k a b c. Hashable k+  => (Refined (InSet 'Hashed s && InSet 'Hashed t) k -> a -> b -> c)+  -> HashMap s k a+  -> HashMap t k b+  -> SomeHashMapWith (IntersectionProof 'Hashed s t) k c+intersectionWithKey f (HashMap m1) (HashMap m2) = SomeHashMapWith+  (HashMap $ HashMap.intersectionWithKey (f . reallyUnsafeRefine) m1 m2)+  $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Thread an accumularing argument through the map in ascending order of+-- hashes.+mapAccumLWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> HashMap s k b+  -> (a, HashMap s k c)+mapAccumLWithKey f = imapAccumL (flip f)++-- | Thread an accumularing argument through the map in descending order of+-- hashes.+mapAccumRWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> HashMap s k b+  -> (a, HashMap s k c)+mapAccumRWithKey f = imapAccumR (flip f)++-- | @'mapKeysWith' c f m@ applies @f@ to each key of @m@ and collects the+-- results into a new map. For keys that were mapped to the same new key, @c@+-- acts as the combining function for corresponding values.+mapKeysWith+  :: forall s k1 k2 a. Hashable k2+  => (a -> a -> a)+  -> (Key s k1 -> k2)+  -> HashMap s k1 a+  -> SomeHashMapWith (MapProof 'Hashed s k1 k2) k2 a+mapKeysWith f g (HashMap m) = SomeHashMapWith+  (HashMap $ HashMap.fromListWith f+    $ HashMap.foldrWithKey (\k x xs -> (g $ unsafeKey k, x) : xs) [] m)+  $ MapProof (unsafeKey . g) \k2 ->+    case HashMap.lookup (unrefine k2) backMap of+      Nothing -> error+        "mapKeysWith: bug: Data.HashMap.Refined has been subverted"+      Just k1 -> k1+  where+    ~backMap = HashMap.fromList+      [ (k2, unsafeKey k1)+      | k1 <- HashMap.keys m+      , let !k2 = g $ unsafeKey k1+      ]++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect only the 'Just' results, returning a potentially smaller+-- map.+mapMaybeWithKey+  :: forall s k a b. (Key s k -> a -> Maybe b)+  -> HashMap s k a+  -> SomeHashMapWith (SupersetProof 'Hashed s) k b+mapMaybeWithKey f (HashMap m)+  = SomeHashMapWith (HashMap $ HashMap.mapMaybeWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect the 'Left' and 'Right' results into separate (disjoint)+-- maps.+mapEitherWithKey+  :: forall s k a b c. Hashable k -- TODO: this is only used in the proof+  => (Key s k -> a -> Either b c)+  -> HashMap s k a+  -> Some2HashMapWith (PartitionProof 'Hashed s k) k b c+mapEitherWithKey p (HashMap m)+  | m' <- HashMap.mapWithKey (p . unsafeKey) m+  = Some2HashMapWith+    (HashMap $ HashMap.mapMaybe (either Just (const Nothing)) m')+    (HashMap $ HashMap.mapMaybe (either (const Nothing) Just) m')+    $ PartitionProof+      do \k -> case HashMap.lookup (unrefine k) m of+          Nothing -> error+            "mapEitherWithKey: bug: Data.HashMap.Refined has been subverted"+          Just x -> case p k x of+            Left _ -> Left $ unsafeKey $ unrefine k+            Right _ -> Right $ unsafeKey $ unrefine k+      unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Apply the inverse image of the given function to the keys of the given map,+-- so that for all @k :: 'Key' s2 k2@,c+-- @'backpermuteKeys' f m '!' k = m '!' f k@.+--+-- If maps are identified with functions, this computes the composition.+backpermuteKeys+  :: forall s1 s2 k1 k2 a. (Hashable k1, KnownHashSet s2 k2)+  => (Key s2 k2 -> Key s1 k1) -> HashMap s1 k1 a -> HashMap s2 k2 a+backpermuteKeys f m = fromSet \k -> m ! f k
+ src/Data/HashMap/Strict/Refined.hs view
@@ -0,0 +1,398 @@+-- | This module defines a way to prove that a key exists in a map, so that the+-- key can be used to index into the map without using a 'Maybe', or manually+-- handling the \"impossible\" case with 'error' or other partial functions.+--+-- To do this, @'HashMap' s k v@ has a type parameter @s@ that identifies its+-- set of keys, so that if another map has the same type parameter, you know+-- that map has the same set of keys. There is @'Key' s k@, a type of keys that+-- have been validated to belong to the set identified by @s@, and for which the+-- operation of indexing into a @'HashMap' s k v@ (only for the same @s@) can+-- proceed without failure (see '!'). The type @s@ itself has no internal+-- structure, rather it is merely a skolem type variable (rank-2 polymorphism+-- 'Control.Monad.ST.runST' trick) introduced by "Data.Reflection".+--+-- Like "Data.HashMap.Strict", functions in this module are strict in the keys+-- and values. The "Data.HashMap.Refined" module reuses the same 'HashMap' type+-- but provides functions that operate lazily on the values.+--+-- = Warning+-- This module together with "Data.HashMap.Lazy" rely on 'Eq' and 'Hashable'+-- instances being lawful: that '==' is an equivalence relation, and that+-- 'Data.Hashable.hashWithSalt' is defined on the quotient by this equivalence+-- relation; at least for the subset of values that are actually encountered at+-- runtime. If this assumption is violated, this module may not be able to+-- uphold its invariants and may throw errors. In particular beware of NaN in+-- 'Float' and 'Double', and, if using @hashable < 1.3@, beware of @0@ and @-0@.+module Data.HashMap.Strict.Refined+  (+  -- * Map type+    Common.HashMap+  , Common.Key+  -- * Existentials and common proofs+  , Common.SomeHashMap(..)+  , Common.withHashMap+  , Common.SomeHashMapWith(..)+  , Common.withHashMapWith+  , Common.Some2HashMapWith(..)+  , Common.with2HashMapWith+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , Common.empty+  , singleton+  , SingletonProof(..)+  , fromSet+  , Common.fromHashMap+  , fromTraversableWithKey+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  , reinsert+  , insertLookupWithKey+  -- * Deletion/Update+  , Common.delete+  , adjust+  , adjustWithKey+  , update+  , updateLookupWithKey+  -- * Query+  , Common.lookup+  , (Common.!)+  , Common.member+  , Common.null+  , Common.isSubmapOfBy+  , SubsetProof(..)+  , Common.disjoint+  , DisjointProof(..)+  -- * Combine+  , zipWithKey+  , bind+  , unionWithKey+  , UnionProof(..)+  , Common.difference+  , DifferenceProof(..)+  , differenceWithKey+  , PartialDifferenceProof(..)+  , intersectionWithKey+  , IntersectionProof(..)+  -- * Traversal+  , mapWithKey+  , traverseWithKey+  , mapAccumLWithKey+  , mapAccumRWithKey+  , mapKeysWith+  , MapProof(..)+  , backpermuteKeys+  -- * Folds+  , Common.foldMapWithKey+  , Common.foldrWithKey+  , Common.foldlWithKey+  , Common.foldrWithKey'+  , Common.foldlWithKey'+  -- * Conversion+  , Common.toMap+  , Common.keysSet+  , Common.toList+  -- * Filter+  , Common.restrictKeys+  , Common.withoutKeys+  , Common.filterWithKey+  , Common.partitionWithKey+  , PartitionProof(..)+  , mapMaybeWithKey+  , mapEitherWithKey+  -- * Casts+  , Common.castKey+  , Common.cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Container.Refined.Hashable+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Functor+import qualified Data.HashMap.Strict as HashMap+import           Data.HashMap.Common.Refined+  ( HashMap(..), Key, unsafeCastKey, unsafeKey, SomeHashMapWith(..)+  , Some2HashMapWith(..), (!)+  )+import qualified Data.HashMap.Common.Refined as Common+import qualified Data.HashSet as HashSet+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable+import           Data.Traversable.WithIndex+import           Data.Type.Coercion+import           Prelude hiding (lookup, null)+import           Refined+import           Refined.Unsafe+++-- | Create a map with a single key-value pair, and return a proof that the+-- key is in the resulting map.+singleton+  :: forall k a. Hashable k+  => k -> a -> SomeHashMapWith (SingletonProof 'Hashed k) k a+singleton k v = SomeHashMapWith (HashMap $ HashMap.singleton k v)+  $ SingletonProof (unsafeKey k)++-- | Create a map from a set of keys, and a function that for each key computes+-- the corresponding value.+fromSet :: forall s k a. KnownHashSet s k => (Key s k -> a) -> HashMap s k a+fromSet f = HashMap $ HashMap.mapWithKey (\k _ -> f $ unsafeKey k)+  $ HashSet.toMap (reflect $ Proxy @s)++-- | Create a map from an arbitrary traversable of key-value pairs.+fromTraversableWithKey+  :: forall t k a. (Traversable t, Hashable k)+  => (k -> a -> a -> a)+  -> t (k, a)+  -> SomeHashMapWith (FromTraversableProof 'Hashed t k) k a+fromTraversableWithKey f xs = SomeHashMapWith (HashMap m)+  $ FromTraversableProof proof+  where+    (m, proof) = mapAccumL+      (\s (k, v)+        -> let !s' = HashMap.insertWith (f k) k v s in (s', unsafeKey k))+      HashMap.empty+      xs++-- | Insert a key-value pair into the map to obtain a potentially larger map,+-- guaranteed to contain the given key. If the key was already present, the+-- associated value is replaced with the supplied value.+insert+  :: forall s k a. Hashable k+  => k -> a -> HashMap s k a -> SomeHashMapWith (InsertProof 'Hashed k s) k a+insert k v (HashMap m) = SomeHashMapWith (HashMap $ HashMap.insert k v m)+  $ InsertProof (unsafeKey k) unsafeSubset++-- | Overwrite a key-value pair that is known to already be in the map. The set+-- of keys remains the same.+reinsert+  :: forall s k a. Hashable k+  => Key s k -> a -> HashMap s k a -> HashMap s k a+reinsert = gcoerceWith (unsafeCastKey @s @k) $ coerce $ HashMap.insert @k @a++-- | Insert a key-value pair into the map using a combining function, and if+-- the key was already present, the old value is returned along with the proof+-- that the key was present.+insertLookupWithKey+  :: forall s k a. Hashable k+  => (Key s k -> a -> a -> a)+  -> k+  -> a+  -> HashMap s k a+  -> (Maybe (Key s k, a), SomeHashMapWith (InsertProof 'Hashed k s) k a)+insertLookupWithKey f k v (HashMap m) =+  ( (unsafeKey k,) <$> HashMap.lookup k m+  , SomeHashMapWith (HashMap $ HashMap.insertWith (f $ unsafeKey k) k v m)+    $ InsertProof (unsafeKey k) unsafeSubset+  )++-- | Update the value at a specific key known the be in the map using the given+-- function. The set of keys remains the same.+adjust+  :: forall s k a. Hashable k+  => (a -> a) -> Key s k -> HashMap s k a -> HashMap s k a+adjust = gcoerceWith (unsafeCastKey @s @k) $ coerce $ HashMap.adjust @k @a++-- | If the given key is in the map, update the associated value using the given+-- function with a proof that the key was in the map; otherwise return the map+-- unchanged. In any case the set of keys remains the same.+adjustWithKey+  :: forall s k a. Hashable k+  => (Key s k -> a -> a) -> k -> HashMap s k a -> HashMap s k a+adjustWithKey f k (HashMap m) = HashMap $ HashMap.adjust (f $ unsafeKey k) k m++-- | Update or delete a key known to be in the map using the given function,+-- returning a potentially smaller map.+update+  :: forall s k a. Hashable k+  => (a -> Maybe a)+  -> Key s k+  -> HashMap s k a+  -> SomeHashMapWith (SupersetProof 'Hashed s) k a+update f k (HashMap m)+  = SomeHashMapWith (HashMap $ HashMap.update f (unrefine k) m)+    $ SupersetProof unsafeSubset++-- | If the given key is in the map, update or delete it using the given+-- function with a proof that the key was in the map; otherwise the map is+-- unchanged. Alongside return the new value if it was updated, or the old value+-- if it was deleted, and a proof that the key was in the map.+updateLookupWithKey+  :: forall s k a. Hashable k+  => (Key s k -> a -> Maybe a)+  -> k+  -> HashMap s k a+  -> (Maybe (Key s k, a), SomeHashMapWith (SupersetProof 'Hashed s) k a)+updateLookupWithKey f k (HashMap m) =+  ( (unsafeKey k,) <$> HashMap.lookup k m+  , SomeHashMapWith (HashMap $ HashMap.update (f $ unsafeKey k) k m)+    $ SupersetProof unsafeSubset+  )++-- | Given two maps proven to have the same keys, for each key apply the+-- function to the associated values, to obtain a new map with the same keys.+zipWithKey+  :: forall s k a b c. Hashable k+  => (Key s k -> a -> b -> c) -> HashMap s k a -> HashMap s k b -> HashMap s k c+zipWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.intersectionWithKey @k @a @b @c++-- | Return the union of two maps, with a given combining function for keys that+-- exist in both maps simultaneously.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+unionWithKey+  :: forall s t k a. Hashable k+  => (Refined (InSet 'Hashed s && InSet 'Hashed t) k -> a -> a -> a)+  -> HashMap s k a+  -> HashMap t k a+  -> SomeHashMapWith (UnionProof 'Hashed s t) k a+unionWithKey f (HashMap m1) (HashMap m2) = SomeHashMapWith+  (HashMap $ HashMap.unionWithKey (f . reallyUnsafeRefine) m1 m2)+  $ UnionProof unsafeSubset unsafeSubsetWith2++-- | For keys that appear in both maps, the given function decides whether the+-- key is removed from the first map.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+differenceWithKey+  :: forall s t k a b. Hashable k+  => (Refined (InSet 'Hashed s && InSet 'Hashed t) k -> a -> b -> Maybe a)+  -> HashMap s k a+  -> HashMap t k b+  -> SomeHashMapWith (PartialDifferenceProof 'Hashed s t) k a+differenceWithKey f (HashMap m1) (HashMap m2) = SomeHashMapWith+  (HashMap $ HashMap.differenceWith+    (\x (k, y) -> f (reallyUnsafeRefine k) x y)+    m1+    (HashMap.mapWithKey (,) m2))+  $ PartialDifferenceProof unsafeSubset unsafeSubset++-- | Return the intersection of two maps with the given combining function.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+intersectionWithKey+  :: forall s t k a b c. Hashable k+  => (Refined (InSet 'Hashed s && InSet 'Hashed t) k -> a -> b -> c)+  -> HashMap s k a+  -> HashMap t k b+  -> SomeHashMapWith (IntersectionProof 'Hashed s t) k c+intersectionWithKey f (HashMap m1) (HashMap m2) = SomeHashMapWith+  (HashMap $ HashMap.intersectionWithKey (f . reallyUnsafeRefine) m1 m2)+  $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Apply a function to all values in a map, together with their corresponding+-- keys, that are proven to be in the map. The set of keys remains the same.+mapWithKey+  :: forall s k a b. (Key s k -> a -> b) -> HashMap s k a -> HashMap s k b+mapWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ HashMap.mapWithKey @k @a @b++-- | Map an 'Applicative' transformation with access to each value's+-- corresponding key and a proof that it is in the map. The set of keys remains+-- unchanged.+traverseWithKey+  :: forall s f k a b. Applicative f+  => (Key s k -> a -> f b) -> HashMap s k a -> f (HashMap s k b)+traverseWithKey f (HashMap m)+  = HashMap <$> HashMap.traverseWithKey (f . unsafeKey) m++-- | Thread an accumularing argument through the map in ascending order of+-- hashes.+mapAccumLWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> HashMap s k b+  -> (a, HashMap s k c)+mapAccumLWithKey f = imapAccumL (flip f)++-- | Thread an accumularing argument through the map in descending order of+-- hashes.+mapAccumRWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> HashMap s k b+  -> (a, HashMap s k c)+mapAccumRWithKey f = imapAccumR (flip f)++-- | @'mapKeysWith' c f m@ applies @f@ to each key of @m@ and collects the+-- results into a new map. For keys that were mapped to the same new key, @c@+-- acts as the combining function for corresponding values.+mapKeysWith+  :: forall s k1 k2 a. Hashable k2+  => (a -> a -> a)+  -> (Key s k1 -> k2)+  -> HashMap s k1 a+  -> SomeHashMapWith (MapProof 'Hashed s k1 k2) k2 a+mapKeysWith f g (HashMap m) = SomeHashMapWith+  (HashMap $ HashMap.fromListWith f+    $ HashMap.foldrWithKey (\k x xs -> (g $ unsafeKey k, x) : xs) [] m)+  $ MapProof (unsafeKey . g) \k2 ->+    case HashMap.lookup (unrefine k2) backMap of+      Nothing -> error+        "mapKeysWith: bug: Data.HashMap.Refined has been subverted"+      Just k1 -> k1+  where+    ~backMap = HashMap.fromList+      [ (k2, unsafeKey k1)+      | k1 <- HashMap.keys m+      , let !k2 = g $ unsafeKey k1+      ]++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect only the 'Just' results, returning a potentially smaller+-- map.+mapMaybeWithKey+  :: forall s k a b. (Key s k -> a -> Maybe b)+  -> HashMap s k a+  -> SomeHashMapWith (SupersetProof 'Hashed s) k b+mapMaybeWithKey f (HashMap m)+  = SomeHashMapWith (HashMap $ HashMap.mapMaybeWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect the 'Left' and 'Right' results into separate (disjoint)+-- maps.+mapEitherWithKey+  :: forall s k a b c. Hashable k -- TODO: this is only used in the proof+  => (Key s k -> a -> Either b c)+  -> HashMap s k a+  -> Some2HashMapWith (PartitionProof 'Hashed s k) k b c+mapEitherWithKey p (HashMap m)+  | m' <- HashMap.mapWithKey (p . unsafeKey) m+  = Some2HashMapWith+    (HashMap $ HashMap.mapMaybe (either Just (const Nothing)) m')+    (HashMap $ HashMap.mapMaybe (either (const Nothing) Just) m')+    $ PartitionProof+      do \k -> case HashMap.lookup (unrefine k) m of+          Nothing -> error+            "mapEitherWithKey: bug: Data.HashMap.Refined has been subverted"+          Just x -> case p k x of+            Left _ -> Left $ unsafeKey $ unrefine k+            Right _ -> Right $ unsafeKey $ unrefine k+      unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | @'bind' m f@ is a map that for each key @k :: 'Key' s k@, contains the+-- value @f (m '!' k) '!' k@, similar to @'>>='@ for functions.+bind+  :: forall s k a b. Hashable k+  => HashMap s k a -> (a -> HashMap s k b) -> HashMap s k b+bind m f = mapWithKey (\k x -> f x ! k) m++-- | Apply the inverse image of the given function to the keys of the given map,+-- so that for all @k :: 'Key' s2 k2@,+-- @'backpermuteKeys' f m '!' k = m '!' f k@.+--+-- If maps are identified with functions, this computes the composition.+backpermuteKeys+  :: forall s1 s2 k1 k2 a. (Hashable k1, KnownHashSet s2 k2)+  => (Key s2 k2 -> Key s1 k1) -> HashMap s1 k1 a -> HashMap s2 k2 a+backpermuteKeys f m = fromSet \k -> m ! f k
+ src/Data/HashSet/Refined.hs view
@@ -0,0 +1,418 @@+{-# LANGUAGE CPP #-}+-- | This module implements a way of tracking the contents of a+-- 'Data.HashSet.HashSet' at the type level, and provides utilities for+-- manipulating such sets.+--+-- The contents of a set are associated with a type parameter, e.g. @s@, so that+-- whenever you see the same type parameter, you know you are working with the+-- same set. The type @s@ itself has no internal structure, rather it is merely+-- a skolem type variable (rank-2 polymorphism 'Control.Monad.ST.runST' trick)+-- introduced by "Data.Reflection".+--+-- = Warning+-- This module together with "Data.HashSet" rely on 'Eq' and 'Hashable'+-- instances being lawful: that '==' is an equivalence relation, and that+-- 'Data.Hashable.hashWithSalt' is defined on the quotient by this equivalence+-- relation; at least for the subset of values that are actually encountered at+-- runtime. If this assumption is violated, this module may not be able to+-- uphold its invariants and may throw errors. In particular beware of NaN in+-- 'Float' and 'Double', and, if using @hashable < 1.3@, beware of @0@ and @-0@.+module Data.HashSet.Refined+  (+  -- * Set type+    KnownHashSet+  , HashSet+  -- * Refinement type+  , InSet(..)+  , Flavor(Hashed)+  , Element+  , revealPredicate+  -- * Existentials and common proofs+  , SomeHashSet(..)+  , withHashSet+  , SomeHashSetWith(..)+  , withHashSetWith+  , Some2HashSetWith(..)+  , with2HashSetWith+  , (:->)+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , empty+  , singleton+  , SingletonProof(..)+  , fromHashSet+  , fromTraversable+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  -- * Deletion+  , delete+  -- * Query+  , member+  , null+  , isSubsetOf+  , SubsetProof(..)+  , disjoint+  , DisjointProof(..)+  -- * Combine+  , union+  , UnionProof(..)+  , difference+  , DifferenceProof(..)+  , intersection+  , IntersectionProof(..)+  -- * Filter+  , filter+  , partition+  , PartitionProof(..)+  -- * Map+  , map+  , MapProof(..)+  -- * Folds+  , foldMap+  , foldr+  , foldl+  , foldr'+  , foldl'+  -- * Conversion+  , toList+  , asSet+  , asIntSet+  -- * Casts+  , castElement+  , cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Conversion+import           Data.Container.Refined.Hashable+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import qualified Data.Foldable as Foldable+import qualified Data.HashMap.Lazy as HashMap+import qualified Data.HashSet as HashSet+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable+import           Data.Type.Coercion+import           Data.Type.Equality ((:~:)(..))+import           Data.Typeable (Typeable)+import           GHC.Exts (Proxy#, proxy#)+import           Prelude hiding (filter, foldl, foldMap, foldr, map, null)+import           Refined+import           Refined.Unsafe+import           Unsafe.Coerce+++-- | To use "Refined" machinery that uses the 'Predicate' typeclass you will+-- need to pattern match on this 'Dict'.+--+-- The reason is that in the default /fast/ implementation of reflection, we+-- don't have @'Typeable' s@, which "Refined" wants for pretty-printing+-- exceptions. We /can/ provide @'Typeable' s@, but at the cost of using the+-- /slow/ implementation of reflection.+revealPredicate+  :: forall s a. (Typeable a, Hashable a, KnownHashSet s a)+  => Dict (Predicate (InSet 'Hashed s) a)+revealPredicate = reifyTypeable (reflect (Proxy @s))+  \(_ :: Proxy s') ->+    reflect (Proxy @s') `seq`+    --  ^ Work around https://github.com/ekmett/reflection/issues/54+      case unsafeCoerce Refl :: s :~: s' of+        Refl -> Dict++-- | @'Element' s a@ is a value of type @a@ that has been verified to be an+-- element of @s@.+--+-- Thus, @'Element' s a@ is a \"refinement\" type of @a@, and this library+-- integrates with an implementation of refimenement types in "Refined", so+-- the machinery from there can be used to manipulate 'Element's (however see+-- 'revealPredicate').+--+-- The underlying @a@ value can be obtained with 'unrefine'. An @a@ can be+-- validated into an @'Element' s a@ with 'member'.+type Element s = Refined (InSet 'Hashed s)++unsafeCastElement :: forall s a. Coercion a (Element s a)+unsafeCastElement = reallyUnsafeUnderlyingRefined++unsafeElement :: a -> Element s a+unsafeElement = coerceWith unsafeCastElement++-- | An existential wrapper for an as-yet-unknown set. Pattern maching on it+-- gives you a way to refer to the set, e.g.+--+-- @+-- case 'fromHashSet' ... of+--   'SomeHashSet' \@s _ -> doSomethingWith \@s+--+-- case 'fromHashSet' ... of+--   'SomeHashSet' (_ :: 'Proxy#' s) -> doSomethingWith \@s+-- @+data SomeHashSet a where+  SomeHashSet :: forall s a. KnownHashSet s a => Proxy# s -> SomeHashSet a++-- | Apply an unknown set to a continuation that can accept any set. This gives+-- you a way to refer to the set (the parameter @s@), e.g.:+--+-- @+-- 'withHashSet' ('fromHashSet' ...) $ \(_ :: 'Proxy' s) -> doSomethingWith \@s+-- @+withHashSet+  :: forall a r. SomeHashSet a+  -> (forall s. KnownHashSet s a => Proxy s -> r)+  -> r+withHashSet (SomeHashSet (_ :: Proxy# s)) k = k $ Proxy @s++-- | Construct a set from a regular 'Data.HashSet.HashSet'.+fromHashSet :: forall a. HashSet.HashSet a -> SomeHashSet a+fromHashSet s = reify s \(_ :: Proxy s) -> SomeHashSet @s proxy#++-- | An existential wrapper for an as-yet-unknown set, together with a proof of+-- some fact @p@ about the set. Pattern matching on it gives you a way to refer+-- to the set (the parameter @s@). Most functions will return a set in this way,+-- together with a proof that somehow relates the set to the function's inputs.+data SomeHashSetWith p a where+  SomeHashSetWith+    :: forall s a p. KnownHashSet s a => !(p s) -> SomeHashSetWith p a++-- | Apply an unknown set with proof to a continuation that can accept any set+-- satisfying the proof. This gives you a way to refer to the set (the parameter+-- @s@).+withHashSetWith+  :: forall a r p. SomeHashSetWith p a+  -> (forall s. KnownHashSet s a => p s -> r)+  -> r+withHashSetWith (SomeHashSetWith p) k = k p++-- | An existential wrapper for an as-yet-unknown pair of sets, together with+-- a proof of some fact @p@ relating them.+data Some2HashSetWith p a where+  Some2HashSetWith+    :: forall s t a p. (KnownHashSet s a, KnownHashSet t a)+    => !(p s t) -> Some2HashSetWith p a++-- | Apply a pair of unknown sets with proof to a continuation that can accept+-- any pair of sets satisfying the proof. This gives you a way to refer to the+-- sets (the parameters @s@ and @t@).+with2HashSetWith+  :: forall a r p. Some2HashSetWith p a+  -> (forall s t. (KnownHashSet s a, KnownHashSet t a) => p s t -> r)+  -> r+with2HashSetWith (Some2HashSetWith p) k = k p++-- | An empty set.+empty :: forall a. SomeHashSetWith (EmptyProof 'Hashed) a+empty = reify HashSet.empty \(_ :: Proxy r)+  -> SomeHashSetWith @r $ EmptyProof unsafeSubset++-- | Create a set with a single element.+singleton+  :: forall a. Hashable a => a -> SomeHashSetWith (SingletonProof 'Hashed a) a+singleton x = reify (HashSet.singleton x) \(_ :: Proxy r)+  -> SomeHashSetWith @r $ SingletonProof $ unsafeElement x++-- | Create a set from the elements of an arbitrary traversable.+fromTraversable+  :: forall t a. (Traversable t, Hashable a)+  => t a -> SomeHashSetWith (FromTraversableProof 'Hashed t a) a+fromTraversable xs = reify set \(_ :: Proxy r)+  -> SomeHashSetWith @r $ FromTraversableProof+    $ unsafeCoerce @(t (Element _ a)) @(t (Element r a)) proof+  where+    (set, proof) = mapAccumL+      (\s x -> let !s' = HashSet.insert x s in (s', unsafeElement x))+      HashSet.empty+      xs++-- | Insert an element in a set.+insert+  :: forall s a. (Hashable a, KnownHashSet s a)+  => a -> SomeHashSetWith (InsertProof 'Hashed a s) a+insert x = reify (HashSet.insert x $ reflect $ Proxy @s) \(_ :: Proxy r)+  -> SomeHashSetWith @r $ InsertProof (unsafeElement x) unsafeSubset++-- | Delete an element from a set.+delete+  :: forall s a. (Hashable a, KnownHashSet s a)+  => a -> SomeHashSetWith (SupersetProof 'Hashed s) a+delete x = reify (HashSet.delete x $ reflect $ Proxy @s) \(_ :: Proxy r)+  -> SomeHashSetWith @s $ SupersetProof unsafeSubset++-- | If an element is in the set, return the proof that it is.+member :: forall s a. (Hashable a, KnownHashSet s a) => a -> Maybe (Element s a)+member x+  | x `HashSet.member` reflect (Proxy @s) = Just $ unsafeElement x+  | otherwise = Nothing++-- | If the set is empty, return the proof that it is.+null :: forall s a. KnownHashSet s a => Maybe (EmptyProof 'Hashed s)+null+  | HashSet.null $ reflect $ Proxy @s = Just $ EmptyProof unsafeSubset+  | otherwise = Nothing++-- | If @s@ is a subset of @t@ (or is equal to), return a proof of that.+isSubsetOf+  :: forall s t a. (Hashable a, KnownHashSet s a, KnownHashSet t a)+  => Maybe (SubsetProof 'Hashed s t)+isSubsetOf+#if MIN_VERSION_unordered_containers(0, 2, 12)+  | reflect (Proxy @s) `HashSet.isSubsetOf` reflect (Proxy @t)+#else+  | all (`HashSet.member` reflect (Proxy @t)) (reflect (Proxy @s))+#endif+  = Just $ SubsetProof unsafeSubset+  | otherwise = Nothing++-- | If @s@ and @t@ are disjoint (i.e. their intersection is empty), return a+-- proof of that.+disjoint+  :: forall s t a. (Hashable a, KnownHashSet s a, KnownHashSet t a)+  => Maybe (DisjointProof 'Hashed s t)+disjoint+  | HashSet.null+    $ HashSet.intersection (reflect $ Proxy @s) (reflect $ Proxy @t)+  = Just $ DisjointProof \f g -> unsafeSubsetWith2 f g+  | otherwise = Nothing++-- | The union of two sets.+union+  :: forall s t a. (Hashable a, KnownHashSet s a, KnownHashSet t a)+  => SomeHashSetWith (UnionProof 'Hashed s t) a+union = reify (reflect (Proxy @s) `HashSet.union` reflect (Proxy @t))+  \(_ :: Proxy r) -> SomeHashSetWith @r+    $ UnionProof unsafeSubset unsafeSubsetWith2++-- unions :: ?++-- | HashSet with elements of @s@ that are not in @t@.+difference+  :: forall s t a. (Hashable a, KnownHashSet s a, KnownHashSet t a)+  => SomeHashSetWith (DifferenceProof 'Hashed s t) a+difference = reify (reflect (Proxy @s) `HashSet.difference` reflect (Proxy @t))+  \(_ :: Proxy r) -> SomeHashSetWith @r+    $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Intersection of two sets.+intersection+  :: forall s t a. (Hashable a, KnownHashSet s a, KnownHashSet t a)+  => SomeHashSetWith (IntersectionProof 'Hashed s t) a+intersection+  = reify (reflect (Proxy @s) `HashSet.intersection` reflect (Proxy @t))+    \(_ :: Proxy r) -> SomeHashSetWith @r+      $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Return a subset of elements that satisfy the given predicate.+filter+  :: forall s a. KnownHashSet s a+  => (Element s a -> Bool) -> SomeHashSetWith (SupersetProof 'Hashed s) a+filter p = reify (HashSet.filter (p . unsafeElement) $ reflect $ Proxy @s)+  \(_ :: Proxy r) -> SomeHashSetWith @r $ SupersetProof unsafeSubset++-- | Partition a set into two disjoint subsets: those that satisfy the+-- predicate, and those that don't.+partition+  :: forall s a. KnownHashSet s a+  => (Element s a -> Bool) -> Some2HashSetWith (PartitionProof 'Hashed s a) a+partition p = reify (HashSet.filter (p . unsafeElement) $ reflect $ Proxy @s)+  \(_ :: Proxy r)+    -> reify (HashSet.filter (not . p . unsafeElement) $ reflect $ Proxy @s)+      \(_ :: Proxy q)+        -> Some2HashSetWith @s @r $ PartitionProof+          do \x -> if p x+              then Left $ unsafeElement $ unrefine x+              else Right $ unsafeElement $ unrefine x+          unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Apply the given function to each element of the set and collect the+-- results. Note that the resulting set can be smaller.+map+  :: forall s a b. (Hashable b, KnownHashSet s a)+  => (Element s a -> b) -> SomeHashSetWith (MapProof 'Hashed s a b) b+map f = reify (HashMap.keysSet m)+  \(_ :: Proxy r) -> SomeHashSetWith @r+    $ MapProof (unsafeElement . f) \y -> case HashMap.lookup (unrefine y) m of+      Nothing -> error "map: bug: Data.HashSet.Refined has been subverted"+      Just x -> x+  where+    !m = HashMap.fromList+      [ (y, unsafeElement x)+      | x <- HashSet.toList $ reflect $ Proxy @s+      , let !y = f $ unsafeElement x+      ]++-- | Map each element of @s@ into a monoid (with proof that it was an element),+-- and combine the results using 'Data.Monoid.<>'.+foldMap :: forall s a m. (KnownHashSet s a, Monoid m) => (Element s a -> m) -> m+foldMap f = Foldable.foldMap (f . unsafeElement) $ reflect $ Proxy @s++-- | Right associative fold with a lazy accumulator.+foldr :: forall s a b. KnownHashSet s a => (Element s a -> b -> b) -> b -> b+foldr f z = HashSet.foldr (f . unsafeElement) z $ reflect $ Proxy @s++-- | Left associative fold with a lazy accumulator.+foldl :: forall s a b. KnownHashSet s a => (b -> Element s a -> b) -> b -> b+foldl f z = Foldable.foldl ((. unsafeElement) . f) z $ reflect $ Proxy @s++-- | Right associative fold with a strict accumulator.+foldr' :: forall s a b. KnownHashSet s a => (Element s a -> b -> b) -> b -> b+foldr' f z = Foldable.foldr' (f . unsafeElement) z $ reflect $ Proxy @s++-- | Left associative fold with a strict accumulator.+foldl' :: forall s a b. KnownHashSet s a => (b -> Element s a -> b) -> b -> b+foldl' f z = HashSet.foldl' ((. unsafeElement) . f) z $ reflect $ Proxy @s++-- | List of elements in the set.+toList :: forall s a. KnownHashSet s a => [Element s a]+toList = gcoerceWith (unsafeCastElement @s @a) $ coerce+  $ HashSet.toList $ reflect $ Proxy @s+++-- | Convert an 'IntSet' into a 'Set', retaining its set of elements, which can+-- be converted with 'castFlavor'.+asSet :: forall s a. (Ord a, KnownHashSet s a) => Set s a+asSet = hashSet2Set++-- | Convert an 'IntSet' into a 'HashSet', retaining its set of elements, which+-- can be converted with 'castFlavor'.+asIntSet :: forall s. KnownHashSet s Int => IntSet s+asIntSet = hashSet2IntSet++-- | If elements of @s@ can be weakened to elements of @t@ and vice versa, then+-- @s@ and @t@ actually stand for the same set and @'Element' s@ can be safely+-- interconverted with @'Element' t@.+--+-- The requirement that the weakenings are natural transformations ensures that+-- they don't actually alter the elements. To build these you can compose+-- ':->''s from proofs returned by functions in this module, or "Refined"+-- functions like 'andLeft' or 'leftOr'.+castElement+  :: forall s t a. (forall x. Element s x -> Element t x)+  -> (forall x. Element t x -> Element s x)+  -> Coercion (Element s a) (Element t a)+castElement = castRefined++-- | If elements can be interconverted (e.g. as proved by 'castElement'), then+-- the sets can be interconverted too. For example we can establish that the+-- intersection of a set with itself is interconvertible with that set:+--+-- @+-- castIntersection+--   :: t'IntersectionProof' ''Data.HashSet.Refined.Hashed' s s r+--   -> 'Coercion' ('HashSet' r a) ('HashSet' s a)+-- castIntersection ( v'IntersectionProof' p1 p2)+--   = 'cast' $ 'castElement' ('andLeft' . p1) (p2 'id' 'id')+-- @+cast+  :: forall s t a. (forall x. Coercion (Element s x) (Element t x))+  -> Coercion (HashSet s a) (HashSet t a)+cast Coercion+#if MIN_VERSION_base(4, 15, 0)+  = case unsafeEqualityProof @s @t of UnsafeRefl -> Coercion+#else+  = repr $ unsafeCoerce Refl+#endif
+ src/Data/IntMap/Common/Refined.hs view
@@ -0,0 +1,523 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE UndecidableInstances #-}+module Data.IntMap.Common.Refined where++import           Control.Monad.Reader+import           Control.DeepSeq+import           Data.Coerce+import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Distributive+import           Data.Foldable.WithIndex+import           Data.Functor.Rep+import           Data.Functor.WithIndex+import qualified Data.Hashable as Hashable+import qualified Data.IntMap as IntMap+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable.WithIndex+import           Data.Type.Coercion+import           Data.Type.Equality ((:~:)(..))+import           Refined+import           Refined.Unsafe+import           Unsafe.Coerce++#if MIN_VERSION_containers(0, 6, 7)+#elif MIN_VERSION_containers(0, 6, 2)+import qualified Data.List as List+#elif MIN_VERSION_containers(0, 5, 8)+import           Data.Functor.Const (Const(..))+import qualified Data.List as List+import           Data.Monoid (Any(..))+import qualified Data.IntMap.Merge.Lazy as IntMap+#else+import qualified Data.IntMap.Strict as IntMapStrict+import qualified Data.List as List+#endif+++-- | A wrapper around a regular 'Data.IntMap.IntMap' with a type parameter @s@+-- identifying the set of keys present in the map.+--+-- An t'Int' key may not be present in the map, but a @'Key' s@ is guaranteed to+-- be present (if the @s@ parameters match). Thus the map is isomorphic to a+-- (total) function @'Key' s -> a@, which motivates many of the instances below.+--+-- An 'IntMap' always knows its set of keys, so given @'IntMap' s a@ we can+-- always derive @'KnownIntSet' s@ by pattern matching on the 'Dict' returned by+-- 'keysSet'.+newtype IntMap s a = IntMap (IntMap.IntMap a)+  deriving newtype (Eq, Ord, Show, Functor, Foldable, NFData)+#if MIN_VERSION_hashable(1, 3, 4)+  deriving newtype (Hashable.Hashable)+#endif+  deriving stock (Traversable)+type role IntMap nominal representational++-- | Convert to a regular 'Data.IntMap.IntMap', forgetting its set of keys.+toIntMap :: forall s a. IntMap s a -> IntMap.IntMap a+toIntMap (IntMap m) = m++-- | @'Key' s@ is a key of type t'Int' that has been verified to be an element+-- of the set @s@, and thus verified to be present in all @'IntMap' s k@ maps.+--+-- Thus, @'Key' s@ is a \"refinement\" type of t'Int', and this library+-- integrates with an implementation of refimenement types in "Refined", so+-- the machinery from there can be used to manipulate 'Key's (however see+-- 'Data.IntSet.Refined.revealPredicate').+--+-- The underlying t'Int' value can be obtained with 'unrefine'. An t'Int' can be+-- validated into an @'Key' s@ with 'member'.+type Key s = Refined (InSet 'Int s) Int++unsafeCastKey :: forall s. Coercion Int (Key s)+unsafeCastKey = reallyUnsafeUnderlyingRefined++unsafeKey :: Int -> Key s+unsafeKey = coerceWith unsafeCastKey++-- | An existential wrapper for an 'IntMap' with an as-yet-unknown set of keys.+-- Pattern maching on it gives you a way to refer to the set (the parameter+-- @s@), e.g.+--+-- @+-- case 'fromIntMap' ... of+--   'SomeIntMap' \@s m -> doSomethingWith \@s+--+-- case 'fromIntMap' ... of+--   'SomeIntMap' (m :: 'IntMap' s a) -> doSomethingWith \@s+-- @+data SomeIntMap a where+  SomeIntMap :: forall s a. !(IntMap s a) -> SomeIntMap a++-- | Apply a map with an unknown set of keys to a continuation that can accept+-- a map with any set of keys. This gives you a way to refer to the set (the+-- parameter @s@), e.g.:+--+-- @+-- 'withIntMap' ('fromIntMap' ...) $ \(m :: 'IntMap' s a) -> doSomethingWith \@s+-- @+withIntMap :: forall a r. SomeIntMap a -> (forall s. IntMap s a -> r) -> r+withIntMap (SomeIntMap m) k = k m++-- | Construct a map from a regular 'Data.IntMap.IntMap'.+fromIntMap :: forall a. IntMap.IntMap a -> SomeIntMap a+fromIntMap m = SomeIntMap (IntMap m)++-- | An existential wrapper for an 'IntMap' with an as-yet-unknown set of keys,+-- together with a proof of some fact @p@ about the set. Pattern matching on it+-- gives you a way to refer to the set (the parameter @s@). Functions that+-- change the set of keys in a map will return the map in this way, together+-- with a proof that somehow relates the keys set to the function's inputs.+data SomeIntMapWith p a where+  SomeIntMapWith :: forall s a p. !(IntMap s a) -> !(p s) -> SomeIntMapWith p a++-- | Apply a map with proof for an unknown set of keys to a continuation that+-- can accept a map with any set of keys satisfying the proof. This gives you a+-- way to refer to the set (the parameter @s@).+withIntMapWith+  :: forall a r p. SomeIntMapWith p a -> (forall s. IntMap s a -> p s -> r) -> r+withIntMapWith (SomeIntMapWith m p) k = k m p++-- | An existential wrapper for a pair of maps with as-yet-unknown sets of keys,+-- together with a proof of some fact @p@ relating them.+data Some2IntMapWith p a b where+  Some2IntMapWith+    :: forall s t a b p. !(IntMap s a)+    -> !(IntMap t b)+    -> !(p s t)+    -> Some2IntMapWith p a b++-- | Apply a pair of maps with proof for unknown sets of keys to a continuation+-- that can accept any pair of maps with any sets of keys satisfying the proof.+-- This gives you a way to refer to the sets (the parameters @s@ and @t@).+with2IntMapWith+  :: forall a b r p. Some2IntMapWith p a b+  -> (forall s t. IntMap s a -> IntMap t b -> p s t -> r)+  -> r+with2IntMapWith (Some2IntMapWith m1 m2 p) k = k m1 m2 p++-- | An empty map.+empty :: forall a. SomeIntMapWith (EmptyProof 'Int) a+empty = SomeIntMapWith (IntMap IntMap.empty) $ EmptyProof unsafeSubset++-- | Create a map from a set of keys, and a function that for each key computes+-- the corresponding value.+fromSet :: forall s a. KnownIntSet s => (Key s -> a) -> IntMap s a+fromSet f = IntMap $ IntMap.fromSet (f . unsafeKey) (reflect $ Proxy @s)++-- | Delete a key and its value from the map if present, returning a potentially+-- smaller map.+delete :: forall s a. Int -> IntMap s a -> SomeIntMapWith (SupersetProof 'Int s) a+delete k (IntMap m) = SomeIntMapWith (IntMap $ IntMap.delete k m)+  $ SupersetProof unsafeSubset++-- | If the key is in the map, return the proof of this, and the associated+-- value; otherwise return 'Nothing'.+lookup :: forall s a. Int -> IntMap s a -> Maybe (Key s, a)+lookup k (IntMap m) = (unsafeKey k,) <$> IntMap.lookup k m++-- | Given a key that is proven to be in the map, return the associated value.+--+-- Unlike 'Data.IntMap.!' from "Data.IntMap", this function is total, as it is+-- impossible to obtain a @'Key' s@ for a key that is not in the map+-- @'IntMap' s a@.+(!) :: forall s a. IntMap s a -> Key s -> a+(!) (IntMap m) k = case IntMap.lookup (unrefine k) m of+  Nothing -> error "(!): bug: Data.IntMap.Refined has been subverted"+  Just x -> x++-- | If a key is in the map, return the proof that it is.+member :: forall s a. Int -> IntMap s a -> Maybe (Key s)+member k (IntMap m)+  | k `IntMap.member` m = Just (unsafeKey k)+  | otherwise = Nothing++-- | Find the largest key smaller than the given one, and return the+-- associated key-value pair.+lookupLT :: forall s a. Int -> IntMap s a -> Maybe (Key s, a)+lookupLT = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.lookupLT @a++-- | Find the smallest key greater than the given one, and return the+-- associated key-value pair.+lookupGT :: forall s a. Int -> IntMap s a -> Maybe (Key s, a)+lookupGT = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.lookupGT @a++-- | Find the largest key smaller or equal to the given one, and return the+-- associated key-value pair.+lookupLE :: forall s a. Int -> IntMap s a -> Maybe (Key s, a)+lookupLE = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.lookupLE @a++-- | Find the smallest key greater or equal to the given one, and return the+-- associated key-value pair.+lookupGE :: forall s a. Int -> IntMap s a -> Maybe (Key s, a)+lookupGE = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.lookupGE @a++-- | If a map is empty, return a proof that it is.+null :: forall s a. IntMap s a -> Maybe (EmptyProof 'Int s)+null (IntMap m)+  | IntMap.null m = Just $ EmptyProof unsafeSubset+  | otherwise = Nothing++-- | If all keys of the first map are also present in the second map, and the+-- given function returns 'True' for their associated values, return a proof+-- that the keys form a subset.+isSubmapOfBy+  :: forall s t a b. (a -> b -> Bool)+  -> IntMap s a+  -> IntMap t b+  -> Maybe (SubsetProof 'Int s t)+isSubmapOfBy f (IntMap m1) (IntMap m2)+  | IntMap.isSubmapOfBy f m1 m2 = Just $ SubsetProof unsafeSubset+  | otherwise = Nothing++-- | If two maps are disjoint (i.e. their intersection is empty), return a proof+-- of that.+disjoint+  :: forall s t a b. IntMap s a -> IntMap t b -> Maybe (DisjointProof 'Int s t)+disjoint (IntMap m1) (IntMap m2)+#if MIN_VERSION_containers(0, 6, 2)+  | IntMap.disjoint m1 m2+#elif MIN_VERSION_containers(0, 5, 8)+  | Const (Any False) <- IntMap.mergeA+    (IntMap.traverseMissing \_ _ -> Const mempty)+    (IntMap.traverseMissing \_ _ -> Const mempty)+    (IntMap.zipWithAMatched \_ _ _ -> Const $ Any True)+    m1+    m2+#else+  | IntMap.null $ IntMapStrict.intersectionWith (\_ _ -> ()) m1 m2+#endif+  = Just $ DisjointProof \f g -> unsafeSubsetWith2 f g+  | otherwise = Nothing++-- | Given two maps proven to have the same keys, for each key apply the+-- function to the associated values, to obtain a new map with the same keys.+zipWithKey+  :: forall s a b c. (Key s -> a -> b -> c)+  -> IntMap s a+  -> IntMap s b+  -> IntMap s c+zipWithKey f (IntMap m1) (IntMap m2) = IntMap+  $ IntMap.mergeWithKey (\k x y -> Just $ f (unsafeKey k) x y)+    (error "zipWithKey: bug: Data.IntMap.Refined has been subverted")+    (error "zipWithKey: bug: Data.IntMap.Refined has been subverted")+    m1+    m2++-- | Remove the keys that appear in the second map from the first map.+difference+  :: forall s t a b. IntMap s a+  -> IntMap t b+  -> SomeIntMapWith (DifferenceProof 'Int s t) a+difference (IntMap m1) (IntMap m2) = SomeIntMapWith+  (IntMap $ IntMap.difference m1 m2)+  $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, that are proven to be in the map. The set of keys remains the same.+mapWithKey :: forall s a b. (Key s -> a -> b) -> IntMap s a -> IntMap s b+mapWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.mapWithKey @a @b++-- | Map an 'Applicative' transformation in ascending order of keys, with access+-- to each value's corresponding key and a proof that it is in the map. The set+-- of keys remains unchanged.+traverseWithKey+  :: forall s f a b. Applicative f+  => (Key s -> a -> f b) -> IntMap s a -> f (IntMap s b)+traverseWithKey f (IntMap m) = IntMap <$> IntMap.traverseWithKey (f . unsafeKey) m++-- | Map each key-value pair of a map into a monoid (with proof that the key was+-- in the map), and combine the results using '<>'.+foldMapWithKey+  :: forall s a m. Monoid m => (Key s -> a -> m) -> IntMap s a -> m+foldMapWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.foldMapWithKey @m @a++-- | Right associative fold with a lazy accumulator.+foldrWithKey :: forall s a b. (Key s -> a -> b -> b) -> b -> IntMap s a -> b+foldrWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.foldrWithKey @a @b++-- | Left associative fold with a lazy accumulator.+foldlWithKey :: forall s a b. (b -> Key s -> a -> b) -> b -> IntMap s a -> b+foldlWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.foldlWithKey @b @a++-- | Right associative fold with a strict accumulator.+foldrWithKey' :: forall s a b. (Key s -> a -> b -> b) -> b -> IntMap s a -> b+foldrWithKey' = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.foldrWithKey' @a @b++-- | Left associative fold with a strict accumulator.+foldlWithKey' :: forall s a b. (b -> Key s -> a -> b) -> b -> IntMap s a -> b+foldlWithKey' = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.foldlWithKey' @b @a++-- | Return the set of keys in the map, with the contents of the set still+-- tracked by the @s@ parameter. See "Data.IntSet.Refined".+keysSet :: forall s a. IntMap s a -> IntSet s+keysSet (IntMap m) = reify (IntMap.keysSet m)+  \(_ :: Proxy s') -> case unsafeCoerce Refl :: s :~: s' of+    Refl -> Dict++-- | Convert to a list of key-value pairs in ascending order of keys.+toList :: forall s a. IntMap s a -> [(Key s, a)]+toList = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.toAscList @a++-- | Convert to a list of key-value pairs in descending order of keys.+toDescList :: forall s a. IntMap s a -> [(Key s, a)]+toDescList = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.toDescList @a++-- | Retain only the key-value pairs that satisfy the predicate, returning a+-- potentially smaller map.+filterWithKey+  :: forall s a. (Key s -> a -> Bool)+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) a+filterWithKey p (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.filterWithKey (p . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Restrict a map to only those keys that are elements of @t@.+restrictKeys+  :: forall s t a. KnownIntSet t+  => IntMap s a -> SomeIntMapWith (IntersectionProof 'Int s t) a+restrictKeys (IntMap m) = SomeIntMapWith+#if MIN_VERSION_containers(0, 5, 8)+  (IntMap $ IntMap.restrictKeys m $ reflect $ Proxy @t)+#else+  (IntMap $ IntMap.intersectionWith const m+    $ IntMap.fromSet id $ reflect $ Proxy @t)+#endif+  $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Remove all keys that are elements of @t@ from the map.+withoutKeys+  :: forall s t a. KnownIntSet t+  => IntMap s a -> SomeIntMapWith (DifferenceProof 'Int s t) a+withoutKeys (IntMap m) = SomeIntMapWith+#if MIN_VERSION_containers(0, 5, 8)+  (IntMap $ IntMap.withoutKeys m $ reflect $ Proxy @t)+#else+  (IntMap $ IntMap.difference m $ IntMap.fromSet id $ reflect $ Proxy @t)+#endif+  $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Partition a map into two disjoint submaps: those whose key-value pairs+-- satisfy the predicate, and those whose don't.+partitionWithKey+  :: forall s a. (Key s -> a -> Bool)+  -> IntMap s a+  -> Some2IntMapWith (PartitionProof 'Int s Int) a a+partitionWithKey p (IntMap m)+  = case IntMap.partitionWithKey (p . unsafeKey) m of+    (m1, m2) -> Some2IntMapWith (IntMap m1) (IntMap m2) $ PartitionProof+      do \k -> case IntMap.lookup (unrefine k) m of+          Nothing -> error+            "partitionWithKey: bug: Data.IntMap.Refined has been subverted"+          Just x -> if p k x+            then Left $ unsafeKey $ unrefine k+            else Right $ unsafeKey $ unrefine k+      unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Divide a map into two disjoint submaps at a point where the predicate on+-- the keys stops holding.+--+-- If @p@ is antitone ( \(\forall x y, x < y \implies p(x) \ge p(y)\) ), then+-- this point is uniquely defined. If @p@ is not antitone, a splitting point is+-- chosen in an unspecified way.+spanAntitone+  :: forall s a. (Key s -> Bool)+  -> IntMap s a+  -> Some2IntMapWith (PartialPartitionProof 'Int s) a a+spanAntitone p (IntMap m) =+#if MIN_VERSION_containers(0, 6, 7)+  case IntMap.spanAntitone (p . unsafeKey) m of+    (m1, m2)+#else+  case List.span (p . unsafeKey . fst) $ IntMap.toAscList m of+    (xs1, xs2)+      | let m1 = IntMap.fromDistinctAscList xs1+      , let m2 = IntMap.fromDistinctAscList xs2+#endif+      -> Some2IntMapWith (IntMap m1) (IntMap m2) $ PartialPartitionProof+        unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Return two disjoint submaps: those whose keys are less than the given key,+-- and those whose keys are greater than the given key. If the key was in the+-- map, also return the associated value and the proof that it was in the map.+splitLookup+  :: forall s a. Int+  -> IntMap s a+  -> Some2IntMapWith (SplitProof 'Int s (Key s, a)) a a+splitLookup k (IntMap m) = case IntMap.splitLookup k m of+  (m1, v, m2) -> Some2IntMapWith (IntMap m1) (IntMap m2) $ SplitProof+    ((unsafeKey k,) <$> v) unsafeSubset \f g -> unsafeSubsetWith2 f g++-- | Retrieves the key-value pair corresponding to the smallest key of the map,+-- and the map with that pair removed; or a proof that the map was empty.+minViewWithKey+  :: forall s a. IntMap s a+  -> Either+    (EmptyProof 'Int s)+    ((Key s, a), SomeIntMapWith (SupersetProof 'Int s) a)+minViewWithKey (IntMap m) = case IntMap.minViewWithKey m of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (kv, m') -> Right $ (gcoerceWith (unsafeCastKey @s) $ coerce kv,)+    $ SomeIntMapWith (IntMap m') $ SupersetProof unsafeSubset++-- | Retrieves the key-value pair corresponding to the greatest key of the map,+-- and the map with that pair removed; or a proof that the map was empty.+maxViewWithKey+  :: forall s a. IntMap s a+  -> Either+    (EmptyProof 'Int s)+    ((Key s, a), SomeIntMapWith (SupersetProof 'Int s) a)+maxViewWithKey (IntMap m) = case IntMap.maxViewWithKey m of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (kv, m') -> Right $ (gcoerceWith (unsafeCastKey @s) $ coerce kv,)+    $ SomeIntMapWith (IntMap m') $ SupersetProof unsafeSubset++-- | If elements of @s@ can be weakened to elements of @t@ and vice versa, then+-- @s@ and @t@ actually stand for the same set and @'Key' s@ can be safely+-- interconverted with @'Key' t@.+--+-- The requirement that the weakenings are natural transformations ensures that+-- they don't actually alter the keys. To build these you can compose ':->''s+-- from proofs returned by functions in this module, or "Refined" functions like+-- 'andLeft' or 'leftOr'.+castKey+  :: forall s t k.+     (forall x. Refined (InSet 'Int s) x -> Refined (InSet 'Int t) x)+  -> (forall x. Refined (InSet 'Int t) x -> Refined (InSet 'Int s) x)+  -> Coercion (Refined (InSet 'Int s) k) (Refined (InSet 'Int t) k)+castKey = castRefined++-- | If keys can be interconverted (e.g. as proved by 'castKey'), then the maps+-- can be interconverted too. For example, 'zipWithKey' can be implemented via+-- 'Data.IntMap.Refined.intersectionWithKey' by proving that the set of keys+-- remains unchanged:+--+-- @+-- 'zipWithKey'+--   :: forall s a b c. ('Key' s -> a -> b -> c)+--   -> 'IntMap' s a+--   -> 'IntMap' s b+--   -> 'IntMap' s c+-- 'zipWithKey' f m1 m2+--   | v'SomeIntMapWith' @r m proof <- 'Data.IntMap.Refined.intersectionWithKey' (f . 'andLeft') m1 m2+--   , v'IntersectionProof' p1 p2 <- proof+--   , ( v'Coercion' :: t'Coercion' ('IntMap' r c) ('IntMap' s c))+--     <- app $ 'cast' $ 'castKey' ('andLeft' . p1) (p2 'id' 'id')+--   = 'coerce' m+--   where+--     app :: t'Coercion' f g -> t'Coercion' (f x) (g x)+--     app v'Coercion' = v'Coercion'+-- @+cast+  :: forall s t k. (forall x. Coercion+    (Refined (InSet 'Int s) x)+    (Refined (InSet 'Int t) x))+  -> Coercion (IntMap s k) (IntMap t k)+cast Coercion = Coercion++instance FunctorWithIndex (Key s) (IntMap s) where+  imap = mapWithKey++instance FoldableWithIndex (Key s) (IntMap s) where+  ifoldMap = foldMapWithKey++instance TraversableWithIndex (Key s) (IntMap s) where+  itraverse = traverseWithKey++-- | Similar to the instance for functions -- zip corresponding keys. To use+-- '<*>'/'liftA2' without 'KnownIntSet' see 'zipWithKey'.+instance  KnownIntSet s => Applicative (IntMap s) where+  pure x = fromSet \_ -> x+  (<*>) = zipWithKey (const id)++-- | @'bind' m f@ is a map that for each key @k :: 'Key' s@, contains the+-- value @f (m '!' k) '!' k@, similar to @'>>='@ for functions.+bind :: forall s a b. IntMap s a -> (a -> IntMap s b) -> IntMap s b+bind m f = mapWithKey (\k x -> f x ! k) m++-- | Similar to the instance for functions. To use '>>=' without 'KnownIntSet'+-- see 'bind'.+instance KnownIntSet s => Monad (IntMap s) where+  (>>=) = bind++-- | Similar to the instance for functions. See also+-- 'Data.IntMap.Refined.backpermuteKeys'.+instance KnownIntSet s => MonadReader (Key s) (IntMap s) where+  ask = fromSet id+  local f m = mapWithKey (\k _ -> m ! f k) m++-- | Append the values at the corresponding keys+instance Semigroup a => Semigroup (IntMap s a) where+  (<>) = zipWithKey (const (<>))++instance (KnownIntSet s, Monoid a) => Monoid (IntMap s a) where+  mempty = fromSet \_ -> mempty++-- | Similar to the instance for functions+instance KnownIntSet s => Distributive (IntMap s) where+  collect = collectRep+  distribute = distributeRep++-- | Witness isomorphism with functions from @'Key' s@+instance KnownIntSet s => Representable (IntMap s) where+  type Rep (IntMap s) = Key s+  index = (!)+  tabulate = fromSet++#if MIN_VERSION_hashable(1, 3, 4)+#else+instance Hashable.Hashable a => Hashable.Hashable (IntMap s a) where+  hashWithSalt s (IntMap m) = IntMap.foldlWithKey'+    (\s' k v -> Hashable.hashWithSalt (Hashable.hashWithSalt s' k) v)+    (Hashable.hashWithSalt s (IntMap.size m))+    m+#endif
+ src/Data/IntMap/Refined.hs view
@@ -0,0 +1,379 @@+-- | This module defines a way to prove that a key exists in a map, so that the+-- key can be used to index into the map without using a 'Maybe', or manually+-- handling the \"impossible\" case with 'error' or other partial functions.+--+-- To do this, @'IntMap' s v@ has a type parameter @s@ that identifies its set+-- ofvkeys, so that if another map has the same type parameter, you know that+-- map has the same set of keys. There is @'Key' s@, a type of keys that have+-- been validated to belong to the set identified by @s@, and for which the+-- operation of indexing into a @'IntMap' s v@ (only for the same @s@) can+-- proceed without failure (see '!'). The type @s@ itself has no internal+-- structure, rather it is merely a skolem type variable (rank-2 polymorphism+-- 'Control.Monad.ST.runST' trick) introduced by "Data.Reflection".+--+-- Like "Data.IntMap", functions in this module are strict in the keys but lazy+-- in the values. The "Data.IntMap.Strict.Refined" module reuses the same+-- 'IntMap' type but provides functions that operate strictly on the values.+module Data.IntMap.Refined+  (+  -- * Map type+    Common.IntMap+  , Common.Key+  -- * Existentials and common proofs+  , Common.SomeIntMap(..)+  , Common.withIntMap+  , Common.SomeIntMapWith(..)+  , Common.withIntMapWith+  , Common.Some2IntMapWith(..)+  , Common.with2IntMapWith+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , Common.empty+  , singleton+  , SingletonProof(..)+  , fromSet+  , Common.fromIntMap+  , fromTraversableWithKey+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  , reinsert+  , insertLookupWithKey+  -- * Deletion/Update+  , Common.delete+  , adjust+  , adjustWithKey+  , update+  , updateLookupWithKey+  -- * Query+  , Common.lookup+  , (Common.!)+  , Common.member+  , Common.lookupLT+  , Common.lookupGT+  , Common.lookupLE+  , Common.lookupGE+  , Common.null+  , Common.isSubmapOfBy+  , SubsetProof(..)+  , Common.disjoint+  , DisjointProof(..)+  -- * Combine+  , zipWithKey+  , bind+  , unionWithKey+  , UnionProof(..)+  , Common.difference+  , DifferenceProof(..)+  , differenceWithKey+  , PartialDifferenceProof(..)+  , intersectionWithKey+  , IntersectionProof(..)+  -- * Traversal+  , mapWithKey+  , traverseWithKey+  , mapAccumLWithKey+  , mapAccumRWithKey+  , mapKeysWith+  , MapProof(..)+  , backpermuteKeys+  -- * Folds+  , Common.foldMapWithKey+  , Common.foldrWithKey+  , Common.foldlWithKey+  , Common.foldrWithKey'+  , Common.foldlWithKey'+  -- * Conversion+  , Common.toIntMap+  , Common.keysSet+  , Common.toList+  , Common.toDescList+  -- * Filter+  , Common.restrictKeys+  , Common.withoutKeys+  , Common.filterWithKey+  , Common.partitionWithKey+  , PartitionProof(..)+  , Common.spanAntitone+  , PartialPartitionProof(..)+  , mapMaybeWithKey+  , mapEitherWithKey+  , Common.splitLookup+  , SplitProof(..)+  -- * Min/Max+  , updateMinWithKey+  , updateMaxWithKey+  , adjustMinWithKey+  , adjustMaxWithKey+  , Common.minViewWithKey+  , Common.maxViewWithKey+  -- * Casts+  , Common.castKey+  , Common.cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Functor+import qualified Data.IntMap as IntMap+import           Data.IntMap.Common.Refined+  ( IntMap(..), Key, unsafeCastKey, unsafeKey, SomeIntMapWith(..)+  , Some2IntMapWith(..), fromSet, (!), zipWithKey, mapWithKey, traverseWithKey+  , bind+  )+import qualified Data.IntMap.Common.Refined as Common+import           Data.Traversable+import           Data.Type.Coercion+import           Prelude hiding (lookup, null)+import           Refined+import           Refined.Unsafe+++-- | Create a map with a single key-value pair, and return a proof that the+-- key is in the resulting map.+singleton :: forall a. Int -> a -> SomeIntMapWith (SingletonProof 'Int Int) a+singleton k v = SomeIntMapWith (IntMap $ IntMap.singleton k v)+  $ SingletonProof (unsafeKey k)++-- | Create a map from an arbitrary traversable of key-value pairs.+fromTraversableWithKey+  :: forall t a. Traversable t+  => (Int -> a -> a -> a)+  -> t (Int, a)+  -> SomeIntMapWith (FromTraversableProof 'Int t Int) a+fromTraversableWithKey f xs+  = SomeIntMapWith (IntMap m) $ FromTraversableProof proof+  where+    (m, proof) = mapAccumL+      (\s (k, v) -> let !s' = IntMap.insertWithKey f k v s in (s', unsafeKey k))+      IntMap.empty+      xs++-- | Insert a key-value pair into the map to obtain a potentially larger map,+-- guaranteed to contain the given key. If the key was already present, the+-- associated value is replaced with the supplied value.+insert+  :: forall s a. Int+  -> a+  -> IntMap s a+  -> SomeIntMapWith (InsertProof 'Int Int s) a+insert k v (IntMap m) = SomeIntMapWith (IntMap $ IntMap.insert k v m)+  $ InsertProof (unsafeKey k) unsafeSubset++-- | Overwrite a key-value pair that is known to already be in the map. The set+-- of keys remains the same.+reinsert+  :: forall s a. Key s -> a -> IntMap s a -> IntMap s a+reinsert = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.insert @a++-- | Insert a key-value pair into the map using a combining function, and if+-- the key was already present, the old value is returned along with the proof+-- that the key was present.+insertLookupWithKey+  :: forall s a. (Key s -> a -> a -> a)+  -> Int+  -> a+  -> IntMap s a+  -> (Maybe (Key s, a), SomeIntMapWith (InsertProof 'Int Int s) a)+insertLookupWithKey f k v (IntMap m)+  = case IntMap.insertLookupWithKey (f . unsafeKey) k v m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeIntMapWith (IntMap m') $ InsertProof (unsafeKey k) unsafeSubset++-- | Update the value at a specific key known the be in the map using the given+-- function. The set of keys remains the same.+adjust :: forall s a. (a -> a) -> Key s -> IntMap s a -> IntMap s a+adjust = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.adjust @a++-- | If the given key is in the map, update the associated value using the given+-- function with a proof that the key was in the map; otherwise return the map+-- unchanged. In any case the set of keys remains the same.+adjustWithKey+  :: forall s a. (Key s -> a -> a) -> Int -> IntMap s a -> IntMap s a+adjustWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.adjustWithKey @a++-- | Update or delete a key known to be in the map using the given function,+-- returning a potentially smaller map.+update+  :: forall s a. (a -> Maybe a)+  -> Key s+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) a+update f k (IntMap m) = SomeIntMapWith (IntMap $ IntMap.update f (unrefine k) m)+  $ SupersetProof unsafeSubset++-- | If the given key is in the map, update or delete it using the given+-- function with a proof that the key was in the map; otherwise the map is+-- unchanged. Alongside return the new value if it was updated, or the old value+-- if it was deleted, and a proof that the key was in the map.+updateLookupWithKey+  :: forall s a. (Key s -> a -> Maybe a)+  -> Int+  -> IntMap s a+  -> (Maybe (Key s, a), SomeIntMapWith (SupersetProof 'Int s) a)+updateLookupWithKey f k (IntMap m)+  = case IntMap.updateLookupWithKey (f . unsafeKey) k m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeIntMapWith (IntMap m') $ SupersetProof unsafeSubset++-- | Return the union of two maps, with a given combining function for keys that+-- exist in both maps simultaneously.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s@ and @'Key' t@+-- respectively.+unionWithKey+  :: forall s t a. (Refined (InSet 'Int s && InSet 'Int t) Int -> a -> a -> a)+  -> IntMap s a+  -> IntMap t a+  -> SomeIntMapWith (UnionProof 'Int s t) a+unionWithKey f (IntMap m1) (IntMap m2)+  = SomeIntMapWith (IntMap $ IntMap.unionWithKey (f . reallyUnsafeRefine) m1 m2)+    $ UnionProof unsafeSubset unsafeSubsetWith2++-- | For keys that appear in both maps, the given function decides whether the+-- key is removed from the first map.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s@ and @'Key' t@+-- respectively.+differenceWithKey+  :: forall s t a b. (Refined (InSet 'Int s && InSet 'Int t) Int+    -> a+    -> b+    -> Maybe a)+  -> IntMap s a+  -> IntMap t b+  -> SomeIntMapWith (PartialDifferenceProof 'Int s t) a+differenceWithKey f (IntMap m1) (IntMap m2) = SomeIntMapWith+  (IntMap $ IntMap.differenceWithKey (f . reallyUnsafeRefine) m1 m2)+  $ PartialDifferenceProof unsafeSubset unsafeSubset++-- | Return the intersection of two maps with the given combining function.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s@ and @'Key' t@+-- respectively.+intersectionWithKey+  :: forall s t a b c. (Refined (InSet 'Int s && InSet 'Int t) Int+    -> a+    -> b+    -> c)+  -> IntMap s a+  -> IntMap t b+  -> SomeIntMapWith (IntersectionProof 'Int s t) c+intersectionWithKey f (IntMap m1) (IntMap m2) = SomeIntMapWith+  (IntMap $ IntMap.intersectionWithKey (f . reallyUnsafeRefine) m1 m2)+  $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Thread an accumularing argument through the map in ascending order of keys.+mapAccumLWithKey+  :: forall s a b c. (a -> Key s -> b -> (a, c))+  -> a+  -> IntMap s b+  -> (a, IntMap s c)+mapAccumLWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.mapAccumWithKey @a @b @c++-- | Thread an accumularing argument through the map in descending order of+-- keys.+mapAccumRWithKey+  :: forall s a b c. (a -> Key s -> b -> (a, c))+  -> a+  -> IntMap s b+  -> (a, IntMap s c)+mapAccumRWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.mapAccumRWithKey @a @b @c++-- | @'mapKeysWith' c f m@ applies @f@ to each key of @m@ and collects the+-- results into a new map. For keys that were mapped to the same new key, @c@+-- acts as the combining function for corresponding values.+mapKeysWith+  :: forall s a. (a -> a -> a)+  -> (Key s -> Int)+  -> IntMap s a+  -> SomeIntMapWith (MapProof 'Int s Int Int) a+mapKeysWith f g (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.mapKeysWith f (g . unsafeKey) m)+    $ MapProof (unsafeKey . g) \k2 ->+      case IntMap.lookup (unrefine k2) backMap of+        Nothing -> error+          "mapKeysWith: bug: Data.IntMap.Refined has been subverted"+        Just k1 -> k1+  where+    ~backMap = IntMap.fromList+      [ (k2, unsafeKey k1)+      | k1 <- IntMap.keys m+      , let !k2 = g $ unsafeKey k1+      ]++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect only the 'Just' results, returning a potentially smaller+-- map.+mapMaybeWithKey+  :: forall s a b. (Key s -> a -> Maybe b)+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) b+mapMaybeWithKey f (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.mapMaybeWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect the 'Left' and 'Right' results into separate (disjoint)+-- maps.+mapEitherWithKey+  :: forall s a b c. (Key s -> a -> Either b c)+  -> IntMap s a+  -> Some2IntMapWith (PartitionProof 'Int s Int) b c+mapEitherWithKey p (IntMap m)+  = case IntMap.mapEitherWithKey (p . unsafeKey) m of+    (m1, m2) -> Some2IntMapWith (IntMap m1) (IntMap m2) $ PartitionProof+      do \k -> case IntMap.lookup (unrefine k) m of+          Nothing -> error+            "mapEitherWithKey: bug: Data.IntMap.Refined has been subverted"+          Just x -> case p k x of+            Left _ -> Left $ unsafeKey $ unrefine k+            Right _ -> Right $ unsafeKey $ unrefine k+      unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Update or delete the value at the smallest key, returning a potentially+-- smaller map.+updateMinWithKey+  :: forall s a. (Key s -> a -> Maybe a)+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) a+updateMinWithKey f (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.updateMinWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Update or delete the value at the largest key, returning a potentially+-- smaller map.+updateMaxWithKey+  :: forall s a. (Key s -> a -> Maybe a)+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) a+updateMaxWithKey f (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.updateMaxWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Adjust the value at the smallest key. The set of keys remains unchanged.+adjustMinWithKey :: forall s a. (Key s -> a -> a) -> IntMap s a -> IntMap s a+adjustMinWithKey f (IntMap m)+  = IntMap $ IntMap.updateMinWithKey ((Just .) . f . unsafeKey) m++-- | Adjust the value at the greatest key. The set of keys remains unchanged.+adjustMaxWithKey :: forall s a. (Key s -> a -> a) -> IntMap s a -> IntMap s a+adjustMaxWithKey f (IntMap m)+  = IntMap $ IntMap.updateMaxWithKey ((Just .) . f . unsafeKey) m++-- | Apply the inverse image of the given function to the keys of the given map,+-- so that for all @k :: 'Key' s2@,+-- @'backpermuteKeys' f m '!' k = m '!' f k@.+--+-- If maps are identified with functions, this computes the composition.+backpermuteKeys+  :: forall s1 s2 a. KnownIntSet s2+  => (Key s2 -> Key s1) -> IntMap s1 a -> IntMap s2 a+backpermuteKeys f m = fromSet \k -> m ! f k
+ src/Data/IntMap/Strict/Refined.hs view
@@ -0,0 +1,418 @@+-- | This module defines a way to prove that a key exists in a map, so that the+-- key can be used to index into the map without using a 'Maybe', or manually+-- handling the \"impossible\" case with 'error' or other partial functions.+--+-- To do this, @'IntMap' s v@ has a type parameter @s@ that identifies its set+-- of keys, so that if another map has the same type parameter, you know that+-- map has the same set of keys. There is @'Key' s@, a type of keys that have+-- been validated to belong to the set identified by @s@, and for which the+-- operation of indexing into a @'IntMap' s v@ (only for the same @s@) can+-- proceed without failure (see '!'). The type @s@ itself has no internal+-- structure, rather it is merely a skolem type variable (rank-2 polymorphism+-- 'Control.Monad.ST.runST' trick) introduced by "Data.Reflection".+--+-- Like "Data.IntMap.Strict", functions in this module are strict in the keys+-- and values. The "Data.IntMap.Refined" module reuses the same 'IntMap' type+-- but provides functions that operate lazily on the values.+module Data.IntMap.Strict.Refined+  (+  -- * Map type+    Common.IntMap+  , Common.Key+  -- * Existentials and common proofs+  , Common.SomeIntMap(..)+  , Common.withIntMap+  , Common.SomeIntMapWith(..)+  , Common.withIntMapWith+  , Common.Some2IntMapWith(..)+  , Common.with2IntMapWith+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , Common.empty+  , singleton+  , SingletonProof(..)+  , fromSet+  , Common.fromIntMap+  , fromTraversableWithKey+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  , reinsert+  , insertLookupWithKey+  -- * Deletion/Update+  , Common.delete+  , adjust+  , adjustWithKey+  , update+  , updateLookupWithKey+  -- * Query+  , Common.lookup+  , (Common.!)+  , Common.member+  , Common.lookupLT+  , Common.lookupGT+  , Common.lookupLE+  , Common.lookupGE+  , Common.null+  , Common.isSubmapOfBy+  , SubsetProof(..)+  , Common.disjoint+  , DisjointProof(..)+  -- * Combine+  , zipWithKey+  , bind+  , unionWithKey+  , UnionProof(..)+  , Common.difference+  , DifferenceProof(..)+  , differenceWithKey+  , PartialDifferenceProof(..)+  , intersectionWithKey+  , IntersectionProof(..)+  -- * Traversal+  , mapWithKey+  , traverseWithKey+  , mapAccumLWithKey+  , mapAccumRWithKey+  , mapKeysWith+  , MapProof(..)+  , backpermuteKeys+  -- * Folds+  , Common.foldMapWithKey+  , Common.foldrWithKey+  , Common.foldlWithKey+  , Common.foldrWithKey'+  , Common.foldlWithKey'+  -- * Conversion+  , Common.toIntMap+  , Common.keysSet+  , Common.toList+  , Common.toDescList+  -- * Filter+  , Common.restrictKeys+  , Common.withoutKeys+  , Common.filterWithKey+  , Common.partitionWithKey+  , PartitionProof(..)+  , Common.spanAntitone+  , PartialPartitionProof(..)+  , mapMaybeWithKey+  , mapEitherWithKey+  , Common.splitLookup+  , SplitProof(..)+  -- * Min/Max+  , updateMinWithKey+  , updateMaxWithKey+  , adjustMinWithKey+  , adjustMaxWithKey+  , Common.minViewWithKey+  , Common.maxViewWithKey+  -- * Casts+  , Common.castKey+  , Common.cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Functor+import qualified Data.IntMap.Strict as IntMap+import           Data.IntMap.Common.Refined+  ( IntMap(..), Key, unsafeCastKey, unsafeKey, SomeIntMapWith(..)+  , Some2IntMapWith(..), (!)+  )+import qualified Data.IntMap.Common.Refined as Common+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable+import           Data.Type.Coercion+import           Prelude hiding (lookup, null)+import           Refined+import           Refined.Unsafe+++-- | Create a map with a single key-value pair, and return a proof that the+-- key is in the resulting map.+singleton :: forall a. Int -> a -> SomeIntMapWith (SingletonProof 'Int Int) a+singleton k v = SomeIntMapWith (IntMap $ IntMap.singleton k v)+  $ SingletonProof (unsafeKey k)++-- | Create a map from a set of keys, and a function that for each key computes+-- the corresponding value.+fromSet :: forall s a. KnownIntSet s => (Key s -> a) -> IntMap s a+fromSet f = IntMap $ IntMap.fromSet (f . unsafeKey) (reflect $ Proxy @s)++-- | Create a map from an arbitrary traversable of key-value pairs.+fromTraversableWithKey+  :: forall t a. Traversable t+  => (Int -> a -> a -> a)+  -> t (Int, a)+  -> SomeIntMapWith (FromTraversableProof 'Int t Int) a+fromTraversableWithKey f xs = SomeIntMapWith (IntMap m)+  $ FromTraversableProof proof+  where+    (m, proof) = mapAccumL+      (\s (k, v) -> (IntMap.insertWithKey f k v s, unsafeKey k))+      IntMap.empty+      xs++-- | Insert a key-value pair into the map to obtain a potentially larger map,+-- guaranteed to contain the given key. If the key was already present, the+-- associated value is replaced with the supplied value.+insert+  :: forall s a. Int+  -> a+  -> IntMap s a+  -> SomeIntMapWith (InsertProof 'Int Int s) a+insert k v (IntMap m) = SomeIntMapWith (IntMap $ IntMap.insert k v m)+  $ InsertProof (unsafeKey k) unsafeSubset++-- | Overwrite a key-value pair that is known to already be in the map. The set+-- of keys remains the same.+reinsert+  :: forall s a. Key s -> a -> IntMap s a -> IntMap s a+reinsert = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.insert @a++-- | Insert a key-value pair into the map using a combining function, and if+-- the key was already present, the old value is returned along with the proof+-- that the key was present.+insertLookupWithKey+  :: forall s a. (Key s -> a -> a -> a)+  -> Int+  -> a+  -> IntMap s a+  -> (Maybe (Key s, a), SomeIntMapWith (InsertProof 'Int Int s) a)+insertLookupWithKey f k v (IntMap m)+  = case IntMap.insertLookupWithKey (f . unsafeKey) k v m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeIntMapWith (IntMap m') $ InsertProof (unsafeKey k) unsafeSubset++-- | Update the value at a specific key known the be in the map using the given+-- function. The set of keys remains the same.+adjust :: forall s a. (a -> a) -> Key s -> IntMap s a -> IntMap s a+adjust = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.adjust @a++-- | If the given key is in the map, update the associated value using the given+-- function with a proof that the key was in the map; otherwise return the map+-- unchanged. In any case the set of keys remains the same.+adjustWithKey+  :: forall s a. (Key s -> a -> a) -> Int -> IntMap s a -> IntMap s a+adjustWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.adjustWithKey @a++-- | Update or delete a key known to be in the map using the given function,+-- returning a potentially smaller map.+update+  :: forall s a. (a -> Maybe a)+  -> Key s+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) a+update f k (IntMap m) = SomeIntMapWith (IntMap $ IntMap.update f (unrefine k) m)+  $ SupersetProof unsafeSubset++-- | If the given key is in the map, update or delete it using the given+-- function with a proof that the key was in the map; otherwise the map is+-- unchanged. Alongside return the new value if it was updated, or the old value+-- if it was deleted, and a proof that the key was in the map.+updateLookupWithKey+  :: forall s a. (Key s -> a -> Maybe a)+  -> Int+  -> IntMap s a+  -> (Maybe (Key s, a), SomeIntMapWith (SupersetProof 'Int s) a)+updateLookupWithKey f k (IntMap m)+  = case IntMap.updateLookupWithKey (f . unsafeKey) k m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeIntMapWith (IntMap m') $ SupersetProof unsafeSubset++-- | Given two maps proven to have the same keys, for each key apply the+-- function to the associated values, to obtain a new map with the same keys.+zipWithKey+  :: forall s a b c. (Key s -> a -> b -> c)+  -> IntMap s a+  -> IntMap s b+  -> IntMap s c+zipWithKey f (IntMap m1) (IntMap m2) = IntMap+  $ IntMap.mergeWithKey (\k x y -> Just $ f (unsafeKey k) x y)+    (error "zipWithKey: bug: Data.IntMap.Strict.Refined has been subverted")+    (error "zipWithKey: bug: Data.IntMap.Strict.Refined has been subverted")+    m1+    m2++-- | Return the union of two maps, with a given combining function for keys that+-- exist in both maps simultaneously.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s@ and @'Key' t@+-- respectively.+unionWithKey+  :: forall s t a. (Refined (InSet 'Int s && InSet 'Int t) Int -> a -> a -> a)+  -> IntMap s a+  -> IntMap t a+  -> SomeIntMapWith (UnionProof 'Int s t) a+unionWithKey f (IntMap m1) (IntMap m2)+  = SomeIntMapWith (IntMap $ IntMap.unionWithKey (f . reallyUnsafeRefine) m1 m2)+    $ UnionProof unsafeSubset unsafeSubsetWith2++-- | For keys that appear in both maps, the given function decides whether the+-- key is removed from the first map.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s@ and @'Key' t@+-- respectively.+differenceWithKey+  :: forall s t a b. (Refined (InSet 'Int s && InSet 'Int t) Int+    -> a+    -> b+    -> Maybe a)+  -> IntMap s a+  -> IntMap t b+  -> SomeIntMapWith (PartialDifferenceProof 'Int s t) a+differenceWithKey f (IntMap m1) (IntMap m2) = SomeIntMapWith+  (IntMap $ IntMap.differenceWithKey (f . reallyUnsafeRefine) m1 m2)+  $ PartialDifferenceProof unsafeSubset unsafeSubset++-- | Return the intersection of two maps with the given combining function.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s@ and @'Key' t@+-- respectively.+intersectionWithKey+  :: forall s t a b c. (Refined (InSet 'Int s && InSet 'Int t) Int+    -> a+    -> b+    -> c)+  -> IntMap s a+  -> IntMap t b+  -> SomeIntMapWith (IntersectionProof 'Int s t) c+intersectionWithKey f (IntMap m1) (IntMap m2) = SomeIntMapWith+  (IntMap $ IntMap.intersectionWithKey (f . reallyUnsafeRefine) m1 m2)+  $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Apply a function to all values in a map, together with their corresponding+-- keys, that are proven to be in the map. The set of keys remains the same.+mapWithKey :: forall s a b. (Key s -> a -> b) -> IntMap s a -> IntMap s b+mapWithKey = gcoerceWith (unsafeCastKey @s) $ coerce $ IntMap.mapWithKey @a @b++-- | Map an 'Applicative' transformation in ascending order of keys, with access+-- to each value's corresponding key and a proof that it is in the map. The set+-- of keys remains unchanged.+traverseWithKey+  :: forall s f a b. Applicative f+  => (Key s -> a -> f b) -> IntMap s a -> f (IntMap s b)+traverseWithKey f (IntMap m)+  = IntMap <$> IntMap.traverseWithKey (f . unsafeKey) m++-- | Thread an accumularing argument through the map in ascending order of keys.+mapAccumLWithKey+  :: forall s a b c. (a -> Key s -> b -> (a, c))+  -> a+  -> IntMap s b+  -> (a, IntMap s c)+mapAccumLWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.mapAccumWithKey @a @b @c++-- | Thread an accumularing argument through the map in descending order of+-- keys.+mapAccumRWithKey+  :: forall s a b c. (a -> Key s -> b -> (a, c))+  -> a+  -> IntMap s b+  -> (a, IntMap s c)+mapAccumRWithKey = gcoerceWith (unsafeCastKey @s) $ coerce+  $ IntMap.mapAccumRWithKey @a @b @c++-- | @'mapKeysWith' c f m@ applies @f@ to each key of @m@ and collects the+-- results into a new map. For keys that were mapped to the same new key, @c@+-- acts as the combining function for corresponding values.+mapKeysWith+  :: forall s a. (a -> a -> a)+  -> (Key s -> Int)+  -> IntMap s a+  -> SomeIntMapWith (MapProof 'Int s Int Int) a+mapKeysWith f g (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.mapKeysWith f (g . unsafeKey) m)+    $ MapProof (unsafeKey . g) \k2 ->+      case IntMap.lookup (unrefine k2) backMap of+        Nothing -> error+          "mapKeysWith: bug: Data.IntMap.Strict.Refined has been subverted"+        Just k1 -> k1+  where+    ~backMap = IntMap.fromList+      [ (k2, unsafeKey k1)+      | k1 <- IntMap.keys m+      , let !k2 = g $ unsafeKey k1+      ]++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect only the 'Just' results, returning a potentially smaller+-- map.+mapMaybeWithKey+  :: forall s a b. (Key s -> a -> Maybe b)+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) b+mapMaybeWithKey f (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.mapMaybeWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect the 'Left' and 'Right' results into separate (disjoint)+-- maps.+mapEitherWithKey+  :: forall s a b c. (Key s -> a -> Either b c)+  -> IntMap s a+  -> Some2IntMapWith (PartitionProof 'Int s Int) b c+mapEitherWithKey p (IntMap m)+  = case IntMap.mapEitherWithKey (p . unsafeKey) m of+    (m1, m2) -> Some2IntMapWith (IntMap m1) (IntMap m2) $ PartitionProof+      do \k -> case IntMap.lookup (unrefine k) m of+          Nothing -> error+            "mapEitherWithKey: bug: Data.IntMap.Strict.Refined has been subverted"+          Just x -> case p k x of+            Left _ -> Left $ unsafeKey $ unrefine k+            Right _ -> Right $ unsafeKey $ unrefine k+      unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Update or delete the value at the smallest key, returning a potentially+-- smaller map.+updateMinWithKey+  :: forall s a. (Key s -> a -> Maybe a)+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) a+updateMinWithKey f (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.updateMinWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Update or delete the value at the largest key, returning a potentially+-- smaller map.+updateMaxWithKey+  :: forall s a. (Key s -> a -> Maybe a)+  -> IntMap s a+  -> SomeIntMapWith (SupersetProof 'Int s) a+updateMaxWithKey f (IntMap m)+  = SomeIntMapWith (IntMap $ IntMap.updateMaxWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Adjust the value at the smallest key. The set of keys remains unchanged.+adjustMinWithKey :: forall s a. (Key s -> a -> a) -> IntMap s a -> IntMap s a+adjustMinWithKey f (IntMap m)+  = IntMap $ IntMap.updateMinWithKey ((Just .) . f . unsafeKey) m++-- | Adjust the value at the greatest key. The set of keys remains unchanged.+adjustMaxWithKey :: forall s a. (Key s -> a -> a) -> IntMap s a -> IntMap s a+adjustMaxWithKey f (IntMap m)+  = IntMap $ IntMap.updateMaxWithKey ((Just .) . f . unsafeKey) m++-- | @'bind' m f@ is a map that for each key @k :: 'Key' s@, contains the+-- value @f (m '!' k) '!' k@, similar to @'>>='@ for functions.+bind :: forall s a b. IntMap s a -> (a -> IntMap s b) -> IntMap s b+bind m f = mapWithKey (\k x -> f x ! k) m++-- | Apply the inverse image of the given function to the keys of the given map,+-- so that for all @k :: 'Key' s2@,+-- @'backpermuteKeys' f m '!' k = m '!' f k@.+--+-- If maps are identified with functions, this computes the composition.+backpermuteKeys+  :: forall s1 s2 a. KnownIntSet s2+  => (Key s2 -> Key s1) -> IntMap s1 a -> IntMap s2 a+backpermuteKeys f m = fromSet \k -> m ! f k
+ src/Data/IntSet/Refined.hs view
@@ -0,0 +1,502 @@+{-# LANGUAGE CPP #-}+-- | This module implements a way of tracking the contents of an+-- 'Data.IntSet.IntSet' at the type level, and provides utilities for+-- manipulating such sets.+--+-- The contents of a set are associated with a type parameter, e.g. @s@, so that+-- whenever you see the same type parameter, you know you are working with the+-- same set. The type @s@ itself has no internal structure, rather it is merely+-- a skolem type variable (rank-2 polymorphism 'Control.Monad.ST.runST' trick)+-- introduced by "Data.Reflection".+module Data.IntSet.Refined+  (+  -- * Set type+    KnownIntSet+  , IntSet+  -- * Refinement type+  , InSet(..)+  , Flavor(Int)+  , Element+  , revealPredicate+  -- * Existentials and common proofs+  , SomeIntSet(..)+  , withIntSet+  , SomeIntSetWith(..)+  , withIntSetWith+  , Some2IntSetWith(..)+  , with2IntSetWith+  , (:->)+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , empty+  , singleton+  , SingletonProof(..)+  , fromIntSet+  , fromTraversable+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  -- * Deletion+  , delete+  -- * Query+  , member+  , lookupLT+  , lookupGT+  , lookupLE+  , lookupGE+  , null+  , isSubsetOf+  , SubsetProof(..)+  , disjoint+  , DisjointProof(..)+  -- * Combine+  , union+  , UnionProof(..)+  , difference+  , DifferenceProof(..)+  , intersection+  , IntersectionProof(..)+  -- * Filter+  , filter+  , partition+  , PartitionProof(..)+  , spanAntitone+  , PartialPartitionProof(..)+  , splitMember+  , SplitProof(..)+  -- * Map+  , map+  , MapProof(..)+  -- * Folds+  , foldMap+  , foldr+  , foldl+  , foldr'+  , foldl'+  -- * Min/Max+  , minView+  , maxView+  -- * Conversion+  , toList+  , toDescList+  , asSet+  , asHashSet+  -- * Casts+  , castElement+  , cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Conversion+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import qualified Data.Foldable as Foldable+import qualified Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable+import           Data.Type.Coercion+import           Data.Type.Equality ((:~:)(..))+import           GHC.Exts (Proxy#, proxy#)+import           Prelude hiding (filter, foldl, foldMap, foldr, map, null)+import           Refined+import           Refined.Unsafe+import           Unsafe.Coerce++#if MIN_VERSION_containers(0, 6, 7)+#else+import qualified Data.List as List+#endif+++-- | To use "Refined" machinery that uses the 'Predicate' typeclass you will+-- need to pattern match on this 'Dict'.+--+-- The reason is that in the default /fast/ implementation of reflection, we+-- don't have @'Data.Typeable.Typeable' s@, which "Refined" wants for+-- pretty-printing exceptions. We /can/ provide @'Data.TypeableTypeable' s@, but+-- at the cost of using the /slow/ implementation of reflection.+revealPredicate+  :: forall s. KnownIntSet s => Dict (Predicate (InSet 'Int s) Int)+revealPredicate = reifyTypeable (reflect (Proxy @s))+  \(_ :: Proxy s') ->+    reflect (Proxy @s') `seq`+    --  ^ Work around https://github.com/ekmett/reflection/issues/54+      case unsafeCoerce Refl :: s :~: s' of+        Refl -> Dict++-- | @'Element' s@ is an t'Int' that has been verified to be an element of @s@.+--+-- Thus, @'Element' s@ is a \"refinement\" type of t'Int', and this library+-- integrates with an implementation of refimenement types in "Refined", so+-- the machinery from there can be used to manipulate 'Element's (however see+-- 'revealPredicate').+--+-- The underlying t'Int' can be obtained with 'unrefine'. An t'Int' can be+-- validated into an @'Element' s@ with 'member'.+type Element s = Refined (InSet 'Int s) Int++unsafeCastElement :: forall s. Coercion Int (Element s)+unsafeCastElement = reallyUnsafeUnderlyingRefined++unsafeElement :: Int -> Element s+unsafeElement = coerceWith unsafeCastElement++-- | An existential wrapper for an as-yet-unknown set. Pattern maching on it+-- gives you a way to refer to the set, e.g.+--+-- @+-- case 'fromIntSet' ... of+--   'SomeIntSet' \@s _ -> doSomethingWith \@s+--+-- case 'fromIntSet' ... of+--   'SomeIntSet' (_ :: 'Proxy#' s) -> doSomethingWith \@s+-- @+data SomeIntSet where+  SomeIntSet :: forall s. KnownIntSet s => Proxy# s -> SomeIntSet++-- | Apply an unknown set to a continuation that can accept any set. This gives+-- you a way to refer to the set (the parameter @s@), e.g.:+--+-- @+-- 'withIntSet' ('fromIntSet' ...) $ \(_ :: 'Proxy' s) -> doSomethingWith \@s+-- @+withIntSet+  :: forall r. SomeIntSet -> (forall s. KnownIntSet s => Proxy s -> r) -> r+withIntSet (SomeIntSet (_ :: Proxy# s)) k = k $ Proxy @s++-- | Construct a set from a regular 'Data.IntSet.IntSet'.+fromIntSet :: IntSet.IntSet -> SomeIntSet+fromIntSet s = reify s \(_ :: Proxy s) -> SomeIntSet @s proxy#++-- | An existential wrapper for an as-yet-unknown set, together with a proof of+-- some fact @p@ about the set. Pattern matching on it gives you a way to refer+-- to the set (the parameter @s@). Most functions will return a set in this way,+-- together with a proof that somehow relates the set to the function's inputs.+data SomeIntSetWith p where+  SomeIntSetWith :: forall s p. KnownIntSet s => !(p s) -> SomeIntSetWith p++-- | Apply an unknown set with proof to a continuation that can accept any set+-- satisfying the proof. This gives you a way to refer to the set (the parameter+-- @s@).+withIntSetWith+  :: forall r p. SomeIntSetWith p -> (forall s. KnownIntSet s => p s -> r) -> r+withIntSetWith (SomeIntSetWith p) k = k p++-- | An existential wrapper for an as-yet-unknown pair of sets, together with+-- a proof of some fact @p@ relating them.+data Some2IntSetWith p where+  Some2IntSetWith+    :: forall s t p. (KnownIntSet s, KnownIntSet t)+    => !(p s t) -> Some2IntSetWith p++-- | Apply a pair of unknown sets with proof to a continuation that can accept+-- any pair of sets satisfying the proof. This gives you a way to refer to the+-- sets (the parameters @s@ and @t@).+with2IntSetWith+  :: forall r p. Some2IntSetWith p+  -> (forall s t. (KnownIntSet s, KnownIntSet t) => p s t -> r)+  -> r+with2IntSetWith (Some2IntSetWith p) k = k p++-- | An empty set.+empty :: SomeIntSetWith (EmptyProof 'Int)+empty = reify IntSet.empty \(_ :: Proxy r)+  -> SomeIntSetWith @r $ EmptyProof unsafeSubset+++-- | Create a set with a single element.+singleton :: Int -> SomeIntSetWith (SingletonProof 'Int Int)+singleton x = reify (IntSet.singleton x) \(_ :: Proxy r)+  -> SomeIntSetWith @r $ SingletonProof $ unsafeElement x++-- | Create a set from the elements of an arbitrary traversable.+fromTraversable+  :: forall t. Traversable t+  => t Int -> SomeIntSetWith (FromTraversableProof 'Int t Int)+fromTraversable xs = reify set \(_ :: Proxy r)+  -> SomeIntSetWith @r $ FromTraversableProof+    $ unsafeCoerce @(t (Element _)) @(t (Element r)) proof+  where+    (set, proof) = mapAccumL+      (\s x -> let !s' = IntSet.insert x s in (s', unsafeElement x))+      IntSet.empty+      xs++-- | Insert an element in a set.+insert :: forall s. KnownIntSet s+  => Int -> SomeIntSetWith (InsertProof 'Int Int s)+insert x = reify (IntSet.insert x $ reflect $ Proxy @s) \(_ :: Proxy r)+  -> SomeIntSetWith @r $ InsertProof (unsafeElement x) unsafeSubset++-- | Delete an element from a set.+delete :: forall s. KnownIntSet s+  => Int -> SomeIntSetWith (SupersetProof 'Int s)+delete x = reify (IntSet.delete x $ reflect $ Proxy @s) \(_ :: Proxy r)+  -> SomeIntSetWith @s $ SupersetProof unsafeSubset++-- | If an element is in the set, return the proof that it is.+member :: forall s. KnownIntSet s => Int -> Maybe (Element s)+member x+  | x `IntSet.member` reflect (Proxy @s) = Just $ unsafeElement x+  | otherwise = Nothing++-- | Find the largest element smaller than the given one.+lookupLT :: forall s. KnownIntSet s => Int -> Maybe (Element s)+lookupLT x = gcoerceWith (unsafeCastElement @s) $ coerce+  $ IntSet.lookupLT x (reflect $ Proxy @s)++-- | Find the smallest element greater than the given one.+lookupGT :: forall s. KnownIntSet s => Int -> Maybe (Element s)+lookupGT x = gcoerceWith (unsafeCastElement @s) $ coerce+  $ IntSet.lookupGT x (reflect $ Proxy @s)++-- | Find the largest element smaller or equal to the given one.+lookupLE :: forall s. KnownIntSet s => Int -> Maybe (Element s)+lookupLE x = gcoerceWith (unsafeCastElement @s) $ coerce+  $ IntSet.lookupLE x (reflect $ Proxy @s)++-- | Find the smallest element greater or equal to the given one.+lookupGE :: forall s. KnownIntSet s => Int -> Maybe (Element s)+lookupGE x = gcoerceWith (unsafeCastElement @s) $ coerce+  $ IntSet.lookupGE x (reflect $ Proxy @s)++-- | If the set is empty, return the proof that it is.+null :: forall s. KnownIntSet s => Maybe (EmptyProof 'Int s)+null+  | IntSet.null $ reflect $ Proxy @s = Just $ EmptyProof unsafeSubset+  | otherwise = Nothing++-- | If @s@ is a subset of @t@ (or is equal to), return a proof of that.+isSubsetOf+  :: forall s t. (KnownIntSet s, KnownIntSet t) => Maybe (SubsetProof 'Int s t)+isSubsetOf+  | reflect (Proxy @s) `IntSet.isSubsetOf` reflect (Proxy @t)+  = Just $ SubsetProof unsafeSubset+  | otherwise = Nothing++-- | If @s@ and @t@ are disjoint (i.e. their intersection is empty), return a+-- proof of that.+disjoint+  :: forall s t. (KnownIntSet s, KnownIntSet t)+  => Maybe (DisjointProof 'Int s t)+disjoint+#if MIN_VERSION_containers(0, 5, 11)+  | IntSet.disjoint (reflect $ Proxy @s) (reflect $ Proxy @t)+#else+  | IntSet.null $ IntSet.intersection (reflect $ Proxy @s) (reflect $ Proxy @t)+#endif+  = Just $ DisjointProof \f g -> unsafeSubsetWith2 f g+  | otherwise = Nothing++-- | The union of two sets.+union+  :: forall s t. (KnownIntSet s, KnownIntSet t)+  => SomeIntSetWith (UnionProof 'Int s t)+union = reify (reflect (Proxy @s) `IntSet.union` reflect (Proxy @t))+  \(_ :: Proxy r) -> SomeIntSetWith @r+    $ UnionProof unsafeSubset unsafeSubsetWith2++-- unions :: ?++-- | Set with elements of @s@ that are not in @t@.+difference+  :: forall s t. (KnownIntSet s, KnownIntSet t)+  => SomeIntSetWith (DifferenceProof 'Int s t)+difference = reify (reflect (Proxy @s) `IntSet.difference` reflect (Proxy @t))+  \(_ :: Proxy r) -> SomeIntSetWith @r+    $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Intersection of two sets.+intersection+  :: forall s t. (KnownIntSet s, KnownIntSet t)+  => SomeIntSetWith (IntersectionProof 'Int s t)+intersection+  = reify (reflect (Proxy @s) `IntSet.intersection` reflect (Proxy @t))+    \(_ :: Proxy r) -> SomeIntSetWith @r+      $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Return a subset of elements that satisfy the given predicate.+filter+  :: forall s. KnownIntSet s+  => (Element s -> Bool) -> SomeIntSetWith (SupersetProof 'Int s)+filter p = reify (IntSet.filter (p . unsafeElement) $ reflect $ Proxy @s)+  \(_ :: Proxy r) -> SomeIntSetWith @r $ SupersetProof unsafeSubset++-- | Partition a set into two disjoint subsets: those that satisfy the+-- predicate, and those that don't.+partition+  :: forall s. KnownIntSet s+  => (Element s -> Bool) -> Some2IntSetWith (PartitionProof 'Int s Int)+partition p = case IntSet.partition (p . unsafeElement) $ reflect $ Proxy @s of+  (r, q) -> reify r \(_ :: Proxy r) -> reify q \(_ :: Proxy q)+    -> Some2IntSetWith @s @r $ PartitionProof+      do \x -> if p x+          then Left $ unsafeElement $ unrefine x+          else Right $ unsafeElement $ unrefine x+      unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Divide a set into two disjoint subsets at a point where the predicate stops+-- holding.+--+-- If @p@ is antitone ( \(\forall x y, x < y \implies p(x) \ge p(y)\) ), then+-- this point is uniquely defined. If @p@ is not antitone, a splitting point is+-- chosen in an unspecified way.+spanAntitone+  :: forall s. KnownIntSet s+  => (Element s -> Bool) -> Some2IntSetWith (PartialPartitionProof 'Int s)+spanAntitone p =+#if MIN_VERSION_containers(0, 6, 7)+  case IntSet.spanAntitone (p . unsafeElement) $ reflect $ Proxy @s of+    (r, q)+#else+  case List.span (p . unsafeElement)+    $ IntSet.toAscList $ reflect $ Proxy @s of+    (rs, qs)+      | let r = IntSet.fromDistinctAscList rs+      , let q = IntSet.fromDistinctAscList qs+#endif+      -> reify r \(_ :: Proxy r) -> reify q \(_ :: Proxy q)+        -> Some2IntSetWith @r @q $ PartialPartitionProof+          unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Return two disjoint subsets: those less than the given element, and those+-- greater than the given element; along with the proof that the given element+-- was in the set, if it was.+splitMember+  :: forall s. KnownIntSet s+  => Int -> Some2IntSetWith (SplitProof 'Int s (Element s))+splitMember x = case IntSet.splitMember x $ reflect $ Proxy @s of+  (r, m, q) -> reify r \(_ :: Proxy r) -> reify q \(_ :: Proxy q)+    -> Some2IntSetWith @r @q $ SplitProof+      (if m then Just (unsafeElement x) else Nothing)+      unsafeSubset \f g -> unsafeSubsetWith2 f g++-- | Apply the given function to each element of the set and collect the+-- results. Note that the resulting set can be smaller.+map+  :: forall s. KnownIntSet s+  => (Element s -> Int) -> SomeIntSetWith (MapProof 'Int s Int Int)+map f = reify (IntMap.keysSet m) \(_ :: Proxy r) -> SomeIntSetWith @r+  $ MapProof (unsafeElement . f) \y -> case IntMap.lookup (unrefine y) m of+    Nothing -> error "map: bug: Data.IntSet.Refined has been subverted"+    Just x -> x+  where+    !m = IntMap.fromList+      [ (y, unsafeElement x)+      | x <- IntSet.toList $ reflect $ Proxy @s+      , let !y = f $ unsafeElement x+      ]+++-- | Map each element of @s@ into a monoid (with proof that it was an element),+-- and combine the results using 'Data.Monoid.<>'.+foldMap :: forall s m. (KnownIntSet s, Monoid m) => (Element s -> m) -> m+foldMap f = go $ reflect $ Proxy @s+  where+    go s = case IntSet.splitRoot s of+      [s'] -> Foldable.foldMap (f . unsafeElement) $ IntSet.toAscList s'+      xs -> Foldable.foldMap go xs++-- | Right associative fold with a lazy accumulator.+foldr :: forall s a. KnownIntSet s => (Element s -> a -> a) -> a -> a+foldr f z = IntSet.foldr (f . unsafeElement) z $ reflect $ Proxy @s++-- | Left associative fold with a lazy accumulator.+foldl :: forall s a. KnownIntSet s => (a -> Element s -> a) -> a -> a+foldl f z = IntSet.foldl ((. unsafeElement) . f) z $ reflect $ Proxy @s++-- | Right associative fold with a strict accumulator.+foldr' :: forall s a. KnownIntSet s => (Element s -> a -> a) -> a -> a+foldr' f z = IntSet.foldr' (f . unsafeElement) z $ reflect $ Proxy @s++-- | Left associative fold with a strict accumulator.+foldl' :: forall s a. KnownIntSet s => (a -> Element s -> a) -> a -> a+foldl' f z = IntSet.foldl' ((. unsafeElement) . f) z $ reflect $ Proxy @s++-- | Retrieves the smallest element of the set, and the set with that element+-- removed; or a proof that the set was empty.+minView+  :: forall s. KnownIntSet s+  => Either+    (EmptyProof 'Int s)+    (Element s, SomeIntSetWith (SupersetProof 'Int s))+minView = case IntSet.minView $ reflect $ Proxy @s of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (x, xs) -> Right $ (unsafeElement x,) $ reify xs \(_ :: Proxy r)+    -> SomeIntSetWith @r $ SupersetProof unsafeSubset++-- | Retrieves the greatest element of the set, and the set with that element+-- removed; or a proof that the set was empty.+maxView+  :: forall s. KnownIntSet s+  => Either+    (EmptyProof 'Int s)+    (Element s, SomeIntSetWith (SupersetProof 'Int s))+maxView = case IntSet.maxView $ reflect $ Proxy @s of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (x, xs) -> Right $ (unsafeElement x,) $ reify xs \(_ :: Proxy r)+    -> SomeIntSetWith @r $ SupersetProof unsafeSubset++-- | List of elements in the set in ascending order.+toList :: forall s. KnownIntSet s => [Element s]+toList = gcoerceWith (unsafeCastElement @s) $ coerce+  $ IntSet.toAscList $ reflect $ Proxy @s++-- | List of elements in the set in descending order.+toDescList :: forall s. KnownIntSet s => [Element s]+toDescList = gcoerceWith (unsafeCastElement @s) $ coerce+  $ IntSet.toDescList $ reflect $ Proxy @s++-- | Convert an 'IntSet' into a 'Set', retaining its set of elements, which can+-- be converted with 'castFlavor'.+asSet :: forall s. KnownIntSet s => Set s Int+asSet = intSet2Set++-- | Convert an 'IntSet' into a 'HashSet', retaining its set of elements, which+-- can be converted with 'castFlavor'.+asHashSet :: forall s. KnownIntSet s => HashSet s Int+asHashSet = intSet2HashSet++-- | If elements of @s@ can be weakened to elements of @t@ and vice versa, then+-- @s@ and @t@ actually stand for the same set and @'Element' s@ can be safely+-- interconverted with @'Element' t@.+--+-- The requirement that the weakenings are natural transformations ensures that+-- they don't actually alter the elements. To build these you can compose+-- ':->''s from proofs returned by functions in this module, or "Refined"+-- functions like 'andLeft' or 'leftOr'.+castElement+  :: forall s t a.+     (forall x. Refined (InSet 'Int s) x -> Refined (InSet 'Int t) x)+  -> (forall x. Refined (InSet 'Int t) x -> Refined (InSet 'Int s) x)+  -> Coercion (Refined (InSet 'Int s) a) (Refined (InSet 'Int t) a)+castElement = castRefined++-- | If elements can be interconverted (e.g. as proved by 'castElement'), then+-- the sets can be interconverted too. For example we can establish that the+-- intersection of a set with itself is interconvertible with that set:+--+-- @+-- castIntersection+--   :: t'IntersectionProof' ''Data.IntSet.Refined.Int' s s r+--   -> 'Coercion' ('IntSet' r) ('IntSet' s)+-- castIntersection ( v'IntersectionProof' p1 p2)+--   = 'cast' $ 'castElement' ('andLeft' . p1) (p2 'id' 'id')+-- @+cast+  :: forall s t. (forall x. Coercion+    (Refined (InSet 'Int s) x)+    (Refined (InSet 'Int t) x))+  -> Coercion (IntSet s) (IntSet t)+cast Coercion+#if MIN_VERSION_base(4, 15, 0)+  = case unsafeEqualityProof @s @t of UnsafeRefl -> Coercion+#else+  = repr $ unsafeCoerce Refl+#endif
+ src/Data/Map/Common/Refined.hs view
@@ -0,0 +1,515 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE UndecidableInstances #-}+module Data.Map.Common.Refined where++import           Control.Monad.Reader+import           Control.DeepSeq+import           Data.Coerce+import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Distributive+import           Data.Foldable.WithIndex+import           Data.Functor.Rep+import           Data.Functor.WithIndex+import qualified Data.Hashable as Hashable+import qualified Data.Map as Map+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable.WithIndex+import           Data.Type.Coercion+import           Data.Type.Equality ((:~:)(..))+import           Refined+import           Refined.Unsafe+import           Unsafe.Coerce++#if MIN_VERSION_containers(0, 6, 2)+#elif MIN_VERSION_containers(0, 5, 8)+import           Data.Functor.Const (Const(..))+import           Data.Monoid (Any(..))+import qualified Data.Map.Merge.Lazy as Map+#else+import qualified Data.List as List+import qualified Data.Map.Strict as MapStrict+#endif+++-- | A wrapper around a regular 'Data.Map.Map' with a type parameter @s@+-- identifying the set of keys present in the map.+--+-- A key of type @k@ may not be present in the map, but a @'Key' s k@ is+-- guaranteed to be present (if the @s@ parameters match). Thus the map is+-- isomorphic to a (total) function @'Key' s k -> a@, which motivates many of+-- the instances below.+--+-- A 'Map' always knows its set of keys, so given @'Map' s k a@ we can always+-- derive @'KnownSet' s k@ by pattern matching on the 'Dict' returned by+-- 'keysSet'.+newtype Map s k a = Map (Map.Map k a)+  deriving newtype (Eq, Ord, Show, Functor, Foldable, NFData)+#if MIN_VERSION_hashable(1, 3, 4)+  deriving newtype (Hashable.Hashable)+#endif+  deriving stock (Traversable)+type role Map nominal nominal representational++-- | Convert to a regular 'Data.Map.Map', forgetting its set of keys.+toMap :: forall s k a. Map s k a -> Map.Map k a+toMap (Map m) = m++-- | @'Key' s k@ is a key of type @k@ that has been verified to be an element+-- of the set @s@, and thus verified to be present in all @'Map' s k@ maps.+--+-- Thus, @'Key' s k@ is a \"refinement\" type of @k@, and this library+-- integrates with an implementation of refimenement types in "Refined", so+-- the machinery from there can be used to manipulate 'Key's (however see+-- 'Data.Set.Refined.revealPredicate').+--+-- The underlying @k@ value can be obtained with 'unrefine'. A @k@ can be+-- validated into an @'Key' s k@ with 'member'.+type Key s = Refined (InSet 'Regular s)++unsafeCastKey :: forall s k. Coercion k (Key s k)+unsafeCastKey = reallyUnsafeUnderlyingRefined++unsafeKey :: k -> Key s k+unsafeKey = coerceWith unsafeCastKey++-- | An existential wrapper for a 'Map' with an as-yet-unknown set of keys.+-- Pattern maching on it gives you a way to refer to the set (the parameter+-- @s@), e.g.+--+-- @+-- case 'fromMap' ... of+--   'SomeMap' \@s m -> doSomethingWith \@s+--+-- case 'fromMap' ... of+--   'SomeMap' (m :: 'Map' s k a) -> doSomethingWith \@s+-- @+data SomeMap k a where+  SomeMap :: forall s k a. !(Map s k a) -> SomeMap k a++-- | Apply a map with an unknown set of keys to a continuation that can accept+-- a map with any set of keys. This gives you a way to refer to the set (the+-- parameter @s@), e.g.:+--+-- @+-- 'withMap' ('fromMap' ...) $ \(m :: 'Map' s k a) -> doSomethingWith \@s+-- @+withMap :: forall k a r. SomeMap k a -> (forall s. Map s k a -> r) -> r+withMap (SomeMap m) k = k m++-- | Construct a map from a regular 'Data.Map.Map'.+fromMap :: forall k a. Map.Map k a -> SomeMap k a+fromMap m = SomeMap (Map m)++-- | An existential wrapper for a 'Map' with an as-yet-unknown set of keys,+-- together with a proof of some fact @p@ about the set. Pattern matching on it+-- gives you a way to refer to the set (the parameter @s@). Functions that+-- change the set of keys in a map will return the map in this way, together+-- with a proof that somehow relates the keys set to the function's inputs.+data SomeMapWith p k a where+  SomeMapWith :: forall s k a p. !(Map s k a) -> !(p s) -> SomeMapWith p k a++-- | Apply a map with proof for an unknown set of keys to a continuation that+-- can accept a map with any set of keys satisfying the proof. This gives you a+-- way to refer to the set (the parameter @s@).+withMapWith+  :: forall k a r p. SomeMapWith p k a -> (forall s. Map s k a -> p s -> r) -> r+withMapWith (SomeMapWith m p) k = k m p++-- | An existential wrapper for a pair of maps with as-yet-unknown sets of keys,+-- together with a proof of some fact @p@ relating them.+data Some2MapWith p k a b where+  Some2MapWith+    :: forall s t k a b p. !(Map s k a)+    -> !(Map t k b)+    -> !(p s t)+    -> Some2MapWith p k a b++-- | Apply a pair of maps with proof for unknown sets of keys to a continuation+-- that can accept any pair of maps with any sets of keys satisfying the proof.+-- This gives you a way to refer to the sets (the parameters @s@ and @t@).+with2MapWith+  :: forall k a b r p. Some2MapWith p k a b+  -> (forall s t. Map s k a -> Map t k b -> p s t -> r)+  -> r+with2MapWith (Some2MapWith m1 m2 p) k = k m1 m2 p++-- | An empty map.+empty :: forall k a. SomeMapWith (EmptyProof 'Regular) k a+empty = SomeMapWith (Map Map.empty) $ EmptyProof unsafeSubset++-- | Create a map from a set of keys, and a function that for each key computes+-- the corresponding value.+fromSet :: forall s k a. KnownSet s k => (Key s k -> a) -> Map s k a+fromSet f = Map $ Map.fromSet (f . unsafeKey) (reflect $ Proxy @s)++-- | Delete a key and its value from the map if present, returning a potentially+-- smaller map.+delete+  :: forall s k a. Ord k+  => k -> Map s k a -> SomeMapWith (SupersetProof 'Regular s) k a+delete k (Map m) = SomeMapWith (Map $ Map.delete k m)+  $ SupersetProof unsafeSubset++-- | If the key is in the map, return the proof of this, and the associated+-- value; otherwise return 'Nothing'.+lookup :: forall s k a. Ord k => k -> Map s k a -> Maybe (Key s k, a)+lookup k (Map m) = (unsafeKey k,) <$> Map.lookup k m++-- | Given a key that is proven to be in the map, return the associated value.+--+-- Unlike 'Data.Map.!' from "Data.Map", this function is total, as it is+-- impossible to obtain a @'Key' s k@ for a key that is not in the map+-- @'Map' s k a@.+(!) :: forall s k a. Ord k => Map s k a -> Key s k -> a+(!) (Map m) k = case Map.lookup (unrefine k) m of+  Nothing -> error "(!): bug: Data.Map.Refined has been subverted"+  Just x -> x++-- | If a key is in the map, return the proof that it is.+member :: forall s k a. Ord k => k -> Map s k a -> Maybe (Key s k)+member k (Map m)+  | k `Map.member` m = Just (unsafeKey k)+  | otherwise = Nothing++-- | Find the largest key smaller than the given one, and return the+-- associated key-value pair.+lookupLT :: forall s k a. Ord k => k -> Map s k a -> Maybe (Key s k, a)+lookupLT = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.lookupLT @k @a++-- | Find the smallest key greater than the given one, and return the+-- associated key-value pair.+lookupGT :: forall s k a. Ord k => k -> Map s k a -> Maybe (Key s k, a)+lookupGT = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.lookupGT @k @a++-- | Find the largest key smaller or equal to the given one, and return the+-- associated key-value pair.+lookupLE :: forall s k a. Ord k => k -> Map s k a -> Maybe (Key s k, a)+lookupLE = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.lookupLE @k @a++-- | Find the smallest key greater or equal to the given one, and return the+-- associated key-value pair.+lookupGE :: forall s k a. Ord k => k -> Map s k a -> Maybe (Key s k, a)+lookupGE = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.lookupGE @k @a++-- | If a map is empty, return a proof that it is.+null :: forall s k a. Map s k a -> Maybe (EmptyProof 'Regular s)+null (Map m)+  | Map.null m = Just $ EmptyProof unsafeSubset+  | otherwise = Nothing++-- | If all keys of the first map are also present in the second map, and the+-- given function returns 'True' for their associated values, return a proof+-- that the keys form a subset.+isSubmapOfBy+  :: forall s t k a b. Ord k+  => (a -> b -> Bool)+  -> Map s k a+  -> Map t k b+  -> Maybe (SubsetProof 'Regular s t)+isSubmapOfBy f (Map m1) (Map m2)+  | Map.isSubmapOfBy f m1 m2 = Just $ SubsetProof unsafeSubset+  | otherwise = Nothing++-- | If two maps are disjoint (i.e. their intersection is empty), return a proof+-- of that.+disjoint+  :: forall s t k a b. Ord k+  => Map s k a -> Map t k b -> Maybe (DisjointProof 'Regular s t)+disjoint (Map m1) (Map m2)+#if MIN_VERSION_containers(0, 6, 2)+  | Map.disjoint m1 m2+#elif MIN_VERSION_containers(0, 5, 8)+  | Const (Any False) <- Map.mergeA+    (Map.traverseMissing \_ _ -> Const mempty)+    (Map.traverseMissing \_ _ -> Const mempty)+    (Map.zipWithAMatched \_ _ _ -> Const $ Any True)+    m1+    m2+#else+  | Map.null $ MapStrict.intersectionWith (\_ _ -> ()) m1 m2+#endif+  = Just $ DisjointProof \f g -> unsafeSubsetWith2 f g+  | otherwise = Nothing++-- | Given two maps proven to have the same keys, for each key apply the+-- function to the associated values, to obtain a new map with the same keys.+zipWithKey+  :: forall s k a b c. Ord k+  => (Key s k -> a -> b -> c) -> Map s k a -> Map s k b -> Map s k c+zipWithKey f (Map m1) (Map m2) = Map+  $ Map.mergeWithKey (\k x y -> Just $ f (unsafeKey k) x y)+    (error "zipWithKey: bug: Data.Map.Refined has been subverted")+    (error "zipWithKey: bug: Data.Map.Refined has been subverted")+    m1+    m2++-- | Remove the keys that appear in the second map from the first map.+difference+  :: forall s t k a b. Ord k+  => Map s k a -> Map t k b -> SomeMapWith (DifferenceProof 'Regular s t) k a+difference (Map m1) (Map m2) = SomeMapWith (Map $ Map.difference m1 m2)+  $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, that are proven to be in the map. The set of keys remains the same.+mapWithKey :: forall s k a b. (Key s k -> a -> b) -> Map s k a -> Map s k b+mapWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.mapWithKey @k @a @b++-- | Map an 'Applicative' transformation in ascending order of keys, with access+-- to each value's corresponding key and a proof that it is in the map. The set+-- of keys remains unchanged.+traverseWithKey+  :: forall s f k a b. Applicative f+  => (Key s k -> a -> f b) -> Map s k a -> f (Map s k b)+traverseWithKey f (Map m) = Map <$> Map.traverseWithKey (f . unsafeKey) m++-- | Map each key-value pair of a map into a monoid (with proof that the key was+-- in the map), and combine the results using '<>'.+foldMapWithKey+  :: forall s k a m. Monoid m => (Key s k -> a -> m) -> Map s k a -> m+foldMapWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.foldMapWithKey @m @k @a++-- | Right associative fold with a lazy accumulator.+foldrWithKey :: forall s k a b. (Key s k -> a -> b -> b) -> b -> Map s k a -> b+foldrWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.foldrWithKey @k @a @b++-- | Left associative fold with a lazy accumulator.+foldlWithKey :: forall s k a b. (b -> Key s k -> a -> b) -> b -> Map s k a -> b+foldlWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.foldlWithKey @b @k @a++-- | Right associative fold with a strict accumulator.+foldrWithKey' :: forall s k a b. (Key s k -> a -> b -> b) -> b -> Map s k a -> b+foldrWithKey' = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.foldrWithKey' @k @a @b++-- | Left associative fold with a strict accumulator.+foldlWithKey' :: forall s k a b. (b -> Key s k -> a -> b) -> b -> Map s k a -> b+foldlWithKey' = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.foldlWithKey' @b @k @a++-- | Return the set of keys in the map, with the contents of the set still+-- tracked by the @s@ parameter. See "Data.Set.Refined".+keysSet :: forall s k a. Map s k a -> Set s k+keysSet (Map m) = reify (Map.keysSet m)+  \(_ :: Proxy s') -> case unsafeCoerce Refl :: s :~: s' of+    Refl -> Dict++-- | Convert to a list of key-value pairs in ascending order of keys.+toList :: forall s k a. Map s k a -> [(Key s k, a)]+toList = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.toAscList @k @a++-- | Convert to a list of key-value pairs in descending order of keys.+toDescList :: forall s k a. Map s k a -> [(Key s k, a)]+toDescList = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.toDescList @k @a++-- | Retain only the key-value pairs that satisfy the predicate, returning a+-- potentially smaller map.+filterWithKey+  :: forall s k a. (Key s k -> a -> Bool)+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k a+filterWithKey p (Map m)+  = SomeMapWith (Map $ Map.filterWithKey (p . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Restrict a map to only those keys that are elements of @t@.+restrictKeys+  :: forall s t k a. (Ord k, KnownSet t k)+  => Map s k a -> SomeMapWith (IntersectionProof 'Regular s t) k a+restrictKeys (Map m) = SomeMapWith+#if MIN_VERSION_containers(0, 5, 8)+  (Map $ Map.restrictKeys m $ reflect $ Proxy @t)+#else+  (Map $ Map.intersectionWith const m $ Map.fromSet id $ reflect $ Proxy @t)+#endif+  $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Remove all keys that are elements of @t@ from the map.+withoutKeys+  :: forall s t k a. (Ord k, KnownSet t k)+  => Map s k a -> SomeMapWith (DifferenceProof 'Regular s t) k a+withoutKeys (Map m) = SomeMapWith+#if MIN_VERSION_containers(0, 5, 8)+  (Map $ Map.withoutKeys m $ reflect $ Proxy @t)+#else+  (Map $ Map.difference m $ Map.fromSet id $ reflect $ Proxy @t)+#endif+  $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Partition a map into two disjoint submaps: those whose key-value pairs+-- satisfy the predicate, and those whose don't.+partitionWithKey+  :: forall s k a. Ord k -- TODO: this is only used in the proof+  => (Key s k -> a -> Bool)+  -> Map s k a+  -> Some2MapWith (PartitionProof 'Regular s k) k a a+partitionWithKey p (Map m) = case Map.partitionWithKey (p . unsafeKey) m of+  (m1, m2) -> Some2MapWith (Map m1) (Map m2) $ PartitionProof+    do \k -> case Map.lookup (unrefine k) m of+        Nothing+          -> error "partitionWithKey: bug: Data.Map.Refined has been subverted"+        Just x -> if p k x+          then Left $ unsafeKey $ unrefine k+          else Right $ unsafeKey $ unrefine k+    unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Divide a map into two disjoint submaps at a point where the predicate on+-- the keys stops holding.+--+-- If @p@ is antitone ( \(\forall x y, x < y \implies p(x) \ge p(y)\) ), then+-- this point is uniquely defined. If @p@ is not antitone, a splitting point is+-- chosen in an unspecified way.+spanAntitone+  :: forall s k a. (Key s k -> Bool)+  -> Map s k a+  -> Some2MapWith (PartialPartitionProof 'Regular s) k a a+spanAntitone p (Map m) =+#if MIN_VERSION_containers(0, 5, 8)+  case Map.spanAntitone (p . unsafeKey) m of+    (m1, m2)+#else+  case List.span (p . unsafeKey . fst) $ Map.toAscList m of+    (xs1, xs2)+      | let m1 = Map.fromDistinctAscList xs1+      , let m2 = Map.fromDistinctAscList xs2+#endif+      -> Some2MapWith (Map m1) (Map m2) $ PartialPartitionProof+        unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Return two disjoint submaps: those whose keys are less than the given key,+-- and those whose keys are greater than the given key. If the key was in the+-- map, also return the associated value and the proof that it was in the map.+splitLookup+  :: forall s k a. Ord k+  => k -> Map s k a -> Some2MapWith (SplitProof 'Regular s (Key s k, a)) k a a+splitLookup k (Map m) = case Map.splitLookup k m of+  (!m1, v, !m2) -> Some2MapWith (Map m1) (Map m2) $ SplitProof+    ((unsafeKey k,) <$> v) unsafeSubset \f g -> unsafeSubsetWith2 f g++-- | Retrieves the key-value pair corresponding to the smallest key of the map,+-- and the map with that pair removed; or a proof that the map was empty.+minViewWithKey+  :: forall s k a. Map s k a+  -> Either+    (EmptyProof 'Regular s)+    ((Key s k, a), SomeMapWith (SupersetProof 'Regular s) k a)+minViewWithKey (Map m) = case Map.minViewWithKey m of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (kv, m') -> Right $ (gcoerceWith (unsafeCastKey @s @k) $ coerce kv,)+    $ SomeMapWith (Map m') $ SupersetProof unsafeSubset++-- | Retrieves the key-value pair corresponding to the greatest key of the map,+-- and the map with that pair removed; or a proof that the map was empty.+maxViewWithKey+  :: forall s k a. Map s k a+  -> Either+    (EmptyProof 'Regular s)+    ((Key s k, a), SomeMapWith (SupersetProof 'Regular s) k a)+maxViewWithKey (Map m) = case Map.maxViewWithKey m of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (kv, m') -> Right $ (gcoerceWith (unsafeCastKey @s @k) $ coerce kv,)+    $ SomeMapWith (Map m') $ SupersetProof unsafeSubset++-- | If elements of @s@ can be weakened to elements of @t@ and vice versa, then+-- @s@ and @t@ actually stand for the same set and @'Key' s@ can be safely+-- interconverted with @'Key' t@.+--+-- The requirement that the weakenings are natural transformations ensures that+-- they don't actually alter the keys. To build these you can compose ':->''s+-- from proofs returned by functions in this module, or "Refined" functions like+-- 'andLeft' or 'leftOr'.+castKey+  :: forall s t k. (forall x. Key s x -> Key t x)+  -> (forall x. Key t x -> Key s x)+  -> Coercion (Key s k) (Key t k)+castKey = castRefined++-- | If keys can be interconverted (e.g. as proved by 'castKey'), then the maps+-- can be interconverted too. For example, 'zipWithKey' can be implemented via+-- 'Data.Map.Refined.intersectionWithKey' by proving that the set of keys+-- remains unchanged:+--+-- @+-- 'zipWithKey'+--   :: forall s k a b c. 'Ord' k+--   => ('Key' s k -> a -> b -> c) -> 'Map' s k a -> 'Map' s k b -> 'Map' s k c+-- 'zipWithKey' f m1 m2+--   | v'SomeMapWith' @r m proof <- 'Data.Map.Refined.intersectionWithKey' (f . 'andLeft') m1 m2+--   , v'IntersectionProof' p1 p2 <- proof+--   , ( v'Coercion' :: t'Coercion' ('Map' r k c) ('Map' s k c))+--     <- app $ 'cast' $ 'castKey' ('andLeft' . p1) (p2 'id' 'id')+--   = 'coerce' m+--   where+--     app :: t'Coercion' f g -> t'Coercion' (f x) (g x)+--     app v'Coercion' = v'Coercion'+-- @+cast+  :: forall s t k. (forall x. Coercion (Key s x) (Key t x))+  -> Coercion (Map s k) (Map t k)+cast Coercion = Coercion++instance FunctorWithIndex (Key s k) (Map s k) where+  imap = mapWithKey++instance FoldableWithIndex (Key s k) (Map s k) where+  ifoldMap = foldMapWithKey++instance TraversableWithIndex (Key s k) (Map s k) where+  itraverse = traverseWithKey++-- | Similar to the instance for functions -- zip corresponding keys. To use+-- '<*>'/'liftA2' without 'KnownSet' see 'zipWithKey'.+instance (Ord k, KnownSet s k) => Applicative (Map s k) where+  pure x = fromSet \_ -> x+  (<*>) = zipWithKey (const id)++-- | @'bind' m f@ is a map that for each key @k :: 'Key' s k@, contains the+-- value @f (m '!' k) '!' k@, similar to @'>>='@ for functions.+bind :: forall s k a b. Ord k => Map s k a -> (a -> Map s k b) -> Map s k b+bind m f = mapWithKey (\k x -> f x ! k) m++-- | Similar to the instance for functions. To use '>>=' without 'KnownSet' see+-- 'bind'.+instance (Ord k, KnownSet s k) => Monad (Map s k) where+  (>>=) = bind++-- | Similar to the instance for functions. See also+-- 'Data.Map.Refined.backpermuteKeys'.+instance (Ord k, KnownSet s k) => MonadReader (Key s k) (Map s k) where+  ask = fromSet id+  local f m = mapWithKey (\k _ -> m ! f k) m++-- | Append the values at the corresponding keys+instance (Ord k, Semigroup a) => Semigroup (Map s k a) where+  (<>) = zipWithKey (const (<>))++instance (Ord k, KnownSet s k, Monoid a) => Monoid (Map s k a) where+  mempty = fromSet \_ -> mempty++-- | Similar to the instance for functions+instance (Ord k, KnownSet s k) => Distributive (Map s k) where+  collect = collectRep+  distribute = distributeRep++-- | Witness isomorphism with functions from @'Key' s k@+instance (Ord k, KnownSet s k) => Representable (Map s k) where+  type Rep (Map s k) = Key s k+  index = (!)+  tabulate = fromSet++#if MIN_VERSION_hashable(1, 3, 4)+#else+instance (Hashable.Hashable a, Hashable.Hashable k)+  => Hashable.Hashable (Map s k a) where+  hashWithSalt s (Map m) = Map.foldlWithKey'+    (\s' k v -> Hashable.hashWithSalt (Hashable.hashWithSalt s' k) v)+    (Hashable.hashWithSalt s (Map.size m))+    m+#endif
+ src/Data/Map/Refined.hs view
@@ -0,0 +1,384 @@+-- | This module defines a way to prove that a key exists in a map, so that the+-- key can be used to index into the map without using a 'Maybe', or manually+-- handling the \"impossible\" case with 'error' or other partial functions.+--+-- To do this, @'Map' s k v@ has a type parameter @s@ that identifies its set of+-- keys, so that if another map has the same type parameter, you know that map+-- has the same set of keys. There is @'Key' s k@, a type of keys that have been+-- validated to belong to the set identified by @s@, and for which the operation+-- of indexing into a @'Map' s k v@ (only for the same @s@) can proceed without+-- failure (see '!'). The type @s@ itself has no internal structure, rather it+-- is merely a skolem type variable (rank-2 polymorphism+-- 'Control.Monad.ST.runST' trick) introduced by "Data.Reflection".+--+-- Like "Data.Map", functions in this module are strict in the keys but lazy in+-- the values. The "Data.Map.Strict.Refined" module reuses the same 'Map' type+-- but provides functions that operate strictly on the values.+--+-- = Warning+-- This module together with "Data.Map" rely on 'Eq' and 'Ord' instances being+-- lawful: that '==' is an equivalence relation, and that the 'Ord' operations+-- define a total order on the quotient defined by this equivalence relation; at+-- least for the subset of keys that are actually encountered at runtime. If+-- this assumption is violated, this module may not be able to uphold its+-- invariants and may throw errors. In particular beware of NaN in 'Float' and+-- 'Double'.+module Data.Map.Refined+  (+  -- * Map type+    Common.Map+  , Common.Key+  -- * Existentials and common proofs+  , Common.SomeMap(..)+  , Common.withMap+  , Common.SomeMapWith(..)+  , Common.withMapWith+  , Common.Some2MapWith(..)+  , Common.with2MapWith+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , Common.empty+  , singleton+  , SingletonProof(..)+  , fromSet+  , Common.fromMap+  , fromTraversableWithKey+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  , reinsert+  , insertLookupWithKey+  -- * Deletion/Update+  , Common.delete+  , adjust+  , adjustWithKey+  , update+  , updateLookupWithKey+  -- * Query+  , Common.lookup+  , (Common.!)+  , Common.member+  , Common.lookupLT+  , Common.lookupGT+  , Common.lookupLE+  , Common.lookupGE+  , Common.null+  , Common.isSubmapOfBy+  , SubsetProof(..)+  , Common.disjoint+  , DisjointProof(..)+  -- * Combine+  , zipWithKey+  , bind+  , unionWithKey+  , UnionProof(..)+  , Common.difference+  , DifferenceProof(..)+  , differenceWithKey+  , PartialDifferenceProof(..)+  , intersectionWithKey+  , IntersectionProof(..)+  -- * Traversal+  , mapWithKey+  , traverseWithKey+  , mapAccumLWithKey+  , mapAccumRWithKey+  , mapKeysWith+  , MapProof(..)+  , backpermuteKeys+  -- * Folds+  , Common.foldMapWithKey+  , Common.foldrWithKey+  , Common.foldlWithKey+  , Common.foldrWithKey'+  , Common.foldlWithKey'+  -- * Conversion+  , Common.toMap+  , Common.keysSet+  , Common.toList+  , Common.toDescList+  -- * Filter+  , Common.restrictKeys+  , Common.withoutKeys+  , Common.filterWithKey+  , Common.partitionWithKey+  , PartitionProof(..)+  , Common.spanAntitone+  , PartialPartitionProof(..)+  , mapMaybeWithKey+  , mapEitherWithKey+  , Common.splitLookup+  , SplitProof(..)+  -- * Min/Max+  , updateMinWithKey+  , updateMaxWithKey+  , adjustMinWithKey+  , adjustMaxWithKey+  , Common.minViewWithKey+  , Common.maxViewWithKey+  -- * Casts+  , Common.castKey+  , Common.cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Functor+import qualified Data.Map as Map+import           Data.Map.Common.Refined+  ( Map(..), Key, unsafeCastKey, unsafeKey, SomeMapWith(..), Some2MapWith(..)+  , fromSet, (!), zipWithKey, mapWithKey, traverseWithKey, bind+  )+import qualified Data.Map.Common.Refined as Common+import           Data.Traversable+import           Data.Type.Coercion+import           Prelude hiding (lookup, null)+import           Refined+import           Refined.Unsafe+++-- | Create a map with a single key-value pair, and return a proof that the+-- key is in the resulting map.+singleton :: forall k a. k -> a -> SomeMapWith (SingletonProof 'Regular k) k a+singleton k v = SomeMapWith (Map $ Map.singleton k v)+  $ SingletonProof (unsafeKey k)++-- | Create a map from an arbitrary traversable of key-value pairs.+fromTraversableWithKey+  :: forall t k a. (Traversable t, Ord k)+  => (k -> a -> a -> a)+  -> t (k, a)+  -> SomeMapWith (FromTraversableProof 'Regular t k) k a+fromTraversableWithKey f xs = SomeMapWith (Map m) $ FromTraversableProof proof+  where+    (m, proof) = mapAccumL+      (\s (k, v) -> let !s' = Map.insertWithKey f k v s in (s', unsafeKey k))+      Map.empty+      xs++-- | Insert a key-value pair into the map to obtain a potentially larger map,+-- guaranteed to contain the given key. If the key was already present, the+-- associated value is replaced with the supplied value.+insert+  :: forall s k a. Ord k+  => k -> a -> Map s k a -> SomeMapWith (InsertProof 'Regular k s) k a+insert k v (Map m) = SomeMapWith (Map $ Map.insert k v m)+  $ InsertProof (unsafeKey k) unsafeSubset++-- | Overwrite a key-value pair that is known to already be in the map. The set+-- of keys remains the same.+reinsert+  :: forall s k a. Ord k+  => Key s k -> a -> Map s k a -> Map s k a+reinsert = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.insert @k @a++-- | Insert a key-value pair into the map using a combining function, and if+-- the key was already present, the old value is returned along with the proof+-- that the key was present.+insertLookupWithKey+  :: forall s k a. Ord k+  => (Key s k -> a -> a -> a)+  -> k+  -> a+  -> Map s k a+  -> (Maybe (Key s k, a), SomeMapWith (InsertProof 'Regular k s) k a)+insertLookupWithKey f k v (Map m)+  = case Map.insertLookupWithKey (f . unsafeKey) k v m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeMapWith (Map m') $ InsertProof (unsafeKey k) unsafeSubset++-- | Update the value at a specific key known the be in the map using the given+-- function. The set of keys remains the same.+adjust :: forall s k a. Ord k => (a -> a) -> Key s k -> Map s k a -> Map s k a+adjust = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.adjust @k @a++-- | If the given key is in the map, update the associated value using the given+-- function with a proof that the key was in the map; otherwise return the map+-- unchanged. In any case the set of keys remains the same.+adjustWithKey+  :: forall s k a. Ord k => (Key s k -> a -> a) -> k -> Map s k a -> Map s k a+adjustWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.adjustWithKey @k @a++-- | Update or delete a key known to be in the map using the given function,+-- returning a potentially smaller map.+update+  :: forall s k a. Ord k+  => (a -> Maybe a)+  -> Key s k+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k a+update f k (Map m) = SomeMapWith (Map $ Map.update f (unrefine k) m)+  $ SupersetProof unsafeSubset++-- | If the given key is in the map, update or delete it using the given+-- function with a proof that the key was in the map; otherwise the map is+-- unchanged. Alongside return the new value if it was updated, or the old value+-- if it was deleted, and a proof that the key was in the map.+updateLookupWithKey+  :: forall s k a. Ord k+  => (Key s k -> a -> Maybe a)+  -> k+  -> Map s k a+  -> (Maybe (Key s k, a), SomeMapWith (SupersetProof 'Regular s) k a)+updateLookupWithKey f k (Map m)+  = case Map.updateLookupWithKey (f . unsafeKey) k m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeMapWith (Map m') $ SupersetProof unsafeSubset++-- | Return the union of two maps, with a given combining function for keys that+-- exist in both maps simultaneously.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+unionWithKey+  :: forall s t k a. Ord k+  => (Refined (InSet 'Regular s && InSet 'Regular t) k -> a -> a -> a)+  -> Map s k a+  -> Map t k a+  -> SomeMapWith (UnionProof 'Regular s t) k a+unionWithKey f (Map m1) (Map m2)+  = SomeMapWith (Map $ Map.unionWithKey (f . reallyUnsafeRefine) m1 m2)+    $ UnionProof unsafeSubset unsafeSubsetWith2++-- | For keys that appear in both maps, the given function decides whether the+-- key is removed from the first map.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+differenceWithKey+  :: forall s t k a b. Ord k+  => (Refined (InSet 'Regular s && InSet 'Regular t) k -> a -> b -> Maybe a)+  -> Map s k a+  -> Map t k b+  -> SomeMapWith (PartialDifferenceProof 'Regular s t) k a+differenceWithKey f (Map m1) (Map m2)+  = SomeMapWith (Map $ Map.differenceWithKey (f . reallyUnsafeRefine) m1 m2)+    $ PartialDifferenceProof unsafeSubset unsafeSubset++-- | Return the intersection of two maps with the given combining function.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+intersectionWithKey+  :: forall s t k a b c. Ord k+  => (Refined (InSet 'Regular s && InSet 'Regular t) k -> a -> b -> c)+  -> Map s k a+  -> Map t k b+  -> SomeMapWith (IntersectionProof 'Regular s t) k c+intersectionWithKey f (Map m1) (Map m2)+  = SomeMapWith (Map $ Map.intersectionWithKey (f . reallyUnsafeRefine) m1 m2)+    $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Thread an accumularing argument through the map in ascending order of keys.+mapAccumLWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> Map s k b+  -> (a, Map s k c)+mapAccumLWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.mapAccumWithKey @a @k @b @c++-- | Thread an accumularing argument through the map in descending order of+-- keys.+mapAccumRWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> Map s k b+  -> (a, Map s k c)+mapAccumRWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.mapAccumRWithKey @a @k @b @c++-- | @'mapKeysWith' c f m@ applies @f@ to each key of @m@ and collects the+-- results into a new map. For keys that were mapped to the same new key, @c@+-- acts as the combining function for corresponding values.+mapKeysWith+  :: forall s k1 k2 a. Ord k2+  => (a -> a -> a)+  -> (Key s k1 -> k2)+  -> Map s k1 a+  -> SomeMapWith (MapProof 'Regular s k1 k2) k2 a+mapKeysWith f g (Map m)+  = SomeMapWith (Map $ Map.mapKeysWith f (g . unsafeKey) m)+    $ MapProof (unsafeKey . g) \k2 -> case Map.lookup (unrefine k2) backMap of+      Nothing -> error "mapKeysWith: bug: Data.Map.Refined has been subverted"+      Just k1 -> k1+  where+    ~backMap = Map.fromList+      [ (k2, unsafeKey k1)+      | k1 <- Map.keys m+      , let !k2 = g $ unsafeKey k1+      ]++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect only the 'Just' results, returning a potentially smaller+-- map.+mapMaybeWithKey+  :: forall s k a b. (Key s k -> a -> Maybe b)+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k b+mapMaybeWithKey f (Map m)+  = SomeMapWith (Map $ Map.mapMaybeWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect the 'Left' and 'Right' results into separate (disjoint)+-- maps.+mapEitherWithKey+  :: forall s k a b c. Ord k -- TODO: this is only used in the proof+  => (Key s k -> a -> Either b c)+  -> Map s k a+  -> Some2MapWith (PartitionProof 'Regular s k) k b c+mapEitherWithKey p (Map m) = case Map.mapEitherWithKey (p . unsafeKey) m of+  (m1, m2) -> Some2MapWith (Map m1) (Map m2) $ PartitionProof+    do \k -> case Map.lookup (unrefine k) m of+        Nothing+          -> error "mapEitherWithKey: bug: Data.Map.Refined has been subverted"+        Just x -> case p k x of+          Left _ -> Left $ unsafeKey $ unrefine k+          Right _ -> Right $ unsafeKey $ unrefine k+    unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Update or delete the value at the smallest key, returning a potentially+-- smaller map.+updateMinWithKey+  :: forall s k a. (Key s k -> a -> Maybe a)+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k a+updateMinWithKey f (Map m)+  = SomeMapWith (Map $ Map.updateMinWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Update or delete the value at the largest key, returning a potentially+-- smaller map.+updateMaxWithKey+  :: forall s k a. (Key s k -> a -> Maybe a)+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k a+updateMaxWithKey f (Map m)+  = SomeMapWith (Map $ Map.updateMaxWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Adjust the value at the smallest key. The set of keys remains unchanged.+adjustMinWithKey :: forall s k a. (Key s k -> a -> a) -> Map s k a -> Map s k a+adjustMinWithKey f (Map m)+  = Map $ Map.updateMinWithKey ((Just .) . f . unsafeKey) m++-- | Adjust the value at the greatest key. The set of keys remains unchanged.+adjustMaxWithKey :: forall s k a. (Key s k -> a -> a) -> Map s k a -> Map s k a+adjustMaxWithKey f (Map m)+  = Map $ Map.updateMaxWithKey ((Just .) . f . unsafeKey) m++-- | Apply the inverse image of the given function to the keys of the given map,+-- so that for all @k :: 'Key' s2 k2@,+-- @'backpermuteKeys' f m '!' k = m '!' f k@.+--+-- If maps are identified with functions, this computes the composition.+backpermuteKeys+  :: forall s1 s2 k1 k2 a. (Ord k1, KnownSet s2 k2)+  => (Key s2 k2 -> Key s1 k1) -> Map s1 k1 a -> Map s2 k2 a+backpermuteKeys f m = fromSet \k -> m ! f k
+ src/Data/Map/Strict/Refined.hs view
@@ -0,0 +1,423 @@+-- | This module defines a way to prove that a key exists in a map, so that the+-- key can be used to index into the map without using a 'Maybe', or manually+-- handling the \"impossible\" case with 'error' or other partial functions.+--+-- To do this, @'Map' s k v@ has a type parameter @s@ that identifies its set of+-- keys, so that if another map has the same type parameter, you know that map+-- has the same set of keys. There is @'Key' s k@, a type of keys that have been+-- validated to belong to the set identified by @s@, and for which the operation+-- of indexing into a @'Map' s k v@ (only for the same @s@) can proceed without+-- failure (see '!'). The type @s@ itself has no internal structure, rather it+-- is merely a skolem type variable (rank-2 polymorphism+-- 'Control.Monad.ST.runST' trick) introduced by "Data.Reflection".+--+-- Like "Data.Map.Strict", functions in this module are strict in the keys and+-- values. The "Data.Map.Refined" module reuses the same 'Map' type but provides+-- functions that operate lazily on the values.+--+-- = Warning+-- This module together with "Data.Map.Strict" rely on 'Eq' and 'Ord' instances+-- being lawful: that '==' is an equivalence relation, and that the 'Ord'+-- operations define a total order on the quotient defined by this equivalence+-- relation; at least for the subset of keys that are actually encountered at+-- runtime. If this assumption is violated, this module may not be able to+-- uphold its invariants and may throw errors. In particular beware of NaN in+-- 'Float' and 'Double'.+module Data.Map.Strict.Refined+  (+  -- * Map type+    Common.Map+  , Common.Key+  -- * Existentials and common proofs+  , Common.SomeMap(..)+  , Common.withMap+  , Common.SomeMapWith(..)+  , Common.withMapWith+  , Common.Some2MapWith(..)+  , Common.with2MapWith+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , Common.empty+  , singleton+  , SingletonProof(..)+  , fromSet+  , Common.fromMap+  , fromTraversableWithKey+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  , reinsert+  , insertLookupWithKey+  -- * Deletion/Update+  , Common.delete+  , adjust+  , adjustWithKey+  , update+  , updateLookupWithKey+  -- * Query+  , Common.lookup+  , (Common.!)+  , Common.member+  , Common.lookupLT+  , Common.lookupGT+  , Common.lookupLE+  , Common.lookupGE+  , Common.null+  , Common.isSubmapOfBy+  , SubsetProof(..)+  , Common.disjoint+  , DisjointProof(..)+  -- * Combine+  , zipWithKey+  , bind+  , unionWithKey+  , UnionProof(..)+  , Common.difference+  , DifferenceProof(..)+  , differenceWithKey+  , PartialDifferenceProof(..)+  , intersectionWithKey+  , IntersectionProof(..)+  -- * Traversal+  , mapWithKey+  , traverseWithKey+  , mapAccumLWithKey+  , mapAccumRWithKey+  , mapKeysWith+  , MapProof(..)+  , backpermuteKeys+  -- * Folds+  , Common.foldMapWithKey+  , Common.foldrWithKey+  , Common.foldlWithKey+  , Common.foldrWithKey'+  , Common.foldlWithKey'+  -- * Conversion+  , Common.toMap+  , Common.keysSet+  , Common.toList+  , Common.toDescList+  -- * Filter+  , Common.restrictKeys+  , Common.withoutKeys+  , Common.filterWithKey+  , Common.partitionWithKey+  , PartitionProof(..)+  , Common.spanAntitone+  , PartialPartitionProof(..)+  , mapMaybeWithKey+  , mapEitherWithKey+  , Common.splitLookup+  , SplitProof(..)+  -- * Min/Max+  , updateMinWithKey+  , updateMaxWithKey+  , adjustMinWithKey+  , adjustMaxWithKey+  , Common.minViewWithKey+  , Common.maxViewWithKey+  -- * Casts+  , Common.castKey+  , Common.cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import           Data.Functor+import qualified Data.Map.Strict as Map+import           Data.Map.Common.Refined+  ( Map(..), Key, unsafeCastKey, unsafeKey, SomeMapWith(..), Some2MapWith(..)+  , (!)+  )+import qualified Data.Map.Common.Refined as Common+import           Data.Proxy+import           Data.Reflection+import           Data.Traversable+import           Data.Type.Coercion+import           Prelude hiding (lookup, null)+import           Refined+import           Refined.Unsafe+++-- | Create a map with a single key-value pair, and return a proof that the+-- key is in the resulting map.+singleton :: forall k a. k -> a -> SomeMapWith (SingletonProof 'Regular k) k a+singleton k v = SomeMapWith (Map $ Map.singleton k v)+  $ SingletonProof (unsafeKey k)++-- | Create a map from a set of keys, and a function that for each key computes+-- the corresponding value.+fromSet :: forall s k a. KnownSet s k => (Key s k -> a) -> Map s k a+fromSet f = Map $ Map.fromSet (f . unsafeKey) (reflect $ Proxy @s)++-- | Create a map from an arbitrary traversable of key-value pairs.+fromTraversableWithKey+  :: forall t k a. (Traversable t, Ord k)+  => (k -> a -> a -> a)+  -> t (k, a)+  -> SomeMapWith (FromTraversableProof 'Regular t k) k a+fromTraversableWithKey f xs = SomeMapWith (Map m) $ FromTraversableProof proof+  where+    (m, proof) = mapAccumL+      (\s (k, v) -> let !s' = Map.insertWithKey f k v s in (s', unsafeKey k))+      Map.empty+      xs++-- | Insert a key-value pair into the map to obtain a potentially larger map,+-- guaranteed to contain the given key. If the key was already present, the+-- associated value is replaced with the supplied value.+insert+  :: forall s k a. Ord k+  => k -> a -> Map s k a -> SomeMapWith (InsertProof 'Regular k s) k a+insert k v (Map m) = SomeMapWith (Map $ Map.insert k v m)+  $ InsertProof (unsafeKey k) unsafeSubset++-- | Overwrite a key-value pair that is known to already be in the map. The set+-- of keys remains the same.+reinsert+  :: forall s k a. Ord k+  => Key s k -> a -> Map s k a -> Map s k a+reinsert = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.insert @k @a++-- | Insert a key-value pair into the map using a combining function, and if+-- the key was already present, the old value is returned along with the proof+-- that the key was present.+insertLookupWithKey+  :: forall s k a. Ord k+  => (Key s k -> a -> a -> a)+  -> k+  -> a+  -> Map s k a+  -> (Maybe (Key s k, a), SomeMapWith (InsertProof 'Regular k s) k a)+insertLookupWithKey f k v (Map m)+  = case Map.insertLookupWithKey (f . unsafeKey) k v m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeMapWith (Map m') $ InsertProof (unsafeKey k) unsafeSubset++-- | Update the value at a specific key known the be in the map using the given+-- function. The set of keys remains the same.+adjust :: forall s k a. Ord k => (a -> a) -> Key s k -> Map s k a -> Map s k a+adjust = gcoerceWith (unsafeCastKey @s @k) $ coerce $ Map.adjust @k @a++-- | If the given key is in the map, update the associated value using the given+-- function with a proof that the key was in the map; otherwise return the map+-- unchanged. In any case the set of keys remains the same.+adjustWithKey+  :: forall s k a. Ord k => (Key s k -> a -> a) -> k -> Map s k a -> Map s k a+adjustWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.adjustWithKey @k @a++-- | Update or delete a key known to be in the map using the given function,+-- returning a potentially smaller map.+update+  :: forall s k a. Ord k+  => (a -> Maybe a)+  -> Key s k+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k a+update f k (Map m) = SomeMapWith (Map $ Map.update f (unrefine k) m)+  $ SupersetProof unsafeSubset++-- | If the given key is in the map, update or delete it using the given+-- function with a proof that the key was in the map; otherwise the map is+-- unchanged. Alongside return the new value if it was updated, or the old value+-- if it was deleted, and a proof that the key was in the map.+updateLookupWithKey+  :: forall s k a. Ord k+  => (Key s k -> a -> Maybe a)+  -> k+  -> Map s k a+  -> (Maybe (Key s k, a), SomeMapWith (SupersetProof 'Regular s) k a)+updateLookupWithKey f k (Map m)+  = case Map.updateLookupWithKey (f . unsafeKey) k m of+    (v', !m') -> ((unsafeKey k,) <$> v',)+      $ SomeMapWith (Map m') $ SupersetProof unsafeSubset++-- | Given two maps proven to have the same keys, for each key apply the+-- function to the associated values, to obtain a new map with the same keys.+zipWithKey+  :: forall s k a b c. Ord k+  => (Key s k -> a -> b -> c) -> Map s k a -> Map s k b -> Map s k c+zipWithKey f (Map m1) (Map m2) = Map+  $ Map.mergeWithKey (\k x y -> Just $ f (unsafeKey k) x y)+    (error "zipWithKey: bug: Data.Map.Strict.Refined has been subverted")+    (error "zipWithKey: bug: Data.Map.Strict.Refined has been subverted")+    m1+    m2++-- | Return the union of two maps, with a given combining function for keys that+-- exist in both maps simultaneously.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+unionWithKey+  :: forall s t k a. Ord k+  => (Refined (InSet 'Regular s && InSet 'Regular t) k -> a -> a -> a)+  -> Map s k a+  -> Map t k a+  -> SomeMapWith (UnionProof 'Regular s t) k a+unionWithKey f (Map m1) (Map m2)+  = SomeMapWith (Map $ Map.unionWithKey (f . reallyUnsafeRefine) m1 m2)+    $ UnionProof unsafeSubset unsafeSubsetWith2++-- | For keys that appear in both maps, the given function decides whether the+-- key is removed from the first map.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+differenceWithKey+  :: forall s t k a b. Ord k+  => (Refined (InSet 'Regular s && InSet 'Regular t) k -> a -> b -> Maybe a)+  -> Map s k a+  -> Map t k b+  -> SomeMapWith (PartialDifferenceProof 'Regular s t) k a+differenceWithKey f (Map m1) (Map m2)+  = SomeMapWith (Map $ Map.differenceWithKey (f . reallyUnsafeRefine) m1 m2)+    $ PartialDifferenceProof unsafeSubset unsafeSubset++-- | Return the intersection of two maps with the given combining function.+--+-- You can use 'andLeft' and 'andRight' to obtain @'Key' s k@ and @'Key' t k@+-- respectively.+intersectionWithKey+  :: forall s t k a b c. Ord k+  => (Refined (InSet 'Regular s && InSet 'Regular t) k -> a -> b -> c)+  -> Map s k a+  -> Map t k b+  -> SomeMapWith (IntersectionProof 'Regular s t) k c+intersectionWithKey f (Map m1) (Map m2)+  = SomeMapWith (Map $ Map.intersectionWithKey (f . reallyUnsafeRefine) m1 m2)+    $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Apply a function to all values in a map, together with their corresponding+-- keys, that are proven to be in the map. The set of keys remains the same.+mapWithKey :: forall s k a b. (Key s k -> a -> b) -> Map s k a -> Map s k b+mapWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.mapWithKey @k @a @b++-- | Map an 'Applicative' transformation in ascending order of keys, with access+-- to each value's corresponding key and a proof that it is in the map. The set+-- of keys remains unchanged.+traverseWithKey+  :: forall s f k a b. Applicative f+  => (Key s k -> a -> f b) -> Map s k a -> f (Map s k b)+traverseWithKey f (Map m) = Map <$> Map.traverseWithKey (f . unsafeKey) m++-- | Thread an accumularing argument through the map in ascending order of keys.+mapAccumLWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> Map s k b+  -> (a, Map s k c)+mapAccumLWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.mapAccumWithKey @a @k @b @c++-- | Thread an accumularing argument through the map in descending order of+-- keys.+mapAccumRWithKey+  :: forall s k a b c. (a -> Key s k -> b -> (a, c))+  -> a+  -> Map s k b+  -> (a, Map s k c)+mapAccumRWithKey = gcoerceWith (unsafeCastKey @s @k) $ coerce+  $ Map.mapAccumRWithKey @a @k @b @c++-- | @'mapKeysWith' c f m@ applies @f@ to each key of @m@ and collects the+-- results into a new map. For keys that were mapped to the same new key, @c@+-- acts as the combining function for corresponding values.+mapKeysWith+  :: forall s k1 k2 a. Ord k2+  => (a -> a -> a)+  -> (Key s k1 -> k2)+  -> Map s k1 a+  -> SomeMapWith (MapProof 'Regular s k1 k2) k2 a+mapKeysWith f g (Map m)+  = SomeMapWith (Map $ Map.mapKeysWith f (g . unsafeKey) m)+    $ MapProof (unsafeKey . g) \k2 -> case Map.lookup (unrefine k2) backMap of+      Nothing -> error+        "mapKeysWith: bug: Data.Map.Strict.Refined has been subverted"+      Just k1 -> k1+  where+    ~backMap = Map.fromList+      [ (k2, unsafeKey k1)+      | k1 <- Map.keys m+      , let !k2 = g $ unsafeKey k1+      ]++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect only the 'Just' results, returning a potentially smaller+-- map.+mapMaybeWithKey+  :: forall s k a b. (Key s k -> a -> Maybe b)+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k b+mapMaybeWithKey f (Map m)+  = SomeMapWith (Map $ Map.mapMaybeWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Apply a function to all values in a map, together with their corresponding+-- keys, and collect the 'Left' and 'Right' results into separate (disjoint)+-- maps.+mapEitherWithKey+  :: forall s k a b c. Ord k -- TODO: this is only used in the proof+  => (Key s k -> a -> Either b c)+  -> Map s k a+  -> Some2MapWith (PartitionProof 'Regular s k) k b c+mapEitherWithKey p (Map m) = case Map.mapEitherWithKey (p . unsafeKey) m of+  (m1, m2) -> Some2MapWith (Map m1) (Map m2) $ PartitionProof+    do \k -> case Map.lookup (unrefine k) m of+        Nothing -> error+          "mapEitherWithKey: bug: Data.Map.Strict.Refined has been subverted"+        Just x -> case p k x of+          Left _ -> Left $ unsafeKey $ unrefine k+          Right _ -> Right $ unsafeKey $ unrefine k+    unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Update or delete the value at the smallest key, returning a potentially+-- smaller map.+updateMinWithKey+  :: forall s k a. (Key s k -> a -> Maybe a)+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k a+updateMinWithKey f (Map m)+  = SomeMapWith (Map $ Map.updateMinWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Update or delete the value at the largest key, returning a potentially+-- smaller map.+updateMaxWithKey+  :: forall s k a. (Key s k -> a -> Maybe a)+  -> Map s k a+  -> SomeMapWith (SupersetProof 'Regular s) k a+updateMaxWithKey f (Map m)+  = SomeMapWith (Map $ Map.updateMaxWithKey (f . unsafeKey) m)+    $ SupersetProof unsafeSubset++-- | Adjust the value at the smallest key. The set of keys remains unchanged.+adjustMinWithKey :: forall s k a. (Key s k -> a -> a) -> Map s k a -> Map s k a+adjustMinWithKey f (Map m)+  = Map $ Map.updateMinWithKey ((Just .) . f . unsafeKey) m++-- | Adjust the value at the greatest key. The set of keys remains unchanged.+adjustMaxWithKey :: forall s k a. (Key s k -> a -> a) -> Map s k a -> Map s k a+adjustMaxWithKey f (Map m)+  = Map $ Map.updateMaxWithKey ((Just .) . f . unsafeKey) m++-- | @'bind' m f@ is a map that for each key @k :: 'Key' s k@, contains the+-- value @f (m '!' k) '!' k@, similar to @'>>='@ for functions.+bind :: forall s k a b. Ord k => Map s k a -> (a -> Map s k b) -> Map s k b+bind m f = mapWithKey (\k x -> f x ! k) m++-- | Apply the inverse image of the given function to the keys of the given map,+-- so that for all @k :: 'Key' s2 k2@,+-- @'backpermuteKeys' f m '!' k = m '!' f k@.+--+-- If maps are identified with functions, this computes the composition.+backpermuteKeys+  :: forall s1 s2 k1 k2 a. (Ord k1, KnownSet s2 k2)+  => (Key s2 k2 -> Key s1 k1) -> Map s1 k1 a -> Map s2 k2 a+backpermuteKeys f m = fromSet \k -> m ! f k
+ src/Data/Set/Refined.hs view
@@ -0,0 +1,556 @@+{-# LANGUAGE CPP #-}+-- | This module implements a way of tracking the contents of a 'Data.Set.Set'+-- at the type level, and provides utilities for manipulating such sets.+--+-- The contents of a set are associated with a type parameter, e.g. @s@, so that+-- whenever you see the same type parameter, you know you are working with the+-- same set. The type @s@ itself has no internal structure, rather it is merely+-- a skolem type variable (rank-2 polymorphism 'Control.Monad.ST.runST' trick)+-- introduced by "Data.Reflection".+--+-- = Warning+-- This module together with "Data.Set" rely on 'Eq' and 'Ord' instances being+-- lawful: that '==' is an equivalence relation, and that the 'Ord' operations+-- define a total order on the quotient defined by this equivalence relation; at+-- least for the subset of values that are actually encountered at runtime. If+-- this assumption is violated, this module may not be able to uphold its+-- invariants and may throw errors. In particular beware of NaN in 'Float' and+-- 'Double'.+module Data.Set.Refined+  (+  -- * Set type+    KnownSet+  , Set+  -- * Refinement type+  , InSet(..)+  , Flavor(Regular)+  , Element+  , revealPredicate+  -- * Existentials and common proofs+  , SomeSet(..)+  , withSet+  , SomeSetWith(..)+  , withSetWith+  , Some2SetWith(..)+  , with2SetWith+  , (:->)+  , SupersetProof(..)+  , EmptyProof(..)+  -- * Construction+  , empty+  , singleton+  , SingletonProof(..)+  , fromSet+  , fromTraversable+  , FromTraversableProof(..)+  -- * Insertion+  , insert+  , InsertProof(..)+  -- * Deletion+  , delete+  -- * Query+  , member+  , lookupLT+  , lookupGT+  , lookupLE+  , lookupGE+  , null+  , isSubsetOf+  , SubsetProof(..)+  , disjoint+  , DisjointProof(..)+  -- * Combine+  , union+  , UnionProof(..)+  , difference+  , DifferenceProof(..)+  , intersection+  , IntersectionProof(..)+  , cartesianProduct+  , ProductProof(..)+  , disjointUnion+  , CoproductProof(..)+  -- * Filter+  , filter+  , partition+  , PartitionProof(..)+  , spanAntitone+  , PartialPartitionProof(..)+  , splitMember+  , SplitProof(..)+  -- * Map+  , map+  , MapProof(..)+  -- * Folds+  , foldMap+  , foldr+  , foldl+  , foldr'+  , foldl'+  -- * Min/Max+  , minView+  , maxView+  -- * Conversion+  , toList+  , toDescList+  , asIntSet+  , asHashSet+  -- * Casts+  , castElement+  , cast+  , castFlavor+  ) where++import           Data.Coerce+import           Data.Constraint (Dict(..))+import           Data.Container.Refined.Conversion+import           Data.Container.Refined.Hashable+import           Data.Container.Refined.Proofs+import           Data.Container.Refined.Unsafe+import qualified Data.Foldable as Foldable+import qualified Data.Map as Map+import           Data.Proxy+import           Data.Reflection+import qualified Data.Set as Set+import           Data.Traversable+import           Data.Type.Coercion+import           Data.Type.Equality ((:~:)(..))+import           Data.Typeable (Typeable)+import           GHC.Exts (Proxy#, proxy#)+import           Prelude hiding (filter, foldl, foldMap, foldr, map, null)+import           Refined+import           Refined.Unsafe+import           Unsafe.Coerce++#if MIN_VERSION_containers(0, 5, 8)+#else+import qualified Data.List as List+#endif+++-- | To use "Refined" machinery that uses the 'Predicate' typeclass you will+-- need to pattern match on this 'Dict'.+--+-- The reason is that in the default /fast/ implementation of reflection, we+-- don't have @'Typeable' s@, which "Refined" wants for pretty-printing+-- exceptions. We /can/ provide @'Typeable' s@, but at the cost of using the+-- /slow/ implementation of reflection.+revealPredicate+  :: forall s a. (Typeable a, Ord a, KnownSet s a)+  => Dict (Predicate (InSet 'Regular s) a)+revealPredicate = reifyTypeable (reflect (Proxy @s))+  \(_ :: Proxy s') ->+    reflect (Proxy @s') `seq`+    --  ^ Work around https://github.com/ekmett/reflection/issues/54+      case unsafeCoerce Refl :: s :~: s' of+        Refl -> Dict++-- | @'Element' s a@ is a value of type @a@ that has been verified to be an+-- element of @s@.+--+-- Thus, @'Element' s a@ is a \"refinement\" type of @a@, and this library+-- integrates with an implementation of refimenement types in "Refined", so+-- the machinery from there can be used to manipulate 'Element's (however see+-- 'revealPredicate').+--+-- The underlying @a@ value can be obtained with 'unrefine'. An @a@ can be+-- validated into an @'Element' s a@ with 'member'.+type Element s = Refined (InSet 'Regular s)++unsafeCastElement :: forall s a. Coercion a (Element s a)+unsafeCastElement = reallyUnsafeUnderlyingRefined++unsafeElement :: a -> Element s a+unsafeElement = coerceWith unsafeCastElement++-- | An existential wrapper for an as-yet-unknown set. Pattern maching on it+-- gives you a way to refer to the set, e.g.+--+-- @+-- case 'fromSet' ... of+--   'SomeSet' \@s _ -> doSomethingWith \@s+--+-- case 'fromSet' ... of+--   'SomeSet' (_ :: 'Proxy#' s) -> doSomethingWith \@s+-- @+data SomeSet a where+  SomeSet :: forall s a. KnownSet s a => Proxy# s -> SomeSet a++-- | Apply an unknown set to a continuation that can accept any set. This gives+-- you a way to refer to the set (the parameter @s@), e.g.:+--+-- @+-- 'withSet' ('fromSet' ...) $ \(_ :: 'Proxy' s) -> doSomethingWith \@s+-- @+withSet+  :: forall a r. SomeSet a -> (forall s. KnownSet s a => Proxy s -> r) -> r+withSet (SomeSet (_ :: Proxy# s)) k = k $ Proxy @s++-- | Construct a set from a regular 'Data.Set.Set'.+fromSet :: forall a. Set.Set a -> SomeSet a+fromSet s = reify s \(_ :: Proxy s) -> SomeSet @s proxy#++-- | An existential wrapper for an as-yet-unknown set, together with a proof of+-- some fact @p@ about the set. Pattern matching on it gives you a way to refer+-- to the set (the parameter @s@). Most functions will return a set in this way,+-- together with a proof that somehow relates the set to the function's inputs.+data SomeSetWith p a where+  SomeSetWith :: forall s a p. KnownSet s a => !(p s) -> SomeSetWith p a++-- | Apply an unknown set with proof to a continuation that can accept any set+-- satisfying the proof. This gives you a way to refer to the set (the parameter+-- @s@).+withSetWith+  :: forall a r p. SomeSetWith p a -> (forall s. KnownSet s a => p s -> r) -> r+withSetWith (SomeSetWith p) k = k p++-- | An existential wrapper for an as-yet-unknown pair of sets, together with+-- a proof of some fact @p@ relating them.+data Some2SetWith p a where+  Some2SetWith+    :: forall s t a p. (KnownSet s a, KnownSet t a)+    => !(p s t) -> Some2SetWith p a++-- | Apply a pair of unknown sets with proof to a continuation that can accept+-- any pair of sets satisfying the proof. This gives you a way to refer to the+-- sets (the parameters @s@ and @t@).+with2SetWith+  :: forall a r p. Some2SetWith p a+  -> (forall s t. (KnownSet s a, KnownSet t a) => p s t -> r)+  -> r+with2SetWith (Some2SetWith p) k = k p++-- | An empty set.+empty :: forall a. SomeSetWith (EmptyProof 'Regular) a+empty = reify Set.empty \(_ :: Proxy r)+  -> SomeSetWith @r $ EmptyProof unsafeSubset++-- | Create a set with a single element.+singleton :: forall a. a -> SomeSetWith (SingletonProof 'Regular a) a+singleton x = reify (Set.singleton x) \(_ :: Proxy r)+  -> SomeSetWith @r $ SingletonProof $ unsafeElement x++-- | Create a set from the elements of an arbitrary traversable.+fromTraversable+  :: forall t a. (Traversable t, Ord a)+  => t a -> SomeSetWith (FromTraversableProof 'Regular t a) a+fromTraversable xs = reify set \(_ :: Proxy r)+  -> SomeSetWith @r $ FromTraversableProof+    $ unsafeCoerce @(t (Element _ a)) @(t (Element r a)) proof+  where+    (set, proof) = mapAccumL+      (\s x -> let !s' = Set.insert x s in (s', unsafeElement x))+      Set.empty+      xs++-- | Insert an element in a set.+insert+  :: forall s a. (Ord a, KnownSet s a)+  => a -> SomeSetWith (InsertProof 'Regular a s) a+insert x = reify (Set.insert x $ reflect $ Proxy @s) \(_ :: Proxy r)+  -> SomeSetWith @r $ InsertProof (unsafeElement x) unsafeSubset++-- | Delete an element from a set.+delete+  :: forall s a. (Ord a, KnownSet s a)+  => a -> SomeSetWith (SupersetProof 'Regular s) a+delete x = reify (Set.delete x $ reflect $ Proxy @s) \(_ :: Proxy r)+  -> SomeSetWith @s $ SupersetProof unsafeSubset++-- | If an element is in the set, return the proof that it is.+member :: forall s a. (Ord a, KnownSet s a) => a -> Maybe (Element s a)+member x+  | x `Set.member` reflect (Proxy @s) = Just $ unsafeElement x+  | otherwise = Nothing++-- | Find the largest element smaller than the given one.+lookupLT :: forall s a. (Ord a, KnownSet s a) => a -> Maybe (Element s a)+lookupLT x = gcoerceWith (unsafeCastElement @s @a) $ coerce+  $ Set.lookupLT x (reflect $ Proxy @s)++-- | Find the smallest element greater than the given one.+lookupGT :: forall s a. (Ord a, KnownSet s a) => a -> Maybe (Element s a)+lookupGT x = gcoerceWith (unsafeCastElement @s @a) $ coerce+  $ Set.lookupGT x (reflect $ Proxy @s)++-- | Find the largest element smaller or equal to the given one.+lookupLE :: forall s a. (Ord a, KnownSet s a) => a -> Maybe (Element s a)+lookupLE x = gcoerceWith (unsafeCastElement @s @a) $ coerce+  $ Set.lookupLE x (reflect $ Proxy @s)++-- | Find the smallest element greater or equal to the given one.+lookupGE :: forall s a. (Ord a, KnownSet s a) => a -> Maybe (Element s a)+lookupGE x = gcoerceWith (unsafeCastElement @s @a) $ coerce+  $ Set.lookupGE x (reflect $ Proxy @s)++-- | If the set is empty, return the proof that it is.+null :: forall s a. KnownSet s a => Maybe (EmptyProof 'Regular s)+null+  | Set.null $ reflect $ Proxy @s = Just $ EmptyProof unsafeSubset+  | otherwise = Nothing++-- | If @s@ is a subset of @t@ (or is equal to), return a proof of that.+isSubsetOf+  :: forall s t a. (Ord a, KnownSet s a, KnownSet t a)+  => Maybe (SubsetProof 'Regular s t)+isSubsetOf+  | reflect (Proxy @s) `Set.isSubsetOf` reflect (Proxy @t)+  = Just $ SubsetProof unsafeSubset+  | otherwise = Nothing++-- | If @s@ and @t@ are disjoint (i.e. their intersection is empty), return a+-- proof of that.+disjoint+  :: forall s t a. (Ord a, KnownSet s a, KnownSet t a)+  => Maybe (DisjointProof 'Regular s t)+disjoint+#if MIN_VERSION_containers(0, 5, 11)+  | Set.disjoint (reflect $ Proxy @s) (reflect $ Proxy @t)+#else+  | Set.null $ Set.intersection (reflect $ Proxy @s) (reflect $ Proxy @t)+#endif+  = Just $ DisjointProof \f g -> unsafeSubsetWith2 f g+  | otherwise = Nothing++-- | The union of two sets.+union+  :: forall s t a. (Ord a, KnownSet s a, KnownSet t a)+  => SomeSetWith (UnionProof 'Regular s t) a+union = reify (reflect (Proxy @s) `Set.union` reflect (Proxy @t))+  \(_ :: Proxy r) -> SomeSetWith @r $ UnionProof unsafeSubset unsafeSubsetWith2++-- unions :: ?++-- | Set with elements of @s@ that are not in @t@.+difference+  :: forall s t a. (Ord a, KnownSet s a, KnownSet t a)+  => SomeSetWith (DifferenceProof 'Regular s t) a+difference = reify (reflect (Proxy @s) `Set.difference` reflect (Proxy @t))+  \(_ :: Proxy r) -> SomeSetWith @r+    $ DifferenceProof unsafeSubset (\f g -> unsafeSubsetWith2 f g) unsafeSubset++-- | Intersection of two sets.+intersection+  :: forall s t a. (Ord a, KnownSet s a, KnownSet t a)+  => SomeSetWith (IntersectionProof 'Regular s t) a+intersection = reify (reflect (Proxy @s) `Set.intersection` reflect (Proxy @t))+  \(_ :: Proxy r) -> SomeSetWith @r+    $ IntersectionProof unsafeSubset unsafeSubsetWith2++-- | Cartesian product of two sets. The elements are all pairs @(x, y)@ for each+-- @x@ from @s@ and for each @y@ from @t@.+cartesianProduct+  :: forall s t a b. (KnownSet s a, KnownSet t b)+  => SomeSetWith (ProductProof 'Regular s t) (a, b)+cartesianProduct = reify+#if MIN_VERSION_containers(0, 5, 11)+  (reflect (Proxy @s) `Set.cartesianProduct` reflect (Proxy @t))+#else+  (Set.fromDistinctAscList $ (,) <$> Set.toAscList (reflect $ Proxy @s)+    <*> Set.toAscList (reflect $ Proxy @t))+#endif+  \(_ :: Proxy r) -> SomeSetWith @r $ ProductProof let+      proof :: forall x y. Coercion+        (Refined (InSet 'Regular s) x, Refined (InSet 'Regular t) y)+        (Refined (InSet 'Regular r) (x, y))+      !proof+        | Coercion <- reallyUnsafeUnderlyingRefined @x @(InSet 'Regular s)+        , Coercion <- reallyUnsafeUnderlyingRefined @y @(InSet 'Regular t)+        = Coercion `trans`+          reallyUnsafeUnderlyingRefined @(x, y) @(InSet 'Regular r)+    in proof++-- | Disjoint union of two sets. Includes @'Left' x@ for each @x@ from @s@, and+-- @'Right' y@ for each @y@ from @t@.+disjointUnion+  :: forall s t a b. (KnownSet s a, KnownSet t b)+  => SomeSetWith (CoproductProof 'Regular s t) (Either a b)+disjointUnion = reify+#if MIN_VERSION_containers(0, 5, 11)+  (reflect (Proxy @s) `Set.disjointUnion` reflect (Proxy @t))+#else+  (Set.fromDistinctAscList $ (Left <$> Set.toAscList (reflect $ Proxy @s))+    ++ (Right <$> Set.toAscList (reflect $ Proxy @t)))+#endif+  \(_ :: Proxy r) -> SomeSetWith @r $ CoproductProof let+      proof :: forall x y. Coercion+        (Either (Refined (InSet 'Regular s) x) (Refined (InSet 'Regular t) y))+        (Refined (InSet 'Regular r) (Either x y))+      !proof+        | Coercion <- reallyUnsafeUnderlyingRefined @x @(InSet 'Regular s)+        , Coercion <- reallyUnsafeUnderlyingRefined @y @(InSet 'Regular t)+        = Coercion `trans`+          reallyUnsafeUnderlyingRefined @(Either x y) @(InSet 'Regular r)+    in proof++-- | Return a subset of elements that satisfy the given predicate.+filter+  :: forall s a. KnownSet s a+  => (Element s a -> Bool) -> SomeSetWith (SupersetProof 'Regular s) a+filter p = reify (Set.filter (p .  unsafeElement) $ reflect $ Proxy @s)+  \(_ :: Proxy r) -> SomeSetWith @r $ SupersetProof unsafeSubset++-- | Partition a set into two disjoint subsets: those that satisfy the+-- predicate, and those that don't.+partition+  :: forall s a. KnownSet s a+  => (Element s a -> Bool) -> Some2SetWith (PartitionProof 'Regular s a) a+partition p = case Set.partition (p . unsafeElement) $ reflect $ Proxy @s of+  (r, q) -> reify r \(_ :: Proxy r) -> reify q \(_ :: Proxy q)+    -> Some2SetWith @s @r $ PartitionProof+      do \x -> if p x+          then Left $ unsafeElement $ unrefine x+          else Right $ unsafeElement $ unrefine x+      unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Divide a set into two disjoint subsets at a point where the predicate stops+-- holding.+--+-- If @p@ is antitone ( \(\forall x y, x < y \implies p(x) \ge p(y)\) ), then+-- this point is uniquely defined. If @p@ is not antitone, a splitting point is+-- chosen in an unspecified way.+spanAntitone+  :: forall s a. KnownSet s a+  => (Element s a -> Bool) -> Some2SetWith (PartialPartitionProof 'Regular s) a+spanAntitone p =+#if MIN_VERSION_containers(0, 5, 8)+  case Set.spanAntitone (p . unsafeElement) $ reflect $ Proxy @s of+    (r, q)+#else+  case List.span (p . unsafeElement)+    $ Set.toAscList $ reflect $ Proxy @s of+    (rs, qs)+      | let r = Set.fromDistinctAscList rs+      , let q = Set.fromDistinctAscList qs+#endif+      -> reify r \(_ :: Proxy r) -> reify q \(_ :: Proxy q)+        -> Some2SetWith @r @q $ PartialPartitionProof+          unsafeSubset unsafeSubsetWith2 \f g -> unsafeSubsetWith2 f g++-- | Return two disjoint subsets: those less than the given element, and those+-- greater than the given element; along with the proof that the given element+-- was in the set, if it was.+splitMember+  :: forall s a. (Ord a, KnownSet s a)+  => a -> Some2SetWith (SplitProof 'Regular s (Element s a)) a+splitMember x = case Set.splitMember x $ reflect $ Proxy @s of+  (r, m, q) -> reify r \(_ :: Proxy r) -> reify q \(_ :: Proxy q)+    -> Some2SetWith @r @q $ SplitProof+      (if m then Just (unsafeElement x) else Nothing)+      unsafeSubset \f g -> unsafeSubsetWith2 f g++-- | Apply the given function to each element of the set and collect the+-- results. Note that the resulting set can be smaller.+map+  :: forall s a b. (Ord b, KnownSet s a)+  => (Element s a -> b) -> SomeSetWith (MapProof 'Regular s a b) b+map f = reify (Map.keysSet m) \(_ :: Proxy r) -> SomeSetWith @r+  $ MapProof (unsafeElement . f) \y -> case Map.lookup (unrefine y) m of+    Nothing -> error "map: bug: Data.Set.Refined has been subverted"+    Just x -> x+  where+    !m = Map.fromList+      [ (y, unsafeElement x)+      | x <- Set.toList $ reflect $ Proxy @s+      , let !y = f $ unsafeElement x+      ]++-- | Map each element of @s@ into a monoid (with proof that it was an element),+-- and combine the results using 'Data.Monoid.<>'.+foldMap :: forall s a m. (KnownSet s a, Monoid m) => (Element s a -> m) -> m+foldMap f = Foldable.foldMap (f . unsafeElement) $ reflect $ Proxy @s++-- | Right associative fold with a lazy accumulator.+foldr :: forall s a b. KnownSet s a => (Element s a -> b -> b) -> b -> b+foldr f z = Set.foldr (f . unsafeElement) z $ reflect $ Proxy @s++-- | Left associative fold with a lazy accumulator.+foldl :: forall s a b. KnownSet s a => (b -> Element s a -> b) -> b -> b+foldl f z = Set.foldl ((. unsafeElement) . f) z $ reflect $ Proxy @s++-- | Right associative fold with a strict accumulator.+foldr' :: forall s a b. KnownSet s a => (Element s a -> b -> b) -> b -> b+foldr' f z = Set.foldr' (f . unsafeElement) z $ reflect $ Proxy @s++-- | Left associative fold with a strict accumulator.+foldl' :: forall s a b. KnownSet s a => (b -> Element s a -> b) -> b -> b+foldl' f z = Set.foldl' ((. unsafeElement) . f) z $ reflect $ Proxy @s++-- | Retrieves the smallest element of the set, and the set with that element+-- removed; or a proof that the set was empty.+minView+  :: forall s a. KnownSet s a+  => Either+    (EmptyProof 'Regular s)+    (Element s a, SomeSetWith (SupersetProof 'Regular s) a)+minView = case Set.minView $ reflect $ Proxy @s of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (x, xs) -> Right $ (unsafeElement x,) $ reify xs \(_ :: Proxy r)+    -> SomeSetWith @r $ SupersetProof unsafeSubset++-- | Retrieves the greatest element of the set, and the set with that element+-- removed; or a proof that the set was empty.+maxView+  :: forall s a. KnownSet s a+  => Either+    (EmptyProof 'Regular s)+    (Element s a, SomeSetWith (SupersetProof 'Regular s) a)+maxView = case Set.maxView $ reflect $ Proxy @s of+  Nothing -> Left $ EmptyProof unsafeSubset+  Just (x, xs) -> Right $ (unsafeElement x,) $ reify xs \(_ :: Proxy r)+    -> SomeSetWith @r $ SupersetProof unsafeSubset++-- | List of elements in the set in ascending order.+toList :: forall s a. KnownSet s a => [Element s a]+toList = gcoerceWith (unsafeCastElement @s @a) $ coerce+  $ Set.toAscList $ reflect $ Proxy @s++-- | List of elements in the set in descending order.+toDescList :: forall s a. KnownSet s a => [Element s a]+toDescList = gcoerceWith (unsafeCastElement @s @a) $ coerce+  $ Set.toDescList $ reflect $ Proxy @s++-- | Convert a 'Set' into an 'IntSet', retaining its set of elements, which can+-- be converted with 'castFlavor'.+asIntSet :: forall s. KnownSet s Int => IntSet s+asIntSet = set2IntSet++-- | Convert a 'Set' into a 'HashSet', retaining its set of elements, which can+-- be converted with 'castFlavor'.+asHashSet :: forall s a. (Hashable a, KnownSet s a) => HashSet s a+asHashSet = set2HashSet++-- | If elements of @s@ can be weakened to elements of @t@ and vice versa, then+-- @s@ and @t@ actually stand for the same set and @'Element' s@ can be safely+-- interconverted with @'Element' t@.+--+-- The requirement that the weakenings are natural transformations ensures that+-- they don't actually alter the elements. To build these you can compose+-- ':->''s from proofs returned by functions in this module, or "Refined"+-- functions like 'andLeft' or 'leftOr'.+castElement+  :: forall s t a. (forall x. Element s x -> Element t x)+  -> (forall x. Element t x -> Element s x)+  -> Coercion (Element s a) (Element t a)+castElement = castRefined++-- | If elements can be interconverted (e.g. as proved by 'castElement'), then+-- the sets can be interconverted too. For example we can establish that the+-- intersection of a set with itself is interconvertible with that set:+--+-- @+-- castIntersection+--   :: t'IntersectionProof' ''Regular' s s r+--   -> 'Coercion' ('Set' r a) ('Set' s a)+-- castIntersection ( v'IntersectionProof' p1 p2)+--   = 'cast' $ 'castElement' ('andLeft' . p1) (p2 'id' 'id')+-- @+cast+  :: forall s t a. (forall x. Coercion (Element s x) (Element t x))+  -> Coercion (Set s a) (Set t a)+cast Coercion+#if MIN_VERSION_base(4, 15, 0)+  = case unsafeEqualityProof @s @t of UnsafeRefl -> Coercion+#else+  = repr $ unsafeCoerce Refl+#endif