diff --git a/ChangeLog.md b/ChangeLog.md index 184a636d..983479ac 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,9 @@ * Add `Reflex.Query.Base.mapQueryT`. See that module for documentation +* The `Reflex.Patch.*` modules were moved to the `patch` library. + They are `Data.Patch.*` there, but reexported under their old names for backwards compatability here. + ## 0.6.3 * `Data.WeakBag.traverse` and `Data.FastWeakBag.traverse` have been deprecated. diff --git a/cabal.project.freeze b/cabal.project.freeze index 0bcc1d6f..db06f8ef 100644 --- a/cabal.project.freeze +++ b/cabal.project.freeze @@ -1 +1,2 @@ constraints: any.text < 1.2.4.0 + , hlint < 2.2.6 || > 2.2.6 diff --git a/dep/reflex-platform/github.json b/dep/reflex-platform/github.json index 1cd7cdcc..66cd618f 100644 --- a/dep/reflex-platform/github.json +++ b/dep/reflex-platform/github.json @@ -1,7 +1,7 @@ { "owner": "reflex-frp", "repo": "reflex-platform", - "branch": "master", - "rev": "510b990d0b11f0626afbec5fe8575b5b2395391b", - "sha256": "09cmahsbxr0963wq171c7j139iyzz49hramr4v9nsf684wcwkngv" + "branch": "develop", + "rev": "f628398d076243a0851b27e625b37f65dff9b89b", + "sha256": "0sl0hf1glgyb1vmf2mhw4r9ipmcqk1y19d3wsic7dix2jwywzrh9" } diff --git a/reflex.cabal b/reflex.cabal index 4142dfc8..8a2171e1 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -9,7 +9,7 @@ Maintainer: ryan.trinkle@gmail.com Stability: Experimental Category: FRP Build-type: Simple -Cabal-version: >=1.9.2 +Cabal-version: 1.22 homepage: https://reflex-frp.org bug-reports: https://github.com/reflex-frp/reflex/issues extra-source-files: @@ -47,6 +47,7 @@ flag split-these default: True library + default-language: Haskell2010 hs-source-dirs: src build-depends: MemoTrie == 0.6.*, @@ -61,6 +62,7 @@ library lens >= 4.7 && < 5, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, + patch >= 0.0 && < 0.1, prim-uniq >= 0.1.0.1 && < 0.2, primitive >= 0.5 && < 0.8, profunctors >= 5.3 && < 5.6, @@ -88,7 +90,6 @@ library Data.AppendMap, Data.FastMutableIntMap, Data.FastWeakBag, - Data.Functor.Misc, Data.Map.Misc, Data.WeakBag, Reflex, @@ -110,13 +111,6 @@ library Reflex.Host.Class, Reflex.Network, Reflex.NotReady.Class, - Reflex.Patch, - Reflex.Patch.Class, - Reflex.Patch.DMap, - Reflex.Patch.DMapWithMove, - Reflex.Patch.IntMap, - Reflex.Patch.Map, - Reflex.Patch.MapWithMove, Reflex.PerformEvent.Base, Reflex.PerformEvent.Class, Reflex.PostBuild.Base, @@ -135,6 +129,16 @@ library Reflex.Widget.Basic, Reflex.Workflow + reexported-modules: + patch:Data.Functor.Misc, + patch:Data.Patch as Reflex.Patch, + patch:Data.Patch.Class as Reflex.Patch.Class, + patch:Data.Patch.DMap as Reflex.Patch.DMap, + patch:Data.Patch.DMapWithMove as Reflex.Patch.DMapWithMove, + patch:Data.Patch.IntMap as Reflex.Patch.IntMap, + patch:Data.Patch.Map as Reflex.Patch.Map, + patch:Data.Patch.MapWithMove as Reflex.Patch.MapWithMove + ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs -funbox-strict-fields -O2 -fspecialise-aggressively if flag(debug-trace-events) @@ -167,6 +171,7 @@ library build-depends: ghcjs-base test-suite semantics + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: semantics.hs hs-source-dirs: test @@ -192,6 +197,7 @@ test-suite semantics Reflex.TestPlan test-suite CrossImpl + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: Reflex/Test/CrossImpl.hs hs-source-dirs: test @@ -213,6 +219,7 @@ test-suite CrossImpl Reflex.Plan.Pure test-suite hlint + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: hlint.hs hs-source-dirs: test @@ -225,6 +232,7 @@ test-suite hlint buildable: False test-suite EventWriterT + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: EventWriterT.hs hs-source-dirs: test @@ -252,6 +260,7 @@ test-suite EventWriterT test-suite RequesterT + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: RequesterT.hs hs-source-dirs: test @@ -276,6 +285,7 @@ test-suite RequesterT Test.Run test-suite QueryT + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: QueryT.hs hs-source-dirs: test @@ -287,6 +297,7 @@ test-suite QueryT , lens , monoidal-containers , mtl + , patch , ref-tf , reflex , these @@ -302,6 +313,7 @@ test-suite QueryT Reflex.Plan.Pure test-suite GC-Semantics + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: GC.hs hs-source-dirs: test @@ -311,6 +323,7 @@ test-suite GC-Semantics , dependent-map , deepseq , mtl + , patch , these , transformers , reflex @@ -326,6 +339,7 @@ test-suite GC-Semantics Test.Run test-suite rootCleanup + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: rootCleanup.hs hs-source-dirs: test @@ -343,6 +357,7 @@ test-suite rootCleanup Test.Run benchmark spider-bench + default-language: Haskell2010 type: exitcode-stdio-1.0 hs-source-dirs: bench test main-is: Main.hs @@ -367,6 +382,7 @@ benchmark spider-bench Reflex.Bench.Focused benchmark saulzar-bench + default-language: Haskell2010 type: exitcode-stdio-1.0 hs-source-dirs: bench test c-sources: bench-cbits/checkCapability.c diff --git a/src/Data/AppendMap.hs b/src/Data/AppendMap.hs index 3b3bbb32..4bfd8e0c 100644 --- a/src/Data/AppendMap.hs +++ b/src/Data/AppendMap.hs @@ -71,7 +71,7 @@ mapMaybeNoNull f as = then Nothing else Just bs --- TODO: Move instances to `Reflex.Patch` +-- TODO: Move instances to `Data.Patch` -- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details. showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String showTree = coerce (Map.showTree :: Map k a -> String) diff --git a/src/Data/FastMutableIntMap.hs b/src/Data/FastMutableIntMap.hs index 15ce190c..94097293 100644 --- a/src/Data/FastMutableIntMap.hs +++ b/src/Data/FastMutableIntMap.hs @@ -34,8 +34,8 @@ import Data.Foldable (traverse_) import Data.IntMap.Strict (IntMap) import qualified Data.IntMap.Strict as IntMap import Data.IORef -import Reflex.Patch.Class -import Reflex.Patch.IntMap +import Data.Patch.Class +import Data.Patch.IntMap -- | A 'FastMutableIntMap' holds a map of values of type @a@ and allows low-overhead modifications via IO. -- Operations on 'FastMutableIntMap' run in IO. @@ -84,7 +84,7 @@ getFrozenAndClear (FastMutableIntMap r) = do writeIORef r IntMap.empty return result --- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Reflex.Patch.IntMap'), +-- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Data.Patch.IntMap'), -- and returns an 'IntMap' with the modified keys and values. applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a) applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do diff --git a/src/Data/Functor/Misc.hs b/src/Data/Functor/Misc.hs deleted file mode 100644 index 058704fb..00000000 --- a/src/Data/Functor/Misc.hs +++ /dev/null @@ -1,235 +0,0 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -#ifdef USE_REFLEX_OPTIMIZER -{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} -#endif --- | This module provides types and functions with no particular theme, but --- which are relevant to the use of 'Functor'-based datastructures like --- 'Data.Dependent.Map.DMap'. -module Data.Functor.Misc - ( -- * Const2 - Const2 (..) - , unConst2 - , dmapToMap - , dmapToIntMap - , dmapToMapWith - , mapToDMap - , weakenDMapWith - -- * WrapArg - , WrapArg (..) - -- * Convenience functions for DMap - , mapWithFunctorToDMap - , intMapWithFunctorToDMap - , mapKeyValuePairsMonotonic - , combineDMapsWithKey - , EitherTag (..) - , dmapToThese - , eitherToDSum - , dsumToEither - , ComposeMaybe (..) - ) where - -import Control.Applicative ((<$>)) -import Control.Monad.Identity -import Data.Dependent.Map (DMap) -import qualified Data.Dependent.Map as DMap -import Data.Dependent.Sum -import Data.GADT.Compare -import Data.GADT.Show -import Data.IntMap (IntMap) -import qualified Data.IntMap as IntMap -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Some (Some(Some)) -import Data.These -import Data.Type.Equality ((:~:)(Refl)) -import Data.Typeable hiding (Refl) - --------------------------------------------------------------------------------- --- Const2 --------------------------------------------------------------------------------- - --- | 'Const2' stores a value of a given type 'k' and ensures that a particular --- type 'v' is always given for the last type parameter -data Const2 :: * -> x -> x -> * where - Const2 :: k -> Const2 k v v - deriving (Typeable) - --- | Extract the value from a Const2 -unConst2 :: Const2 k v v' -> k -unConst2 (Const2 k) = k - -deriving instance Eq k => Eq (Const2 k v v') -deriving instance Ord k => Ord (Const2 k v v') -deriving instance Show k => Show (Const2 k v v') -deriving instance Read k => Read (Const2 k v v) - -instance Show k => GShow (Const2 k v) where - gshowsPrec n x@(Const2 _) = showsPrec n x - -instance Eq k => GEq (Const2 k v) where - geq (Const2 a) (Const2 b) = - if a == b - then Just Refl - else Nothing - -instance Ord k => GCompare (Const2 k v) where - gcompare (Const2 a) (Const2 b) = case compare a b of - LT -> GLT - EQ -> GEQ - GT -> GGT - --- | Convert a 'DMap' to a regular 'Map' -dmapToMap :: DMap (Const2 k v) Identity -> Map k v -dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList - --- | Convert a 'DMap' to an 'IntMap' -dmapToIntMap :: DMap (Const2 IntMap.Key v) Identity -> IntMap v -dmapToIntMap = IntMap.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList - --- | Convert a 'DMap' to a regular 'Map', applying the given function to remove --- the wrapping 'Functor' -dmapToMapWith :: (f v -> v') -> DMap (Const2 k v) f -> Map k v' -dmapToMapWith f = Map.fromDistinctAscList . map (\(Const2 k :=> v) -> (k, f v)) . DMap.toAscList - --- | Convert a regular 'Map' to a 'DMap' -mapToDMap :: Map k v -> DMap (Const2 k v) Identity -mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList - --- | Convert a regular 'Map', where the values are already wrapped in a functor, --- to a 'DMap' -mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f -mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList - --- | Convert a regular 'IntMap', where the values are already wrapped in a --- functor, to a 'DMap' -intMapWithFunctorToDMap :: IntMap (f v) -> DMap (Const2 IntMap.Key v) f -intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . IntMap.toAscList - --- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with --- the keys, using a function to remove the wrapping 'Functor' -weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v' -weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList - --------------------------------------------------------------------------------- --- WrapArg --------------------------------------------------------------------------------- - --- | 'WrapArg' can be used to tag a value in one functor with a type --- representing another functor. This was primarily used with dependent-map < --- 0.2, in which the value type was not wrapped in a separate functor. -data WrapArg :: (k -> *) -> (k -> *) -> * -> * where - WrapArg :: f a -> WrapArg g f (g a) - -deriving instance Eq (f a) => Eq (WrapArg g f (g' a)) -deriving instance Ord (f a) => Ord (WrapArg g f (g' a)) -deriving instance Show (f a) => Show (WrapArg g f (g' a)) -deriving instance Read (f a) => Read (WrapArg g f (g a)) - -instance GEq f => GEq (WrapArg g f) where - geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b - -instance GCompare f => GCompare (WrapArg g f) where - gcompare (WrapArg a) (WrapArg b) = case gcompare a b of - GLT -> GLT - GEQ -> GEQ - GGT -> GGT - --------------------------------------------------------------------------------- --- Convenience functions for DMap --------------------------------------------------------------------------------- - --- | Map over all key/value pairs in a 'DMap', potentially altering the key as --- well as the value. The provided function MUST preserve the ordering of the --- keys, or the resulting 'DMap' will be malformed. -mapKeyValuePairsMonotonic :: (DSum k v -> DSum k' v') -> DMap k v -> DMap k' v' -mapKeyValuePairsMonotonic f = DMap.fromDistinctAscList . map f . DMap.toAscList - -{-# INLINE combineDMapsWithKey #-} --- | Union two 'DMap's of different types, yielding another type. Each key that --- is present in either input map will be present in the output. -combineDMapsWithKey :: forall f g h i. - GCompare f - => (forall a. f a -> These (g a) (h a) -> i a) - -> DMap f g - -> DMap f h - -> DMap f i -combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh) - where go :: [DSum f g] -> [DSum f h] -> [DSum f i] - go [] hs = map (\(hk :=> hv) -> hk :=> f hk (That hv)) hs - go gs [] = map (\(gk :=> gv) -> gk :=> f gk (This gv)) gs - go gs@((gk :=> gv) : gs') hs@((hk :=> hv) : hs') = case gk `gcompare` hk of - GLT -> (gk :=> f gk (This gv)) : go gs' hs - GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs' - GGT -> (hk :=> f hk (That hv)) : go gs hs' - --- | Extract the values of a 'DMap' of 'EitherTag's. -dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b) -dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of - (Nothing, Nothing) -> Nothing - (Just (Identity a), Nothing) -> Just $ This a - (Nothing, Just (Identity b)) -> Just $ That b - (Just (Identity a), Just (Identity b)) -> Just $ These a b - --- | Tag type for 'Either' to use it as a 'DSum'. -data EitherTag l r a where - LeftTag :: EitherTag l r l - RightTag :: EitherTag l r r - deriving (Typeable) - -deriving instance Show (EitherTag l r a) -deriving instance Eq (EitherTag l r a) -deriving instance Ord (EitherTag l r a) - -instance GEq (EitherTag l r) where - geq a b = case (a, b) of - (LeftTag, LeftTag) -> Just Refl - (RightTag, RightTag) -> Just Refl - _ -> Nothing - -instance GCompare (EitherTag l r) where - gcompare a b = case (a, b) of - (LeftTag, LeftTag) -> GEQ - (LeftTag, RightTag) -> GLT - (RightTag, LeftTag) -> GGT - (RightTag, RightTag) -> GEQ - -instance GShow (EitherTag l r) where - gshowsPrec _ a = case a of - LeftTag -> showString "LeftTag" - RightTag -> showString "RightTag" - --- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'. -eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity -eitherToDSum = \case - Left a -> (LeftTag :=> Identity a) - Right b -> (RightTag :=> Identity b) - --- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'. -dsumToEither :: DSum (EitherTag a b) Identity -> Either a b -dsumToEither = \case - (LeftTag :=> Identity a) -> Left a - (RightTag :=> Identity b) -> Right b - --------------------------------------------------------------------------------- --- ComposeMaybe --------------------------------------------------------------------------------- - --- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would --- make the 'f' parameter have a nominal type role. We need f to be --- representational so that we can use safe 'coerce'. -newtype ComposeMaybe f a = - ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord) - -deriving instance Functor f => Functor (ComposeMaybe f) diff --git a/src/Data/Map/Misc.hs b/src/Data/Map/Misc.hs index f0d3e229..a41010da 100644 --- a/src/Data/Map/Misc.hs +++ b/src/Data/Map/Misc.hs @@ -43,7 +43,7 @@ diffMap olds news = flip Map.mapMaybe (align olds news) $ \case -- |Given a @'Map' k (Maybe v)@ representing keys to insert/update (@Just@) or delete (@Nothing@), produce a new map from the given input @'Map' k v@. -- --- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'. +-- See also 'Data.Patch.Map' and 'Data.Patch.MapWithMove'. applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v applyMap patch old = insertions `Map.union` (old `Map.difference` deletions) where (deletions, insertions) = Map.mapEither maybeToEither patch diff --git a/src/Reflex/Adjustable/Class.hs b/src/Reflex/Adjustable/Class.hs index 5d591a31..ab14122c 100644 --- a/src/Reflex/Adjustable/Class.hs +++ b/src/Reflex/Adjustable/Class.hs @@ -39,7 +39,7 @@ import qualified Data.IntMap.Strict as IntMap import Data.Map (Map) import Reflex.Class -import Reflex.Patch.DMapWithMove +import Data.Patch.DMapWithMove -- | A 'Monad' that supports adjustment over time. After an action has been -- run, if the given events fire, it will adjust itself so that its net effect diff --git a/src/Reflex/Class.hs b/src/Reflex/Class.hs index e8fbb720..a059d9d2 100644 --- a/src/Reflex/Class.hs +++ b/src/Reflex/Class.hs @@ -29,7 +29,7 @@ -- convenience functions for working with 'Event's, 'Behavior's, and other -- signals. module Reflex.Class - ( module Reflex.Patch + ( module Data.Patch -- * Primitives , Reflex (..) , mergeInt @@ -220,8 +220,8 @@ import Data.Witherable (Filterable(..)) import qualified Data.Witherable as W import Reflex.FunctorMaybe (FunctorMaybe) import qualified Reflex.FunctorMaybe -import Reflex.Patch -import qualified Reflex.Patch.MapWithMove as PatchMapWithMove +import Data.Patch +import qualified Data.Patch.MapWithMove as PatchMapWithMove import Debug.Trace (trace) diff --git a/src/Reflex/DynamicWriter/Base.hs b/src/Reflex/DynamicWriter/Base.hs index 643b9ccc..975caa89 100644 --- a/src/Reflex/DynamicWriter/Base.hs +++ b/src/Reflex/DynamicWriter/Base.hs @@ -44,7 +44,7 @@ import Reflex.Class import Reflex.DynamicWriter.Class import Reflex.EventWriter.Class (EventWriter, tellEvent) import Reflex.Host.Class -import qualified Reflex.Patch.MapWithMove as MapWithMove +import qualified Data.Patch.MapWithMove as MapWithMove import Reflex.PerformEvent.Class import Reflex.PostBuild.Class import Reflex.Query.Class diff --git a/src/Reflex/Patch.hs b/src/Reflex/Patch.hs deleted file mode 100644 index 5eb75329..00000000 --- a/src/Reflex/Patch.hs +++ /dev/null @@ -1,46 +0,0 @@ -{-# LANGUAGE TypeFamilies #-} --- | --- Module: --- Reflex.Patch --- Description: --- This module defines the 'Patch' class, which is used by Reflex to manage --- changes to 'Reflex.Class.Incremental' values. -module Reflex.Patch - ( module Reflex.Patch - , module X - ) where - -import Reflex.Patch.Class as X -import Reflex.Patch.DMap as X hiding (getDeletions) -import Reflex.Patch.DMapWithMove as X (PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove, - patchDMapWithMoveToPatchMapWithMoveWith, - traversePatchDMapWithMoveWithKey, unPatchDMapWithMove, - unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith) -import Reflex.Patch.IntMap as X hiding (getDeletions) -import Reflex.Patch.Map as X -import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements, - patchMapWithMoveNewElementsMap, unPatchMapWithMove, - unsafePatchMapWithMove) -import Data.Map.Monoidal (MonoidalMap) -import Data.Semigroup (Semigroup (..), (<>)) - --- | A 'Group' is a 'Monoid' where every element has an inverse. -class (Semigroup q, Monoid q) => Group q where - negateG :: q -> q - (~~) :: q -> q -> q - r ~~ s = r <> negateG s - --- | An 'Additive' 'Semigroup' is one where (<>) is commutative -class Semigroup q => Additive q where - --- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type. -newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p } - -instance Additive p => Patch (AdditivePatch p) where - type PatchTarget (AdditivePatch p) = p - apply (AdditivePatch p) q = Just $ p <> q - -instance (Ord k, Group q) => Group (MonoidalMap k q) where - negateG = fmap negateG - -instance (Ord k, Additive q) => Additive (MonoidalMap k q) diff --git a/src/Reflex/Patch/Class.hs b/src/Reflex/Patch/Class.hs deleted file mode 100644 index 3ba6d7de..00000000 --- a/src/Reflex/Patch/Class.hs +++ /dev/null @@ -1,34 +0,0 @@ -{-# LANGUAGE TypeFamilies #-} --- | The interface for types which represent changes made to other types -module Reflex.Patch.Class where - -import Control.Monad.Identity -import Data.Maybe -import Data.Semigroup (Semigroup(..)) - --- | A 'Patch' type represents a kind of change made to a datastructure. --- --- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey --- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@. -class Patch p where - type PatchTarget p :: * - -- | Apply the patch @p a@ to the value @a@. If no change is needed, return - -- 'Nothing'. - apply :: p -> PatchTarget p -> Maybe (PatchTarget p) - --- | Apply a 'Patch'; if it does nothing, return the original value -applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p -applyAlways p t = fromMaybe t $ apply p t - --- | 'Identity' can be used as a 'Patch' that always fully replaces the value -instance Patch (Identity a) where - type PatchTarget (Identity a) = a - apply (Identity a) _ = Just a - --- | Like '(.)', but composes functions that return patches rather than --- functions that return new values. The Semigroup instance for patches must --- apply patches right-to-left, like '(.)'. -composePatchFunctions :: (Patch p, Semigroup p) => (PatchTarget p -> p) -> (PatchTarget p -> p) -> PatchTarget p -> p -composePatchFunctions g f a = - let fp = f a - in g (applyAlways fp a) <> fp diff --git a/src/Reflex/Patch/DMap.hs b/src/Reflex/Patch/DMap.hs deleted file mode 100644 index 05319afd..00000000 --- a/src/Reflex/Patch/DMap.hs +++ /dev/null @@ -1,82 +0,0 @@ -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE Rank2Types #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeFamilies #-} --- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions. -module Reflex.Patch.DMap where - -import Reflex.Patch.Class -import Reflex.Patch.IntMap -import Reflex.Patch.Map - -import Data.Dependent.Map (DMap, DSum (..), GCompare (..)) -import qualified Data.Dependent.Map as DMap -import Data.Functor.Constant -import Data.Functor.Misc -import qualified Data.IntMap as IntMap -import qualified Data.Map as Map -import Data.Semigroup (Semigroup (..)) -import Data.Some (Some) - --- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted. --- Insertions are represented as @'ComposeMaybe' (Just value)@, --- while deletions are represented as @'ComposeMaybe' Nothing@. -newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) } - -deriving instance GCompare k => Semigroup (PatchDMap k v) - -deriving instance GCompare k => Monoid (PatchDMap k v) - --- | Apply the insertions or deletions to a given 'DMap'. -instance GCompare k => Patch (PatchDMap k v) where - type PatchTarget (PatchDMap k v) = DMap k v - apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust? - where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff - deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff - nothingToJust = \case - Nothing -> Just $ Constant () - Just _ -> Nothing - --- | Map a function @v a -> v' a@ over any inserts/updates in the given --- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@. -mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v' -mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p - --- | Map an effectful function @v a -> f (v' a)@ over any inserts/updates in the given --- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@. -traversePatchDMap :: Applicative f => (forall a. v a -> f (v' a)) -> PatchDMap k v -> f (PatchDMap k v') -traversePatchDMap f = traversePatchDMapWithKey $ const f - --- | Map an effectful function @k a -> v a -> f (v' a)@ over any inserts/updates --- in the given @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@. -traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v') -traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p - --- | Weaken a @'PatchDMap' k v@ to a @'PatchMap' (Some k) v'@ using a function --- @v a -> v'@ to weaken each value contained in the patch. -weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v' -weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p - --- | Convert a weak @'PatchDMap' ('Const2' k a) v@ where the @a@ is known by way of --- the @Const2@ into a @'PatchMap' k v'@ using a rank 1 function @v a -> v'@. -patchDMapToPatchMapWith :: (v a -> v') -> PatchDMap (Const2 k a) v -> PatchMap k v' -patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p - --- | Convert a @'PatchMap' k v@ into a @'PatchDMap' ('Const2' k a) v'@ using a function @v -> v' a@. -const2PatchDMapWith :: forall k v v' a. (v -> v' a) -> PatchMap k v -> PatchDMap (Const2 k a) v' -const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p - where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v') - g (k, e) = Const2 k :=> ComposeMaybe (f <$> e) - --- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@. -const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f -const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p - where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f) - g (k, e) = Const2 k :=> ComposeMaybe (f <$> e) - --- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'. -getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v' -getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p diff --git a/src/Reflex/Patch/DMapWithMove.hs b/src/Reflex/Patch/DMapWithMove.hs deleted file mode 100644 index 4fb9216c..00000000 --- a/src/Reflex/Patch/DMapWithMove.hs +++ /dev/null @@ -1,363 +0,0 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE PatternGuards #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE Rank2Types #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} - --- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and --- move values between keys. -module Reflex.Patch.DMapWithMove where - -import Reflex.Patch.Class -import Reflex.Patch.MapWithMove (PatchMapWithMove (..)) -import qualified Reflex.Patch.MapWithMove as MapWithMove - -import Data.Constraint.Extras -import Data.Dependent.Map (DMap, DSum (..), GCompare (..)) -import qualified Data.Dependent.Map as DMap -import Data.Functor.Constant -import Data.Functor.Misc -import Data.Functor.Product -import Data.GADT.Compare (GEq (..)) -import Data.GADT.Show (GShow, gshow) -import qualified Data.Map as Map -import Data.Maybe -import Data.Semigroup (Semigroup (..), (<>)) -import Data.Some (Some(Some)) -import Data.These - --- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and --- destinations. --- --- Invariants: --- --- * A key should not move to itself. --- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@) -newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v)) - --- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key --- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move. -data NodeInfo k v a = NodeInfo - { _nodeInfo_from :: !(From k v a) - -- ^Change applying to the current key, be it an insert, move, or delete. - , _nodeInfo_to :: !(To k a) - -- ^Where this key is moving to, if involved in a move. Should only be @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'. - } - deriving (Show) - --- |Structure describing a particular change to a key, be it inserting a new key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting a --- key (@From_Delete@), or moving a key (@From_Move@). -data From (k :: a -> *) (v :: a -> *) :: a -> * where - -- |Insert a new or update an existing key with the given value @v a@ - From_Insert :: v a -> From k v a - -- |Delete the existing key - From_Delete :: From k v a - -- |Move the value from the given key @k a@ to this key. The source key should also have an entry in the patch giving the current key as @_nodeInfo_to@, - -- usually but not necessarily with @From_Delete@. - From_Move :: !(k a) -> From k v a - deriving (Show, Read, Eq, Ord) - --- |Type alias for the "to" part of a 'NodeInfo'. @'ComposeMaybe' ('Just' k)@ means the key is moving to another key, @ComposeMaybe Nothing@ for any other --- operation. -type To = ComposeMaybe - --- |Test whether a 'PatchDMapWithMove' satisfies its invariants. -validPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Bool -validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove - --- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid. -validationErrorsForPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> [String] -validationErrorsForPatchDMapWithMove m = - noSelfMoves <> movesBalanced - where - noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m - selfMove (dst :=> NodeInfo (From_Move src) _) | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side" - selfMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side" - selfMove _ = Nothing - movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m - unbalancedMove (dst :=> NodeInfo (From_Move src) _) = - case DMap.lookup src m of - Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch" - Just (NodeInfo _ (ComposeMaybe (Just dst'))) -> - if isNothing (dst' `geq` dst) - then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead" - else Nothing - _ -> - Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key" - unbalancedMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) = - case DMap.lookup dst m of - Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch" - Just (NodeInfo (From_Move src') _) -> - if isNothing (src' `geq` src) - then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead" - else Nothing - - _ -> - Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving" - unbalancedMove _ = Nothing - --- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations. -instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where - PatchDMapWithMove a == PatchDMapWithMove b = a == b - --- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9 -data Pair1 f g a = Pair1 (f a) (g a) - --- |Helper data structure used for composing patches using the monoid instance. -data Fixup k v a - = Fixup_Delete - | Fixup_Update (These (From k v a) (To k a)) - --- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@ -instance GCompare k => Semigroup (PatchDMapWithMove k v) where - PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m - where - connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb - h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)] - h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of - (Just toAfter, From_Move fromBefore) - | isJust $ fromBefore `geq` toAfter - -> [toAfter :=> Fixup_Delete] - | otherwise - -> [ toAfter :=> Fixup_Update (This editBefore) - , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter)) - ] - (Nothing, From_Move fromBefore) -> [fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map - (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)] - (Nothing, _) -> [] - mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete - mergeFixups _ (Fixup_Update a) (Fixup_Update b) - | This x <- a, That y <- b - = Fixup_Update $ These x y - | That y <- a, This x <- b - = Fixup_Update $ These x y - mergeFixups _ _ _ = error "PatchDMapWithMove: incompatible fixups" - fixups = DMap.fromListWithKey mergeFixups $ concatMap h connections - combineNodeInfos _ nia nib = NodeInfo - { _nodeInfo_from = _nodeInfo_from nia - , _nodeInfo_to = _nodeInfo_to nib - } - applyFixup _ ni = \case - Fixup_Delete -> Nothing - Fixup_Update u -> Just $ NodeInfo - { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u - , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u - } - m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups - getHere :: These a b -> Maybe a - getHere = \case - This a -> Just a - These a _ -> Just a - That _ -> Nothing - getThere :: These a b -> Maybe b - getThere = \case - This _ -> Nothing - These _ b -> Just b - That b -> Just b - --- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@ -instance GCompare k => Monoid (PatchDMapWithMove k v) where - mempty = PatchDMapWithMove mempty - mappend = (<>) - -{- -mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v -PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src - where - getDstAction k m = fromMaybe (From_Move k) $ DMap.lookup k m -- Any key that isn't present is treated as that key moving to itself - removeRedundantDst toKey (From_Move fromKey) | isJust (toKey `geq` fromKey) = Nothing - removeRedundantDst _ a = Just a - f :: forall a. k a -> From k v a -> Maybe (From k v a) - f toKey _ = removeRedundantDst toKey $ case getDstAction toKey dstAfter of - From_Move fromKey -> getDstAction fromKey dstBefore - nonMove -> nonMove - dst = DMap.mapMaybeWithKey f $ DMap.union dstAfter dstBefore - getSrcAction k m = fromMaybe (ComposeMaybe $ Just k) $ DMap.lookup k m - removeRedundantSrc fromKey (ComposeMaybe (Just toKey)) | isJust (fromKey `geq` toKey) = Nothing - removeRedundantSrc _ a = Just a - g :: forall a. k a -> ComposeMaybe k a -> Maybe (ComposeMaybe k a) - g fromKey _ = removeRedundantSrc fromKey $ case getSrcAction fromKey srcBefore of - ComposeMaybe Nothing -> ComposeMaybe Nothing - ComposeMaybe (Just toKeyBefore) -> getSrcAction toKeyBefore srcAfter - src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore --} - --- |Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or updating a value @v a@ to the given key @k a@, like 'DMap.insert'. -insertDMapKey :: k a -> v a -> PatchDMapWithMove k v -insertDMapKey k v = - PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing) - --- |Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value from the first key @k a@ to the second key @k a@, equivalent to: --- --- @ --- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap)) --- @ -moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v -moveDMapKey src dst = case src `geq` dst of - Nothing -> PatchDMapWithMove $ DMap.fromList - [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing) - , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst) - ] - Just _ -> mempty - --- |Make a @'PatchDMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to: --- --- @ --- let aMay = DMap.lookup a dmap --- bMay = DMap.lookup b dmap --- in maybe id (DMap.insert a) (bMay `mplus` aMay) --- . maybe id (DMap.insert b) (aMay `mplus` bMay) --- . DMap.delete a . DMap.delete b $ dmap --- @ -swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v -swapDMapKey src dst = case src `geq` dst of - Nothing -> PatchDMapWithMove $ DMap.fromList - [ dst :=> NodeInfo (From_Move src) (ComposeMaybe $ Just src) - , src :=> NodeInfo (From_Move dst) (ComposeMaybe $ Just dst) - ] - Just _ -> mempty - --- |Make a @'PatchDMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'DMap.delete'. -deleteDMapKey :: k a -> PatchDMapWithMove k v -deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete $ ComposeMaybe Nothing - -{- -k1, k2 :: Const2 Int () () -k1 = Const2 1 -k2 = Const2 2 -p1, p2 :: PatchDMapWithMove (Const2 Int ()) Identity -p1 = moveDMapKey k1 k2 -p2 = moveDMapKey k2 k1 -p12 = p1 <> p2 -p21 = p2 <> p1 -p12Slow = p1 `mappendPatchDMapWithMoveSlow` p2 -p21Slow = p2 `mappendPatchDMapWithMoveSlow` p1 - -testPatchDMapWithMove = do - print p1 - print p2 - print $ p12 == deleteDMapKey k1 - print $ p21 == deleteDMapKey k2 - print $ p12Slow == deleteDMapKey k1 - print $ p21Slow == deleteDMapKey k2 - -dst (PatchDMapWithMove x _) = x -src (PatchDMapWithMove _ x) = x --} - --- |Extract the 'DMap' representing the patch changes from the 'PatchDMapWithMove'. -unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v) -unPatchDMapWithMove (PatchDMapWithMove p) = p - --- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove', without checking any invariants. --- --- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithMove' are preserved; they will not be checked. -unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v -unsafePatchDMapWithMove = PatchDMapWithMove - --- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while checking invariants. If the invariants are satisfied, @Right p@ is returned --- otherwise @Left errors@. -patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v) -patchDMapWithMove dm = - case validationErrorsForPatchDMapWithMove dm of - [] -> Right $ unsafePatchDMapWithMove dm - errs -> Left errs - --- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@. -mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v' -mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $ - DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p - where g :: forall a. From k v a -> From k v' a - g = \case - From_Insert v -> From_Insert $ f v - From_Delete -> From_Delete - From_Move k -> From_Move k - --- |Traverse an effectful function @forall a. v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@. -traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v') -traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f - --- |Map an effectful function @forall a. k a -> v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@. -traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v') -traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p - where g :: forall a. k a -> From k v a -> m (From k v' a) - g k = \case - From_Insert v -> From_Insert <$> f k v - From_Delete -> pure From_Delete - From_Move fromKey -> pure $ From_Move fromKey - --- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@. -nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a -nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni } - --- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@. -nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a) -nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni - --- |Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys from @k a@ to @'Some' k@ and applying a given weakening function @v a -> v'@ to --- values. -weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v' -weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p - where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v' - g ni = MapWithMove.NodeInfo - { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of - From_Insert v -> MapWithMove.From_Insert $ f v - From_Delete -> MapWithMove.From_Delete - From_Move k -> MapWithMove.From_Move $ Some k - , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni) - } - --- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any --- dependency in the typing and all points are already @a@, hence the function to map each value to @v'@ is not higher rank. -patchDMapWithMoveToPatchMapWithMoveWith :: forall k v v' a. (v a -> v') -> PatchDMapWithMove (Const2 k a) v -> PatchMapWithMove k v' -patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p - where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k v' - g ni = MapWithMove.NodeInfo - { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of - From_Insert v -> MapWithMove.From_Insert $ f v - From_Delete -> MapWithMove.From_Delete - From_Move (Const2 k) -> MapWithMove.From_Move k - , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni) - } - --- |"Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove ('Const2' k a)@; that is, turn a non-dependently-typed patch into a dependently typed --- one but which always has a constant key type represented by 'Const2'. Apply the given function to each @v@ to produce a @v' a@. --- Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith' -const2PatchDMapWithMoveWith :: forall k v v' a. (v -> v' a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) v' -const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p - where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v') - g (k, ni) = Const2 k :=> NodeInfo - { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of - MapWithMove.From_Insert v -> From_Insert $ f v - MapWithMove.From_Delete -> From_Delete - MapWithMove.From_Move fromKey -> From_Move $ Const2 fromKey - , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni - } - --- | Apply the insertions, deletions, and moves to a given 'DMap'. -instance GCompare k => Patch (PatchDMapWithMove k v) where - type PatchTarget (PatchDMapWithMove k v) = DMap k v - apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust? - where insertions = DMap.mapMaybeWithKey insertFunc p - insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a) - insertFunc _ ni = case _nodeInfo_from ni of - From_Insert v -> Just v - From_Move k -> DMap.lookup k old - From_Delete -> Nothing - deletions = DMap.mapMaybeWithKey deleteFunc p - deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a) - deleteFunc _ ni = case _nodeInfo_from ni of - From_Delete -> Just $ Constant () - _ -> Nothing - --- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'. -getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k)) -getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p - where f _ v ni = Pair v $ _nodeInfo_to ni diff --git a/src/Reflex/Patch/IntMap.hs b/src/Reflex/Patch/IntMap.hs deleted file mode 100644 index 3aba432f..00000000 --- a/src/Reflex/Patch/IntMap.hs +++ /dev/null @@ -1,59 +0,0 @@ -{-# LANGUAGE DeriveFoldable #-} -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE DeriveTraversable #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE TypeFamilies #-} --- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for --- insert/update or delete of associations. -module Reflex.Patch.IntMap where - -import Data.IntMap.Strict (IntMap) -import qualified Data.IntMap.Strict as IntMap -import Data.Maybe -import Data.Semigroup -import Reflex.Patch.Class - --- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping. --- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update --- and @Nothing@ means delete. -newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid) - --- | Apply the insertions or deletions to a given 'IntMap'. -instance Patch (PatchIntMap a) where - type PatchTarget (PatchIntMap a) = IntMap a - apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $ - let removes = IntMap.filter isNothing p - adds = IntMap.mapMaybe id p - in IntMap.union adds $ v `IntMap.difference` removes - --- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@. --- If the same key is modified by both patches, the one on the left will take --- precedence. -instance Semigroup (PatchIntMap v) where - PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map - -- PatchMap is idempotent, so stimes n is id for every n - stimes = stimesIdempotentMonoid - --- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@ --- (that is, all inserts/updates), producing a @PatchIntMap b@. -mapIntMapPatchWithKey :: (Int -> a -> b) -> PatchIntMap a -> PatchIntMap b -mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k mv -> f k <$> mv) m - --- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@ --- (that is, all inserts/updates), producing a @f (PatchIntMap b)@. -traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b) -traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m - --- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@. -patchIntMapNewElements :: PatchIntMap a -> [a] -patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m - --- | Convert the given @'PatchIntMap' a@ into an @'IntMap' a@ with all --- the inserts/updates in the given patch. -patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a -patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m - --- | Subset the given @'IntMap' a@ to contain only the keys that would be --- deleted by the given @'PatchIntMap' a@. -getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v' -getDeletions (PatchIntMap m) v = IntMap.intersection v m diff --git a/src/Reflex/Patch/Map.hs b/src/Reflex/Patch/Map.hs deleted file mode 100644 index d0812ae3..00000000 --- a/src/Reflex/Patch/Map.hs +++ /dev/null @@ -1,55 +0,0 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE TypeFamilies #-} --- | 'Patch'es on 'Map' that consist only of insertions (including overwrites) --- and deletions -module Reflex.Patch.Map where - -import Reflex.Patch.Class - -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe -import Data.Semigroup - --- | A set of changes to a 'Map'. Any element may be inserted/updated or --- deleted. Insertions are represented as values wrapped in 'Just', while --- deletions are represented as 'Nothing's -newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) } - deriving (Show, Read, Eq, Ord) - --- | Apply the insertions or deletions to a given 'Map'. -instance Ord k => Patch (PatchMap k v) where - type PatchTarget (PatchMap k v) = Map k v - {-# INLINABLE apply #-} - apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust? - where insertions = Map.mapMaybeWithKey (const id) p - deletions = Map.mapMaybeWithKey (const nothingToJust) p - nothingToJust = \case - Nothing -> Just () - Just _ -> Nothing - --- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@. --- If the same key is modified by both patches, the one on the left will take --- precedence. -instance Ord k => Semigroup (PatchMap k v) where - PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map - -- PatchMap is idempotent, so stimes n is id for every n - stimes = stimesIdempotentMonoid - --- | The empty 'PatchMap' contains no insertions or deletions -instance Ord k => Monoid (PatchMap k v) where - mempty = PatchMap mempty - mappend = (<>) - --- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert. --- Deletions are unaffected. -instance Functor (PatchMap k) where - fmap f = PatchMap . fmap (fmap f) . unPatchMap - --- | Returns all the new elements that will be added to the 'Map' -patchMapNewElements :: PatchMap k v -> [v] -patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p - --- | Returns all the new elements that will be added to the 'Map' -patchMapNewElementsMap :: PatchMap k v -> Map k v -patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p diff --git a/src/Reflex/Patch/MapWithMove.hs b/src/Reflex/Patch/MapWithMove.hs deleted file mode 100644 index ccdb8b4d..00000000 --- a/src/Reflex/Patch/MapWithMove.hs +++ /dev/null @@ -1,270 +0,0 @@ -{-# LANGUAGE DeriveFoldable #-} -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE DeriveTraversable #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE PatternGuards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} --- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to --- another -module Reflex.Patch.MapWithMove where - -import Reflex.Patch.Class - -import Control.Arrow -import Control.Monad.State -import Data.Foldable -import Data.Function -import Data.List -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe -import Data.Semigroup (Semigroup (..), (<>)) -import qualified Data.Set as Set -import Data.These (These(..)) -import Data.Tuple - --- | Patch a DMap with additions, deletions, and moves. Invariant: If key @k1@ --- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@, --- and vice versa. There should never be any unpaired From/To keys. -newtype PatchMapWithMove k v = PatchMapWithMove (Map k (NodeInfo k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable) - --- | Holds the information about each key: where its new value should come from, --- and where its old value should go to -data NodeInfo k v = NodeInfo - { _nodeInfo_from :: !(From k v) - -- ^ Where do we get the new value for this key? - , _nodeInfo_to :: !(To k) - -- ^ If the old value is being kept (i.e. moved rather than deleted or - -- replaced), where is it going? - } - deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable) - --- | Describe how a key's new value should be produced -data From k v - = From_Insert v -- ^ Insert the given value here - | From_Delete -- ^ Delete the existing value, if any, from here - | From_Move !k -- ^ Move the value here from the given key - deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable) - --- | Describe where a key's old value will go. If this is 'Just', that means --- the key's old value will be moved to the given other key; if it is 'Nothing', --- that means it will be deleted. -type To = Maybe - --- | Create a 'PatchMapWithMove', validating it -patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v) -patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing - where valid = forwardLinks == backwardLinks - forwardLinks = Map.mapMaybe _nodeInfo_to m - backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, v) -> - case _nodeInfo_from v of - From_Move from -> Just (from, to) - _ -> Nothing - --- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map' -patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v -patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo - { _nodeInfo_from = From_Insert v - , _nodeInfo_to = Nothing - } - --- | Extract the internal representation of the 'PatchMapWithMove' -unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v) -unPatchMapWithMove (PatchMapWithMove p) = p - --- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'. -insertMapKey :: k -> v -> PatchMapWithMove k v -insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing - --- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to: --- --- @ --- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map)) --- @ -moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v -moveMapKey src dst - | src == dst = mempty - | otherwise = - PatchMapWithMove $ Map.fromList - [ (dst, NodeInfo (From_Move src) Nothing) - , (src, NodeInfo From_Delete (Just dst)) - ] - --- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to: --- --- @ --- let aMay = Map.lookup a map --- bMay = Map.lookup b map --- in maybe id (Map.insert a) (bMay `mplus` aMay) --- . maybe id (Map.insert b) (aMay `mplus` bMay) --- . Map.delete a . Map.delete b $ map --- @ -swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v -swapMapKey src dst - | src == dst = mempty - | otherwise = - PatchMapWithMove $ Map.fromList - [ (dst, NodeInfo (From_Move src) (Just src)) - , (src, NodeInfo (From_Move dst) (Just dst)) - ] - --- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'. -deleteMapKey :: k -> PatchMapWithMove k v -deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete Nothing - --- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants. --- --- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked. -unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v -unsafePatchMapWithMove = PatchMapWithMove - --- | Apply the insertions, deletions, and moves to a given 'Map' -instance Ord k => Patch (PatchMapWithMove k v) where - type PatchTarget (PatchMapWithMove k v) = Map k v - apply (PatchMapWithMove p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust? - where insertions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of - From_Insert v -> Just v - From_Move k -> Map.lookup k old - From_Delete -> Nothing - deletions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of - From_Delete -> Just () - _ -> Nothing - --- | Returns all the new elements that will be added to the 'Map'. -patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v] -patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap - --- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@. -patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v -patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p - where f ni = case _nodeInfo_from ni of - From_Insert v -> Just v - From_Move _ -> Nothing - From_Delete -> Nothing - --- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort --- its values using the given ordering function. The set keys of the 'Map' is --- not changed. -patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v -patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted - where unsorted = Map.toList m - sorted = sortBy (cmp `on` snd) unsorted - f (to, _) (from, _) = if to == from then Nothing else - Just (from, to) - reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted - g (to, _) (from, _) = if to == from then Nothing else - let Just movingTo = Map.lookup to reverseMapping - in Just (to, NodeInfo (From_Move from) $ Just movingTo) - --- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided, --- will produce a 'Map' with the same values as the second 'Map' but with the --- values sorted with the given ordering function. -patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v -patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex - where newList = Map.toList newByIndexUnsorted - newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList - --- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided, --- will produce the second 'Map'. -patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v -patchThatChangesMap oldByIndex newByIndex = patch - where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex - (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do - let f k v = do - remainingValues <- get - let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys - then Map.delete v remainingValues - else Map.insert v remainingKeys remainingValues - case Map.lookup v remainingValues of - Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take - Just fromKs -> - if k `Set.member` fromKs - then do - putRemainingKeys $ Set.delete k fromKs - return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary - else do - (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here - putRemainingKeys remainingKeys - return $ NodeInfo (From_Move fromK) $ Just undefined - Map.traverseWithKey f newByIndex - unusedOldKeys = fold unusedValuesByValue - pointlessMove k = \case - From_Move k' | k == k' -> True - _ -> False - keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys) - then Just undefined - else Nothing - patch = unsafePatchMapWithMove $ Map.filterWithKey (\k -> not . pointlessMove k . _nodeInfo_from) $ Map.mergeWithKey (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a) (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k) (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k) insertsAndMoves oldByIndex - --- | Change the 'From' value of a 'NodeInfo' -nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v -nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni } - --- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or --- 'Applicative', 'Monad', etc.) action to get the new value -nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v) -nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni - --- | Set the 'To' field of a 'NodeInfo' -nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v -nodeInfoSetTo to ni = ni { _nodeInfo_to = to } - --- |Helper data structure used for composing patches using the monoid instance. -data Fixup k v - = Fixup_Delete - | Fixup_Update (These (From k v) (To k)) - --- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@ -instance Ord k => Semigroup (PatchMapWithMove k v) where - PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m - where - connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb - h :: (k, (Maybe k, From k v)) -> [(k, Fixup k v)] - h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of - (Just toAfter, From_Move fromBefore) - | fromBefore == toAfter - -> [(toAfter, Fixup_Delete)] - | otherwise - -> [ (toAfter, Fixup_Update (This editBefore)) - , (fromBefore, Fixup_Update (That mToAfter)) - ] - (Nothing, From_Move fromBefore) -> [(fromBefore, Fixup_Update (That mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map - (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))] - (Nothing, _) -> [] - mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete - mergeFixups _ (Fixup_Update a) (Fixup_Update b) - | This x <- a, That y <- b - = Fixup_Update $ These x y - | That y <- a, This x <- b - = Fixup_Update $ These x y - mergeFixups _ _ _ = error "PatchMapWithMove: incompatible fixups" - fixups = Map.fromListWithKey mergeFixups $ concatMap h connections - combineNodeInfos _ nia nib = NodeInfo - { _nodeInfo_from = _nodeInfo_from nia - , _nodeInfo_to = _nodeInfo_to nib - } - applyFixup _ ni = \case - Fixup_Delete -> Nothing - Fixup_Update u -> Just $ NodeInfo - { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u - , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u - } - m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups - getHere :: These a b -> Maybe a - getHere = \case - This a -> Just a - These a _ -> Just a - That _ -> Nothing - getThere :: These a b -> Maybe b - getThere = \case - This _ -> Nothing - These _ b -> Just b - That b -> Just b - ---TODO: Figure out how to implement this in terms of PatchDMapWithMove rather than duplicating it here --- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@ -instance Ord k => Monoid (PatchMapWithMove k v) where - mempty = PatchMapWithMove mempty - mappend = (<>) diff --git a/src/Reflex/Query/Base.hs b/src/Reflex/Query/Base.hs index 99709451..5ee67759 100644 --- a/src/Reflex/Query/Base.hs +++ b/src/Reflex/Query/Base.hs @@ -47,7 +47,7 @@ import Reflex.DynamicWriter.Class import Reflex.EventWriter.Base import Reflex.EventWriter.Class import Reflex.Host.Class -import qualified Reflex.Patch.MapWithMove as MapWithMove +import qualified Data.Patch.MapWithMove as MapWithMove import Reflex.PerformEvent.Class import Reflex.PostBuild.Class import Reflex.Query.Class diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs index c0057961..54a09bef 100644 --- a/src/Reflex/Spider/Internal.hs +++ b/src/Reflex/Spider/Internal.hs @@ -104,8 +104,8 @@ import qualified Reflex.Class import qualified Reflex.Class as R import qualified Reflex.Host.Class import Reflex.NotReady.Class -import Reflex.Patch -import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove +import Data.Patch +import qualified Data.Patch.DMapWithMove as PatchDMapWithMove import Reflex.PerformEvent.Base (PerformEventT) #ifdef DEBUG_TRACE_EVENTS diff --git a/src/Reflex/Widget/Basic.hs b/src/Reflex/Widget/Basic.hs index 2286e494..22e2c76e 100644 --- a/src/Reflex/Widget/Basic.hs +++ b/src/Reflex/Widget/Basic.hs @@ -13,7 +13,7 @@ import Data.Map (Map) import Reflex.Class import Reflex.Adjustable.Class -import Reflex.Patch.MapWithMove +import Data.Patch.MapWithMove -- | Build sortable content in such a way that re-sorting it can cause minimal diff --git a/test/GC.hs b/test/GC.hs index 0fabf7cb..c9cfd681 100644 --- a/test/GC.hs +++ b/test/GC.hs @@ -20,7 +20,7 @@ import Data.Semigroup import Data.These import Data.Functor.Misc -import Reflex.Patch +import Data.Patch import qualified Reflex.Host.Class as Host import qualified Reflex.Spider.Internal as S diff --git a/test/QueryT.hs b/test/QueryT.hs index ac4add3b..47566510 100644 --- a/test/QueryT.hs +++ b/test/QueryT.hs @@ -24,7 +24,7 @@ import Data.These.Lens #endif import Reflex -import Reflex.Patch.MapWithMove +import Data.Patch.MapWithMove import Test.Run newtype MyQuery = MyQuery SelectedCount