on develop help sign in
Merge remote-tracking branch 'origin/develop' into extra-query-instances
8e6c2d1John Ericson
ChangeLog.md+5
@@ -2,8 +2,13 @@
2
2
3## Unreleased
3## Unreleased
4
4
5+* Support GHC 8.8
6+
5* Add `Reflex.Query.Base.mapQueryT`. See that module for documentation
7* Add `Reflex.Query.Base.mapQueryT`. See that module for documentation
6
8
9+* The `Reflex.Patch.*` modules were moved to the `patch` library.
10+ They are `Data.Patch.*` there, but reexported under their old names for backwards compatability here.
11+
7* Additional instances for `Group` and `Query` classes for basic types.
12* Additional instances for `Group` and `Query` classes for basic types.
8
13
9## 0.6.3
14## 0.6.3
cabal.project.freeze+1
@@ -1 +1,2 @@
1constraints: any.text < 1.2.4.0
1constraints: any.text < 1.2.4.0
2+ , hlint < 2.2.6 || > 2.2.6
dep/reflex-platform/github.json+3−3
@@ -1,7 +1,7 @@
1{
1{
2 "owner": "reflex-frp",
2 "owner": "reflex-frp",
3 "repo": "reflex-platform",
3 "repo": "reflex-platform",
4− "branch": "master",
4+ "branch": "develop",
5− "rev": "510b990d0b11f0626afbec5fe8575b5b2395391b",
5+ "rev": "f628398d076243a0851b27e625b37f65dff9b89b",
6− "sha256": "09cmahsbxr0963wq171c7j139iyzz49hramr4v9nsf684wcwkngv"
6+ "sha256": "0sl0hf1glgyb1vmf2mhw4r9ipmcqk1y19d3wsic7dix2jwywzrh9"
7}
7}
reflex.cabal+25−9
@@ -9,7 +9,7 @@ Maintainer: ryan.trinkle@gmail.com
9Stability: Experimental
9Stability: Experimental
10Category: FRP
10Category: FRP
11Build-type: Simple
11Build-type: Simple
12−Cabal-version: >=1.9.2
12+Cabal-version: 1.22
13homepage: https://reflex-frp.org
13homepage: https://reflex-frp.org
14bug-reports: https://github.com/reflex-frp/reflex/issues
14bug-reports: https://github.com/reflex-frp/reflex/issues
15extra-source-files:
15extra-source-files:
@@ -47,6 +47,7 @@ flag split-these
47 default: True
47 default: True
48
48
49library
49library
50+ default-language: Haskell2010
50 hs-source-dirs: src
51 hs-source-dirs: src
51 build-depends:
52 build-depends:
52 MemoTrie == 0.6.*,
53 MemoTrie == 0.6.*,
@@ -61,6 +62,7 @@ library
61 lens >= 4.7 && < 5,
62 lens >= 4.7 && < 5,
62 monad-control >= 1.0.1 && < 1.1,
63 monad-control >= 1.0.1 && < 1.1,
63 mtl >= 2.1 && < 2.3,
64 mtl >= 2.1 && < 2.3,
65+ patch >= 0.0 && < 0.1,
64 prim-uniq >= 0.1.0.1 && < 0.2,
66 prim-uniq >= 0.1.0.1 && < 0.2,
65 primitive >= 0.5 && < 0.8,
67 primitive >= 0.5 && < 0.8,
66 profunctors >= 5.3 && < 5.6,
68 profunctors >= 5.3 && < 5.6,
@@ -88,7 +90,6 @@ library
88 Data.AppendMap,
90 Data.AppendMap,
89 Data.FastMutableIntMap,
91 Data.FastMutableIntMap,
90 Data.FastWeakBag,
92 Data.FastWeakBag,
91− Data.Functor.Misc,
92 Data.Map.Misc,
93 Data.Map.Misc,
93 Data.WeakBag,
94 Data.WeakBag,
94 Reflex,
95 Reflex,
@@ -110,13 +111,6 @@ library
110 Reflex.Host.Class,
111 Reflex.Host.Class,
111 Reflex.Network,
112 Reflex.Network,
112 Reflex.NotReady.Class,
113 Reflex.NotReady.Class,
113− Reflex.Patch,
114− Reflex.Patch.Class,
115− Reflex.Patch.DMap,
116− Reflex.Patch.DMapWithMove,
117− Reflex.Patch.IntMap,
118− Reflex.Patch.Map,
119− Reflex.Patch.MapWithMove,
120 Reflex.PerformEvent.Base,
114 Reflex.PerformEvent.Base,
121 Reflex.PerformEvent.Class,
115 Reflex.PerformEvent.Class,
122 Reflex.PostBuild.Base,
116 Reflex.PostBuild.Base,
@@ -135,6 +129,16 @@ library
135 Reflex.Widget.Basic,
129 Reflex.Widget.Basic,
136 Reflex.Workflow
130 Reflex.Workflow
137
131
132+ reexported-modules:
133+ patch:Data.Functor.Misc,
134+ patch:Data.Patch as Reflex.Patch,
135+ patch:Data.Patch.Class as Reflex.Patch.Class,
136+ patch:Data.Patch.DMap as Reflex.Patch.DMap,
137+ patch:Data.Patch.DMapWithMove as Reflex.Patch.DMapWithMove,
138+ patch:Data.Patch.IntMap as Reflex.Patch.IntMap,
139+ patch:Data.Patch.Map as Reflex.Patch.Map,
140+ patch:Data.Patch.MapWithMove as Reflex.Patch.MapWithMove
141+
138 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs -funbox-strict-fields -O2 -fspecialise-aggressively
142 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs -funbox-strict-fields -O2 -fspecialise-aggressively
139
143
140 if flag(debug-trace-events)
144 if flag(debug-trace-events)
@@ -167,6 +171,7 @@ library
167 build-depends: ghcjs-base
171 build-depends: ghcjs-base
168
172
169test-suite semantics
173test-suite semantics
174+ default-language: Haskell2010
170 type: exitcode-stdio-1.0
175 type: exitcode-stdio-1.0
171 main-is: semantics.hs
176 main-is: semantics.hs
172 hs-source-dirs: test
177 hs-source-dirs: test
@@ -192,6 +197,7 @@ test-suite semantics
192 Reflex.TestPlan
197 Reflex.TestPlan
193
198
194test-suite CrossImpl
199test-suite CrossImpl
200+ default-language: Haskell2010
195 type: exitcode-stdio-1.0
201 type: exitcode-stdio-1.0
196 main-is: Reflex/Test/CrossImpl.hs
202 main-is: Reflex/Test/CrossImpl.hs
197 hs-source-dirs: test
203 hs-source-dirs: test
@@ -213,6 +219,7 @@ test-suite CrossImpl
213 Reflex.Plan.Pure
219 Reflex.Plan.Pure
214
220
215test-suite hlint
221test-suite hlint
222+ default-language: Haskell2010
216 type: exitcode-stdio-1.0
223 type: exitcode-stdio-1.0
217 main-is: hlint.hs
224 main-is: hlint.hs
218 hs-source-dirs: test
225 hs-source-dirs: test
@@ -225,6 +232,7 @@ test-suite hlint
225 buildable: False
232 buildable: False
226
233
227test-suite EventWriterT
234test-suite EventWriterT
235+ default-language: Haskell2010
228 type: exitcode-stdio-1.0
236 type: exitcode-stdio-1.0
229 main-is: EventWriterT.hs
237 main-is: EventWriterT.hs
230 hs-source-dirs: test
238 hs-source-dirs: test
@@ -252,6 +260,7 @@ test-suite EventWriterT
252
260
253
261
254test-suite RequesterT
262test-suite RequesterT
263+ default-language: Haskell2010
255 type: exitcode-stdio-1.0
264 type: exitcode-stdio-1.0
256 main-is: RequesterT.hs
265 main-is: RequesterT.hs
257 hs-source-dirs: test
266 hs-source-dirs: test
@@ -276,6 +285,7 @@ test-suite RequesterT
276 Test.Run
285 Test.Run
277
286
278test-suite QueryT
287test-suite QueryT
288+ default-language: Haskell2010
279 type: exitcode-stdio-1.0
289 type: exitcode-stdio-1.0
280 main-is: QueryT.hs
290 main-is: QueryT.hs
281 hs-source-dirs: test
291 hs-source-dirs: test
@@ -287,6 +297,7 @@ test-suite QueryT
287 , lens
297 , lens
288 , monoidal-containers
298 , monoidal-containers
289 , mtl
299 , mtl
300+ , patch
290 , ref-tf
301 , ref-tf
291 , reflex
302 , reflex
292 , these
303 , these
@@ -302,6 +313,7 @@ test-suite QueryT
302 Reflex.Plan.Pure
313 Reflex.Plan.Pure
303
314
304test-suite GC-Semantics
315test-suite GC-Semantics
316+ default-language: Haskell2010
305 type: exitcode-stdio-1.0
317 type: exitcode-stdio-1.0
306 main-is: GC.hs
318 main-is: GC.hs
307 hs-source-dirs: test
319 hs-source-dirs: test
@@ -311,6 +323,7 @@ test-suite GC-Semantics
311 , dependent-map
323 , dependent-map
312 , deepseq
324 , deepseq
313 , mtl
325 , mtl
326+ , patch
314 , these
327 , these
315 , transformers
328 , transformers
316 , reflex
329 , reflex
@@ -326,6 +339,7 @@ test-suite GC-Semantics
326 Test.Run
339 Test.Run
327
340
328test-suite rootCleanup
341test-suite rootCleanup
342+ default-language: Haskell2010
329 type: exitcode-stdio-1.0
343 type: exitcode-stdio-1.0
330 main-is: rootCleanup.hs
344 main-is: rootCleanup.hs
331 hs-source-dirs: test
345 hs-source-dirs: test
@@ -343,6 +357,7 @@ test-suite rootCleanup
343 Test.Run
357 Test.Run
344
358
345benchmark spider-bench
359benchmark spider-bench
360+ default-language: Haskell2010
346 type: exitcode-stdio-1.0
361 type: exitcode-stdio-1.0
347 hs-source-dirs: bench test
362 hs-source-dirs: bench test
348 main-is: Main.hs
363 main-is: Main.hs
@@ -367,6 +382,7 @@ benchmark spider-bench
367 Reflex.Bench.Focused
382 Reflex.Bench.Focused
368
383
369benchmark saulzar-bench
384benchmark saulzar-bench
385+ default-language: Haskell2010
370 type: exitcode-stdio-1.0
386 type: exitcode-stdio-1.0
371 hs-source-dirs: bench test
387 hs-source-dirs: bench test
372 c-sources: bench-cbits/checkCapability.c
388 c-sources: bench-cbits/checkCapability.c
src/Data/AppendMap.hs+1−1
@@ -71,7 +71,7 @@ mapMaybeNoNull f as =
71 then Nothing
71 then Nothing
72 else Just bs
72 else Just bs
73
73
74−-- TODO: Move instances to `Reflex.Patch`
74+-- TODO: Move instances to `Data.Patch`
75-- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details.
75-- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details.
76showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
76showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
77showTree = coerce (Map.showTree :: Map k a -> String)
77showTree = coerce (Map.showTree :: Map k a -> String)
src/Data/FastMutableIntMap.hs+3−3
@@ -34,8 +34,8 @@ import Data.Foldable (traverse_)
34import Data.IntMap.Strict (IntMap)
34import Data.IntMap.Strict (IntMap)
35import qualified Data.IntMap.Strict as IntMap
35import qualified Data.IntMap.Strict as IntMap
36import Data.IORef
36import Data.IORef
37−import Reflex.Patch.Class
37+import Data.Patch.Class
38−import Reflex.Patch.IntMap
38+import Data.Patch.IntMap
39
39
40-- | A 'FastMutableIntMap' holds a map of values of type @a@ and allows low-overhead modifications via IO.
40-- | A 'FastMutableIntMap' holds a map of values of type @a@ and allows low-overhead modifications via IO.
41-- Operations on 'FastMutableIntMap' run in IO.
41-- Operations on 'FastMutableIntMap' run in IO.
@@ -84,7 +84,7 @@ getFrozenAndClear (FastMutableIntMap r) = do
84 writeIORef r IntMap.empty
84 writeIORef r IntMap.empty
85 return result
85 return result
86
86
87−-- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Reflex.Patch.IntMap'),
87+-- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Data.Patch.IntMap'),
88-- and returns an 'IntMap' with the modified keys and values.
88-- and returns an 'IntMap' with the modified keys and values.
89applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
89applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
90applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
90applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
src/Data/Functor/Misc.hsdeleted−235
@@ -1,235 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveDataTypeable #-}
3−{-# LANGUAGE DeriveFunctor #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE LambdaCase #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PatternSynonyms #-}
10−{-# LANGUAGE PolyKinds #-}
11−{-# LANGUAGE RankNTypes #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE StandaloneDeriving #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−-- | This module provides types and functions with no particular theme, but
18−-- which are relevant to the use of 'Functor'-based datastructures like
19−-- 'Data.Dependent.Map.DMap'.
20−module Data.Functor.Misc
21− ( -- * Const2
22− Const2 (..)
23− , unConst2
24− , dmapToMap
25− , dmapToIntMap
26− , dmapToMapWith
27− , mapToDMap
28− , weakenDMapWith
29− -- * WrapArg
30− , WrapArg (..)
31− -- * Convenience functions for DMap
32− , mapWithFunctorToDMap
33− , intMapWithFunctorToDMap
34− , mapKeyValuePairsMonotonic
35− , combineDMapsWithKey
36− , EitherTag (..)
37− , dmapToThese
38− , eitherToDSum
39− , dsumToEither
40− , ComposeMaybe (..)
41− ) where
42−
43−import Control.Applicative ((<$>))
44−import Control.Monad.Identity
45−import Data.Dependent.Map (DMap)
46−import qualified Data.Dependent.Map as DMap
47−import Data.Dependent.Sum
48−import Data.GADT.Compare
49−import Data.GADT.Show
50−import Data.IntMap (IntMap)
51−import qualified Data.IntMap as IntMap
52−import Data.Map (Map)
53−import qualified Data.Map as Map
54−import Data.Some (Some(Some))
55−import Data.These
56−import Data.Type.Equality ((:~:)(Refl))
57−import Data.Typeable hiding (Refl)
58−
59−--------------------------------------------------------------------------------
60−-- Const2
61−--------------------------------------------------------------------------------
62−
63−-- | 'Const2' stores a value of a given type 'k' and ensures that a particular
64−-- type 'v' is always given for the last type parameter
65−data Const2 :: * -> x -> x -> * where
66− Const2 :: k -> Const2 k v v
67− deriving (Typeable)
68−
69−-- | Extract the value from a Const2
70−unConst2 :: Const2 k v v' -> k
71−unConst2 (Const2 k) = k
72−
73−deriving instance Eq k => Eq (Const2 k v v')
74−deriving instance Ord k => Ord (Const2 k v v')
75−deriving instance Show k => Show (Const2 k v v')
76−deriving instance Read k => Read (Const2 k v v)
77−
78−instance Show k => GShow (Const2 k v) where
79− gshowsPrec n x@(Const2 _) = showsPrec n x
80−
81−instance Eq k => GEq (Const2 k v) where
82− geq (Const2 a) (Const2 b) =
83− if a == b
84− then Just Refl
85− else Nothing
86−
87−instance Ord k => GCompare (Const2 k v) where
88− gcompare (Const2 a) (Const2 b) = case compare a b of
89− LT -> GLT
90− EQ -> GEQ
91− GT -> GGT
92−
93−-- | Convert a 'DMap' to a regular 'Map'
94−dmapToMap :: DMap (Const2 k v) Identity -> Map k v
95−dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
96−
97−-- | Convert a 'DMap' to an 'IntMap'
98−dmapToIntMap :: DMap (Const2 IntMap.Key v) Identity -> IntMap v
99−dmapToIntMap = IntMap.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
100−
101−-- | Convert a 'DMap' to a regular 'Map', applying the given function to remove
102−-- the wrapping 'Functor'
103−dmapToMapWith :: (f v -> v') -> DMap (Const2 k v) f -> Map k v'
104−dmapToMapWith f = Map.fromDistinctAscList . map (\(Const2 k :=> v) -> (k, f v)) . DMap.toAscList
105−
106−-- | Convert a regular 'Map' to a 'DMap'
107−mapToDMap :: Map k v -> DMap (Const2 k v) Identity
108−mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
109−
110−-- | Convert a regular 'Map', where the values are already wrapped in a functor,
111−-- to a 'DMap'
112−mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
113−mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
114−
115−-- | Convert a regular 'IntMap', where the values are already wrapped in a
116−-- functor, to a 'DMap'
117−intMapWithFunctorToDMap :: IntMap (f v) -> DMap (Const2 IntMap.Key v) f
118−intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . IntMap.toAscList
119−
120−-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
121−-- the keys, using a function to remove the wrapping 'Functor'
122−weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
123−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
124−
125−--------------------------------------------------------------------------------
126−-- WrapArg
127−--------------------------------------------------------------------------------
128−
129−-- | 'WrapArg' can be used to tag a value in one functor with a type
130−-- representing another functor. This was primarily used with dependent-map <
131−-- 0.2, in which the value type was not wrapped in a separate functor.
132−data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
133− WrapArg :: f a -> WrapArg g f (g a)
134−
135−deriving instance Eq (f a) => Eq (WrapArg g f (g' a))
136−deriving instance Ord (f a) => Ord (WrapArg g f (g' a))
137−deriving instance Show (f a) => Show (WrapArg g f (g' a))
138−deriving instance Read (f a) => Read (WrapArg g f (g a))
139−
140−instance GEq f => GEq (WrapArg g f) where
141− geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
142−
143−instance GCompare f => GCompare (WrapArg g f) where
144− gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
145− GLT -> GLT
146− GEQ -> GEQ
147− GGT -> GGT
148−
149−--------------------------------------------------------------------------------
150−-- Convenience functions for DMap
151−--------------------------------------------------------------------------------
152−
153−-- | Map over all key/value pairs in a 'DMap', potentially altering the key as
154−-- well as the value. The provided function MUST preserve the ordering of the
155−-- keys, or the resulting 'DMap' will be malformed.
156−mapKeyValuePairsMonotonic :: (DSum k v -> DSum k' v') -> DMap k v -> DMap k' v'
157−mapKeyValuePairsMonotonic f = DMap.fromDistinctAscList . map f . DMap.toAscList
158−
159−{-# INLINE combineDMapsWithKey #-}
160−-- | Union two 'DMap's of different types, yielding another type. Each key that
161−-- is present in either input map will be present in the output.
162−combineDMapsWithKey :: forall f g h i.
163− GCompare f
164− => (forall a. f a -> These (g a) (h a) -> i a)
165− -> DMap f g
166− -> DMap f h
167− -> DMap f i
168−combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
169− where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
170− go [] hs = map (\(hk :=> hv) -> hk :=> f hk (That hv)) hs
171− go gs [] = map (\(gk :=> gv) -> gk :=> f gk (This gv)) gs
172− go gs@((gk :=> gv) : gs') hs@((hk :=> hv) : hs') = case gk `gcompare` hk of
173− GLT -> (gk :=> f gk (This gv)) : go gs' hs
174− GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
175− GGT -> (hk :=> f hk (That hv)) : go gs hs'
176−
177−-- | Extract the values of a 'DMap' of 'EitherTag's.
178−dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
179−dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
180− (Nothing, Nothing) -> Nothing
181− (Just (Identity a), Nothing) -> Just $ This a
182− (Nothing, Just (Identity b)) -> Just $ That b
183− (Just (Identity a), Just (Identity b)) -> Just $ These a b
184−
185−-- | Tag type for 'Either' to use it as a 'DSum'.
186−data EitherTag l r a where
187− LeftTag :: EitherTag l r l
188− RightTag :: EitherTag l r r
189− deriving (Typeable)
190−
191−deriving instance Show (EitherTag l r a)
192−deriving instance Eq (EitherTag l r a)
193−deriving instance Ord (EitherTag l r a)
194−
195−instance GEq (EitherTag l r) where
196− geq a b = case (a, b) of
197− (LeftTag, LeftTag) -> Just Refl
198− (RightTag, RightTag) -> Just Refl
199− _ -> Nothing
200−
201−instance GCompare (EitherTag l r) where
202− gcompare a b = case (a, b) of
203− (LeftTag, LeftTag) -> GEQ
204− (LeftTag, RightTag) -> GLT
205− (RightTag, LeftTag) -> GGT
206− (RightTag, RightTag) -> GEQ
207−
208−instance GShow (EitherTag l r) where
209− gshowsPrec _ a = case a of
210− LeftTag -> showString "LeftTag"
211− RightTag -> showString "RightTag"
212−
213−-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
214−eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
215−eitherToDSum = \case
216− Left a -> (LeftTag :=> Identity a)
217− Right b -> (RightTag :=> Identity b)
218−
219−-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
220−dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
221−dsumToEither = \case
222− (LeftTag :=> Identity a) -> Left a
223− (RightTag :=> Identity b) -> Right b
224−
225−--------------------------------------------------------------------------------
226−-- ComposeMaybe
227−--------------------------------------------------------------------------------
228−
229−-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
230−-- make the 'f' parameter have a nominal type role. We need f to be
231−-- representational so that we can use safe 'coerce'.
232−newtype ComposeMaybe f a =
233− ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
234−
235−deriving instance Functor f => Functor (ComposeMaybe f)
src/Data/Map/Misc.hs+1−1
@@ -43,7 +43,7 @@ diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
43
43
44-- |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@.
44-- |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@.
45--
45--
46−-- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'.
46+-- See also 'Data.Patch.Map' and 'Data.Patch.MapWithMove'.
47applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
47applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
48applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
48applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
49 where (deletions, insertions) = Map.mapEither maybeToEither patch
49 where (deletions, insertions) = Map.mapEither maybeToEither patch
src/Reflex/Adjustable/Class.hs+1−1
@@ -39,7 +39,7 @@ import qualified Data.IntMap.Strict as IntMap
39import Data.Map (Map)
39import Data.Map (Map)
40
40
41import Reflex.Class
41import Reflex.Class
42−import Reflex.Patch.DMapWithMove
42+import Data.Patch.DMapWithMove
43
43
44-- | A 'Monad' that supports adjustment over time. After an action has been
44-- | A 'Monad' that supports adjustment over time. After an action has been
45-- run, if the given events fire, it will adjust itself so that its net effect
45-- run, if the given events fire, it will adjust itself so that its net effect
src/Reflex/Class.hs+3−3
@@ -29,7 +29,7 @@
29-- convenience functions for working with 'Event's, 'Behavior's, and other
29-- convenience functions for working with 'Event's, 'Behavior's, and other
30-- signals.
30-- signals.
31module Reflex.Class
31module Reflex.Class
32− ( module Reflex.Patch
32+ ( module Data.Patch
33 -- * Primitives
33 -- * Primitives
34 , Reflex (..)
34 , Reflex (..)
35 , mergeInt
35 , mergeInt
@@ -220,8 +220,8 @@ import Data.Witherable (Filterable(..))
220import qualified Data.Witherable as W
220import qualified Data.Witherable as W
221import Reflex.FunctorMaybe (FunctorMaybe)
221import Reflex.FunctorMaybe (FunctorMaybe)
222import qualified Reflex.FunctorMaybe
222import qualified Reflex.FunctorMaybe
223−import Reflex.Patch
223+import Data.Patch
224−import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
224+import qualified Data.Patch.MapWithMove as PatchMapWithMove
225
225
226import Debug.Trace (trace)
226import Debug.Trace (trace)
227
227
src/Reflex/DynamicWriter/Base.hs+1−1
@@ -44,7 +44,7 @@ import Reflex.Class
44import Reflex.DynamicWriter.Class
44import Reflex.DynamicWriter.Class
45import Reflex.EventWriter.Class (EventWriter, tellEvent)
45import Reflex.EventWriter.Class (EventWriter, tellEvent)
46import Reflex.Host.Class
46import Reflex.Host.Class
47−import qualified Reflex.Patch.MapWithMove as MapWithMove
47+import qualified Data.Patch.MapWithMove as MapWithMove
48import Reflex.PerformEvent.Class
48import Reflex.PerformEvent.Class
49import Reflex.PostBuild.Class
49import Reflex.PostBuild.Class
50import Reflex.Query.Class
50import Reflex.Query.Class
src/Reflex/Patch.hsdeleted−99
@@ -1,99 +0,0 @@
1−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2−{-# LANGUAGE StandaloneDeriving #-}
3−{-# LANGUAGE TypeFamilies #-}
4−{-# LANGUAGE TypeOperators #-}
5−-- |
6−-- Module:
7−-- Reflex.Patch
8−-- Description:
9−-- This module defines the 'Patch' class, which is used by Reflex to manage
10−-- changes to 'Reflex.Class.Incremental' values.
11−module Reflex.Patch
12− ( module Reflex.Patch
13− , module X
14− ) where
15−
16−import Reflex.Patch.Class as X
17−import Reflex.Patch.DMap as X hiding (getDeletions)
18−import Reflex.Patch.DMapWithMove as X (PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove,
19− patchDMapWithMoveToPatchMapWithMoveWith,
20− traversePatchDMapWithMoveWithKey, unPatchDMapWithMove,
21− unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith)
22−import Reflex.Patch.IntMap as X hiding (getDeletions)
23−import Reflex.Patch.Map as X
24−import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
25− patchMapWithMoveNewElementsMap, unPatchMapWithMove,
26− unsafePatchMapWithMove)
27−import Data.Map.Monoidal (MonoidalMap)
28−import Data.Semigroup (Semigroup (..), (<>))
29−import GHC.Generics
30−import Data.Functor.Identity
31−import Data.Functor.Const
32−import Data.Proxy
33−import Control.Applicative
34−
35−-- | A 'Group' is a 'Monoid' where every element has an inverse.
36−class (Semigroup q, Monoid q) => Group q where
37− negateG :: q -> q
38− (~~) :: q -> q -> q
39− r ~~ s = r <> negateG s
40−
41−-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
42−class Semigroup q => Additive q where
43−
44−-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
45−newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
46−
47−instance Additive p => Patch (AdditivePatch p) where
48− type PatchTarget (AdditivePatch p) = p
49− apply (AdditivePatch p) q = Just $ p <> q
50−
51−instance (Ord k, Group q) => Group (MonoidalMap k q) where
52− negateG = fmap negateG
53−
54−instance (Ord k, Additive q) => Additive (MonoidalMap k q)
55−
56−-- | Trivial group.
57−instance Group () where
58− negateG _ = ()
59− _ ~~ _ = ()
60−instance Additive ()
61−
62−-- | Product group. A Pair of groups gives rise to a group
63−instance (Group a, Group b) => Group (a, b) where
64− negateG (a, b) = (negateG a, negateG b)
65− (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
66−instance (Additive a, Additive b) => Additive (a, b)
67−
68−-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
69−-- Base does not define Monoid (Compose f g a) so this is the best we can
70−-- really do for functor composition.
71−instance Group (f (g a)) => Group ((f :.: g) a) where
72− negateG (Comp1 xs) = Comp1 (negateG xs)
73− Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
74−instance Additive (f (g a)) => Additive ((f :.: g) a)
75−
76−-- | Product of groups, Functor style.
77−instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
78− negateG (a :*: b) = negateG a :*: negateG b
79− (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
80−instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
81−
82−-- | Trivial group, Functor style
83−instance Group (Proxy x) where
84− negateG _ = Proxy
85− _ ~~ _ = Proxy
86−instance Additive (Proxy x)
87−
88−-- | Const lifts groups into a functor.
89−deriving instance Group a => Group (Const a x)
90−instance Additive a => Additive (Const a x)
91−-- | Ideitnty lifts groups pointwise (at only one point)
92−deriving instance Group a => Group (Identity a)
93−instance Additive a => Additive (Identity a)
94−
95−-- | Functions lift groups pointwise.
96−instance Group b => Group (a -> b) where
97− negateG f = negateG . f
98− (~~) = liftA2 (~~)
99−instance Additive b => Additive (a -> b)
src/Reflex/Patch/Class.hsdeleted−34
@@ -1,34 +0,0 @@
1−{-# LANGUAGE TypeFamilies #-}
2−-- | The interface for types which represent changes made to other types
3−module Reflex.Patch.Class where
4−
5−import Control.Monad.Identity
6−import Data.Maybe
7−import Data.Semigroup (Semigroup(..))
8−
9−-- | A 'Patch' type represents a kind of change made to a datastructure.
10−--
11−-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
12−-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
13−class Patch p where
14− type PatchTarget p :: *
15− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
16− -- 'Nothing'.
17− apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
18−
19−-- | Apply a 'Patch'; if it does nothing, return the original value
20−applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
21−applyAlways p t = fromMaybe t $ apply p t
22−
23−-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
24−instance Patch (Identity a) where
25− type PatchTarget (Identity a) = a
26− apply (Identity a) _ = Just a
27−
28−-- | Like '(.)', but composes functions that return patches rather than
29−-- functions that return new values. The Semigroup instance for patches must
30−-- apply patches right-to-left, like '(.)'.
31−composePatchFunctions :: (Patch p, Semigroup p) => (PatchTarget p -> p) -> (PatchTarget p -> p) -> PatchTarget p -> p
32−composePatchFunctions g f a =
33− let fp = f a
34− in g (applyAlways fp a) <> fp
src/Reflex/Patch/DMap.hsdeleted−82
@@ -1,82 +0,0 @@
1−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2−{-# LANGUAGE LambdaCase #-}
3−{-# LANGUAGE PolyKinds #-}
4−{-# LANGUAGE Rank2Types #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE StandaloneDeriving #-}
7−{-# LANGUAGE TypeFamilies #-}
8−-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
9−module Reflex.Patch.DMap where
10−
11−import Reflex.Patch.Class
12−import Reflex.Patch.IntMap
13−import Reflex.Patch.Map
14−
15−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
16−import qualified Data.Dependent.Map as DMap
17−import Data.Functor.Constant
18−import Data.Functor.Misc
19−import qualified Data.IntMap as IntMap
20−import qualified Data.Map as Map
21−import Data.Semigroup (Semigroup (..))
22−import Data.Some (Some)
23−
24−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
25−-- Insertions are represented as @'ComposeMaybe' (Just value)@,
26−-- while deletions are represented as @'ComposeMaybe' Nothing@.
27−newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
28−
29−deriving instance GCompare k => Semigroup (PatchDMap k v)
30−
31−deriving instance GCompare k => Monoid (PatchDMap k v)
32−
33−-- | Apply the insertions or deletions to a given 'DMap'.
34−instance GCompare k => Patch (PatchDMap k v) where
35− type PatchTarget (PatchDMap k v) = DMap k v
36− 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?
37− where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
38− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
39− nothingToJust = \case
40− Nothing -> Just $ Constant ()
41− Just _ -> Nothing
42−
43−-- | Map a function @v a -> v' a@ over any inserts/updates in the given
44−-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
45−mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
46−mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
47−
48−-- | Map an effectful function @v a -> f (v' a)@ over any inserts/updates in the given
49−-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
50−traversePatchDMap :: Applicative f => (forall a. v a -> f (v' a)) -> PatchDMap k v -> f (PatchDMap k v')
51−traversePatchDMap f = traversePatchDMapWithKey $ const f
52−
53−-- | Map an effectful function @k a -> v a -> f (v' a)@ over any inserts/updates
54−-- in the given @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
55−traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
56−traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
57−
58−-- | Weaken a @'PatchDMap' k v@ to a @'PatchMap' (Some k) v'@ using a function
59−-- @v a -> v'@ to weaken each value contained in the patch.
60−weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
61−weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
62−
63−-- | Convert a weak @'PatchDMap' ('Const2' k a) v@ where the @a@ is known by way of
64−-- the @Const2@ into a @'PatchMap' k v'@ using a rank 1 function @v a -> v'@.
65−patchDMapToPatchMapWith :: (v a -> v') -> PatchDMap (Const2 k a) v -> PatchMap k v'
66−patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
67−
68−-- | Convert a @'PatchMap' k v@ into a @'PatchDMap' ('Const2' k a) v'@ using a function @v -> v' a@.
69−const2PatchDMapWith :: forall k v v' a. (v -> v' a) -> PatchMap k v -> PatchDMap (Const2 k a) v'
70−const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
71− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v')
72− g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
73−
74−-- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@.
75−const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
76−const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
77− where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
78− g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
79−
80−-- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'.
81−getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
82−getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Reflex/Patch/DMapWithMove.hsdeleted−363
@@ -1,363 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE PatternGuards #-}
8−{-# LANGUAGE PolyKinds #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−
14−-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
15−-- move values between keys.
16−module Reflex.Patch.DMapWithMove where
17−
18−import Reflex.Patch.Class
19−import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
20−import qualified Reflex.Patch.MapWithMove as MapWithMove
21−
22−import Data.Constraint.Extras
23−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
24−import qualified Data.Dependent.Map as DMap
25−import Data.Functor.Constant
26−import Data.Functor.Misc
27−import Data.Functor.Product
28−import Data.GADT.Compare (GEq (..))
29−import Data.GADT.Show (GShow, gshow)
30−import qualified Data.Map as Map
31−import Data.Maybe
32−import Data.Semigroup (Semigroup (..), (<>))
33−import Data.Some (Some(Some))
34−import Data.These
35−
36−-- | 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
37−-- destinations.
38−--
39−-- Invariants:
40−--
41−-- * A key should not move to itself.
42−-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
43−newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
44−
45−-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
46−-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
47−data NodeInfo k v a = NodeInfo
48− { _nodeInfo_from :: !(From k v a)
49− -- ^Change applying to the current key, be it an insert, move, or delete.
50− , _nodeInfo_to :: !(To k a)
51− -- ^Where this key is moving to, if involved in a move. Should only be @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
52− }
53− deriving (Show)
54−
55−-- |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
56−-- key (@From_Delete@), or moving a key (@From_Move@).
57−data From (k :: a -> *) (v :: a -> *) :: a -> * where
58− -- |Insert a new or update an existing key with the given value @v a@
59− From_Insert :: v a -> From k v a
60− -- |Delete the existing key
61− From_Delete :: From k v a
62− -- |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@,
63− -- usually but not necessarily with @From_Delete@.
64− From_Move :: !(k a) -> From k v a
65− deriving (Show, Read, Eq, Ord)
66−
67−-- |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
68−-- operation.
69−type To = ComposeMaybe
70−
71−-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
72−validPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Bool
73−validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
74−
75−-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
76−validationErrorsForPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> [String]
77−validationErrorsForPatchDMapWithMove m =
78− noSelfMoves <> movesBalanced
79− where
80− noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
81− selfMove (dst :=> NodeInfo (From_Move src) _) | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
82− selfMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
83− selfMove _ = Nothing
84− movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
85− unbalancedMove (dst :=> NodeInfo (From_Move src) _) =
86− case DMap.lookup src m of
87− Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
88− Just (NodeInfo _ (ComposeMaybe (Just dst'))) ->
89− if isNothing (dst' `geq` dst)
90− then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
91− else Nothing
92− _ ->
93− Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
94− unbalancedMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) =
95− case DMap.lookup dst m of
96− Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
97− Just (NodeInfo (From_Move src') _) ->
98− if isNothing (src' `geq` src)
99− then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
100− else Nothing
101−
102− _ ->
103− Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
104− unbalancedMove _ = Nothing
105−
106−-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
107−instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
108− PatchDMapWithMove a == PatchDMapWithMove b = a == b
109−
110−-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
111−data Pair1 f g a = Pair1 (f a) (g a)
112−
113−-- |Helper data structure used for composing patches using the monoid instance.
114−data Fixup k v a
115− = Fixup_Delete
116− | Fixup_Update (These (From k v a) (To k a))
117−
118−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
119−instance GCompare k => Semigroup (PatchDMapWithMove k v) where
120− PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
121− where
122− connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
123− h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
124− h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of
125− (Just toAfter, From_Move fromBefore)
126− | isJust $ fromBefore `geq` toAfter
127− -> [toAfter :=> Fixup_Delete]
128− | otherwise
129− -> [ toAfter :=> Fixup_Update (This editBefore)
130− , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))
131− ]
132− (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
133− (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
134− (Nothing, _) -> []
135− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
136− mergeFixups _ (Fixup_Update a) (Fixup_Update b)
137− | This x <- a, That y <- b
138− = Fixup_Update $ These x y
139− | That y <- a, This x <- b
140− = Fixup_Update $ These x y
141− mergeFixups _ _ _ = error "PatchDMapWithMove: incompatible fixups"
142− fixups = DMap.fromListWithKey mergeFixups $ concatMap h connections
143− combineNodeInfos _ nia nib = NodeInfo
144− { _nodeInfo_from = _nodeInfo_from nia
145− , _nodeInfo_to = _nodeInfo_to nib
146− }
147− applyFixup _ ni = \case
148− Fixup_Delete -> Nothing
149− Fixup_Update u -> Just $ NodeInfo
150− { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
151− , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
152− }
153− m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
154− getHere :: These a b -> Maybe a
155− getHere = \case
156− This a -> Just a
157− These a _ -> Just a
158− That _ -> Nothing
159− getThere :: These a b -> Maybe b
160− getThere = \case
161− This _ -> Nothing
162− These _ b -> Just b
163− That b -> Just b
164−
165−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
166−instance GCompare k => Monoid (PatchDMapWithMove k v) where
167− mempty = PatchDMapWithMove mempty
168− mappend = (<>)
169−
170−{-
171−mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
172−PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
173− where
174− 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
175− removeRedundantDst toKey (From_Move fromKey) | isJust (toKey `geq` fromKey) = Nothing
176− removeRedundantDst _ a = Just a
177− f :: forall a. k a -> From k v a -> Maybe (From k v a)
178− f toKey _ = removeRedundantDst toKey $ case getDstAction toKey dstAfter of
179− From_Move fromKey -> getDstAction fromKey dstBefore
180− nonMove -> nonMove
181− dst = DMap.mapMaybeWithKey f $ DMap.union dstAfter dstBefore
182− getSrcAction k m = fromMaybe (ComposeMaybe $ Just k) $ DMap.lookup k m
183− removeRedundantSrc fromKey (ComposeMaybe (Just toKey)) | isJust (fromKey `geq` toKey) = Nothing
184− removeRedundantSrc _ a = Just a
185− g :: forall a. k a -> ComposeMaybe k a -> Maybe (ComposeMaybe k a)
186− g fromKey _ = removeRedundantSrc fromKey $ case getSrcAction fromKey srcBefore of
187− ComposeMaybe Nothing -> ComposeMaybe Nothing
188− ComposeMaybe (Just toKeyBefore) -> getSrcAction toKeyBefore srcAfter
189− src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
190−-}
191−
192−-- |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'.
193−insertDMapKey :: k a -> v a -> PatchDMapWithMove k v
194−insertDMapKey k v =
195− PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing)
196−
197−-- |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:
198−--
199−-- @
200−-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
201−-- @
202−moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
203−moveDMapKey src dst = case src `geq` dst of
204− Nothing -> PatchDMapWithMove $ DMap.fromList
205− [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing)
206− , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
207− ]
208− Just _ -> mempty
209−
210−-- |Make a @'PatchDMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
211−--
212−-- @
213−-- let aMay = DMap.lookup a dmap
214−-- bMay = DMap.lookup b dmap
215−-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
216−-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
217−-- . DMap.delete a . DMap.delete b $ dmap
218−-- @
219−swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
220−swapDMapKey src dst = case src `geq` dst of
221− Nothing -> PatchDMapWithMove $ DMap.fromList
222− [ dst :=> NodeInfo (From_Move src) (ComposeMaybe $ Just src)
223− , src :=> NodeInfo (From_Move dst) (ComposeMaybe $ Just dst)
224− ]
225− Just _ -> mempty
226−
227−-- |Make a @'PatchDMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'DMap.delete'.
228−deleteDMapKey :: k a -> PatchDMapWithMove k v
229−deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete $ ComposeMaybe Nothing
230−
231−{-
232−k1, k2 :: Const2 Int () ()
233−k1 = Const2 1
234−k2 = Const2 2
235−p1, p2 :: PatchDMapWithMove (Const2 Int ()) Identity
236−p1 = moveDMapKey k1 k2
237−p2 = moveDMapKey k2 k1
238−p12 = p1 <> p2
239−p21 = p2 <> p1
240−p12Slow = p1 `mappendPatchDMapWithMoveSlow` p2
241−p21Slow = p2 `mappendPatchDMapWithMoveSlow` p1
242−
243−testPatchDMapWithMove = do
244− print p1
245− print p2
246− print $ p12 == deleteDMapKey k1
247− print $ p21 == deleteDMapKey k2
248− print $ p12Slow == deleteDMapKey k1
249− print $ p21Slow == deleteDMapKey k2
250−
251−dst (PatchDMapWithMove x _) = x
252−src (PatchDMapWithMove _ x) = x
253−-}
254−
255−-- |Extract the 'DMap' representing the patch changes from the 'PatchDMapWithMove'.
256−unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
257−unPatchDMapWithMove (PatchDMapWithMove p) = p
258−
259−-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove', without checking any invariants.
260−--
261−-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithMove' are preserved; they will not be checked.
262−unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
263−unsafePatchDMapWithMove = PatchDMapWithMove
264−
265−-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while checking invariants. If the invariants are satisfied, @Right p@ is returned
266−-- otherwise @Left errors@.
267−patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
268−patchDMapWithMove dm =
269− case validationErrorsForPatchDMapWithMove dm of
270− [] -> Right $ unsafePatchDMapWithMove dm
271− errs -> Left errs
272−
273−-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
274−mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
275−mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
276− DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
277− where g :: forall a. From k v a -> From k v' a
278− g = \case
279− From_Insert v -> From_Insert $ f v
280− From_Delete -> From_Delete
281− From_Move k -> From_Move k
282−
283−-- |Traverse an effectful function @forall a. v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
284−traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
285−traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
286−
287−-- |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')@.
288−traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
289−traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
290− where g :: forall a. k a -> From k v a -> m (From k v' a)
291− g k = \case
292− From_Insert v -> From_Insert <$> f k v
293− From_Delete -> pure From_Delete
294− From_Move fromKey -> pure $ From_Move fromKey
295−
296−-- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@.
297−nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
298−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
299−
300−-- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@.
301−nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
302−nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
303−
304−-- |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
305−-- values.
306−weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
307−weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
308− where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v'
309− g ni = MapWithMove.NodeInfo
310− { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
311− From_Insert v -> MapWithMove.From_Insert $ f v
312− From_Delete -> MapWithMove.From_Delete
313− From_Move k -> MapWithMove.From_Move $ Some k
314− , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
315− }
316−
317−-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
318−-- dependency in the typing and all points are already @a@, hence the function to map each value to @v'@ is not higher rank.
319−patchDMapWithMoveToPatchMapWithMoveWith :: forall k v v' a. (v a -> v') -> PatchDMapWithMove (Const2 k a) v -> PatchMapWithMove k v'
320−patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
321− where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k v'
322− g ni = MapWithMove.NodeInfo
323− { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
324− From_Insert v -> MapWithMove.From_Insert $ f v
325− From_Delete -> MapWithMove.From_Delete
326− From_Move (Const2 k) -> MapWithMove.From_Move k
327− , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
328− }
329−
330−-- |"Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove ('Const2' k a)@; that is, turn a non-dependently-typed patch into a dependently typed
331−-- one but which always has a constant key type represented by 'Const2'. Apply the given function to each @v@ to produce a @v' a@.
332−-- Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
333−const2PatchDMapWithMoveWith :: forall k v v' a. (v -> v' a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) v'
334−const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
335− where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
336− g (k, ni) = Const2 k :=> NodeInfo
337− { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
338− MapWithMove.From_Insert v -> From_Insert $ f v
339− MapWithMove.From_Delete -> From_Delete
340− MapWithMove.From_Move fromKey -> From_Move $ Const2 fromKey
341− , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
342− }
343−
344−-- | Apply the insertions, deletions, and moves to a given 'DMap'.
345−instance GCompare k => Patch (PatchDMapWithMove k v) where
346− type PatchTarget (PatchDMapWithMove k v) = DMap k v
347− 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?
348− where insertions = DMap.mapMaybeWithKey insertFunc p
349− insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a)
350− insertFunc _ ni = case _nodeInfo_from ni of
351− From_Insert v -> Just v
352− From_Move k -> DMap.lookup k old
353− From_Delete -> Nothing
354− deletions = DMap.mapMaybeWithKey deleteFunc p
355− deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a)
356− deleteFunc _ ni = case _nodeInfo_from ni of
357− From_Delete -> Just $ Constant ()
358− _ -> Nothing
359−
360−-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
361−getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
362−getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
363− where f _ v ni = Pair v $ _nodeInfo_to ni
src/Reflex/Patch/IntMap.hsdeleted−59
@@ -1,59 +0,0 @@
1−{-# LANGUAGE DeriveFoldable #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE DeriveTraversable #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE TypeFamilies #-}
6−-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
7−-- insert/update or delete of associations.
8−module Reflex.Patch.IntMap where
9−
10−import Data.IntMap.Strict (IntMap)
11−import qualified Data.IntMap.Strict as IntMap
12−import Data.Maybe
13−import Data.Semigroup
14−import Reflex.Patch.Class
15−
16−-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
17−-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
18−-- and @Nothing@ means delete.
19−newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
20−
21−-- | Apply the insertions or deletions to a given 'IntMap'.
22−instance Patch (PatchIntMap a) where
23− type PatchTarget (PatchIntMap a) = IntMap a
24− apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
25− let removes = IntMap.filter isNothing p
26− adds = IntMap.mapMaybe id p
27− in IntMap.union adds $ v `IntMap.difference` removes
28−
29−-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
30−-- If the same key is modified by both patches, the one on the left will take
31−-- precedence.
32−instance Semigroup (PatchIntMap v) where
33− PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
34− -- PatchMap is idempotent, so stimes n is id for every n
35− stimes = stimesIdempotentMonoid
36−
37−-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
38−-- (that is, all inserts/updates), producing a @PatchIntMap b@.
39−mapIntMapPatchWithKey :: (Int -> a -> b) -> PatchIntMap a -> PatchIntMap b
40−mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k mv -> f k <$> mv) m
41−
42−-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
43−-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
44−traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
45−traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
46−
47−-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
48−patchIntMapNewElements :: PatchIntMap a -> [a]
49−patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
50−
51−-- | Convert the given @'PatchIntMap' a@ into an @'IntMap' a@ with all
52−-- the inserts/updates in the given patch.
53−patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
54−patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
55−
56−-- | Subset the given @'IntMap' a@ to contain only the keys that would be
57−-- deleted by the given @'PatchIntMap' a@.
58−getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
59−getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Reflex/Patch/Map.hsdeleted−55
@@ -1,55 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−{-# LANGUAGE TypeFamilies #-}
3−-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
4−-- and deletions
5−module Reflex.Patch.Map where
6−
7−import Reflex.Patch.Class
8−
9−import Data.Map (Map)
10−import qualified Data.Map as Map
11−import Data.Maybe
12−import Data.Semigroup
13−
14−-- | A set of changes to a 'Map'. Any element may be inserted/updated or
15−-- deleted. Insertions are represented as values wrapped in 'Just', while
16−-- deletions are represented as 'Nothing's
17−newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
18− deriving (Show, Read, Eq, Ord)
19−
20−-- | Apply the insertions or deletions to a given 'Map'.
21−instance Ord k => Patch (PatchMap k v) where
22− type PatchTarget (PatchMap k v) = Map k v
23− {-# INLINABLE apply #-}
24− 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?
25− where insertions = Map.mapMaybeWithKey (const id) p
26− deletions = Map.mapMaybeWithKey (const nothingToJust) p
27− nothingToJust = \case
28− Nothing -> Just ()
29− Just _ -> Nothing
30−
31−-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
32−-- If the same key is modified by both patches, the one on the left will take
33−-- precedence.
34−instance Ord k => Semigroup (PatchMap k v) where
35− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
36− -- PatchMap is idempotent, so stimes n is id for every n
37− stimes = stimesIdempotentMonoid
38−
39−-- | The empty 'PatchMap' contains no insertions or deletions
40−instance Ord k => Monoid (PatchMap k v) where
41− mempty = PatchMap mempty
42− mappend = (<>)
43−
44−-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
45−-- Deletions are unaffected.
46−instance Functor (PatchMap k) where
47− fmap f = PatchMap . fmap (fmap f) . unPatchMap
48−
49−-- | Returns all the new elements that will be added to the 'Map'
50−patchMapNewElements :: PatchMap k v -> [v]
51−patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
52−
53−-- | Returns all the new elements that will be added to the 'Map'
54−patchMapNewElementsMap :: PatchMap k v -> Map k v
55−patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
src/Reflex/Patch/MapWithMove.hsdeleted−270
@@ -1,270 +0,0 @@
1−{-# LANGUAGE DeriveFoldable #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE DeriveTraversable #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE PatternGuards #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeFamilies #-}
9−-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
10−-- another
11−module Reflex.Patch.MapWithMove where
12−
13−import Reflex.Patch.Class
14−
15−import Control.Arrow
16−import Control.Monad.State
17−import Data.Foldable
18−import Data.Function
19−import Data.List
20−import Data.Map (Map)
21−import qualified Data.Map as Map
22−import Data.Maybe
23−import Data.Semigroup (Semigroup (..), (<>))
24−import qualified Data.Set as Set
25−import Data.These (These(..))
26−import Data.Tuple
27−
28−-- | Patch a DMap with additions, deletions, and moves. Invariant: If key @k1@
29−-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
30−-- and vice versa. There should never be any unpaired From/To keys.
31−newtype PatchMapWithMove k v = PatchMapWithMove (Map k (NodeInfo k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
32−
33−-- | Holds the information about each key: where its new value should come from,
34−-- and where its old value should go to
35−data NodeInfo k v = NodeInfo
36− { _nodeInfo_from :: !(From k v)
37− -- ^ Where do we get the new value for this key?
38− , _nodeInfo_to :: !(To k)
39− -- ^ If the old value is being kept (i.e. moved rather than deleted or
40− -- replaced), where is it going?
41− }
42− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
43−
44−-- | Describe how a key's new value should be produced
45−data From k v
46− = From_Insert v -- ^ Insert the given value here
47− | From_Delete -- ^ Delete the existing value, if any, from here
48− | From_Move !k -- ^ Move the value here from the given key
49− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
50−
51−-- | Describe where a key's old value will go. If this is 'Just', that means
52−-- the key's old value will be moved to the given other key; if it is 'Nothing',
53−-- that means it will be deleted.
54−type To = Maybe
55−
56−-- | Create a 'PatchMapWithMove', validating it
57−patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
58−patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
59− where valid = forwardLinks == backwardLinks
60− forwardLinks = Map.mapMaybe _nodeInfo_to m
61− backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, v) ->
62− case _nodeInfo_from v of
63− From_Move from -> Just (from, to)
64− _ -> Nothing
65−
66−-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
67−patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
68−patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
69− { _nodeInfo_from = From_Insert v
70− , _nodeInfo_to = Nothing
71− }
72−
73−-- | Extract the internal representation of the 'PatchMapWithMove'
74−unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
75−unPatchMapWithMove (PatchMapWithMove p) = p
76−
77−-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
78−insertMapKey :: k -> v -> PatchMapWithMove k v
79−insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
80−
81−-- |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:
82−--
83−-- @
84−-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
85−-- @
86−moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
87−moveMapKey src dst
88− | src == dst = mempty
89− | otherwise =
90− PatchMapWithMove $ Map.fromList
91− [ (dst, NodeInfo (From_Move src) Nothing)
92− , (src, NodeInfo From_Delete (Just dst))
93− ]
94−
95−-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
96−--
97−-- @
98−-- let aMay = Map.lookup a map
99−-- bMay = Map.lookup b map
100−-- in maybe id (Map.insert a) (bMay `mplus` aMay)
101−-- . maybe id (Map.insert b) (aMay `mplus` bMay)
102−-- . Map.delete a . Map.delete b $ map
103−-- @
104−swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
105−swapMapKey src dst
106− | src == dst = mempty
107− | otherwise =
108− PatchMapWithMove $ Map.fromList
109− [ (dst, NodeInfo (From_Move src) (Just src))
110− , (src, NodeInfo (From_Move dst) (Just dst))
111− ]
112−
113−-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
114−deleteMapKey :: k -> PatchMapWithMove k v
115−deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete Nothing
116−
117−-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
118−--
119−-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
120−unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
121−unsafePatchMapWithMove = PatchMapWithMove
122−
123−-- | Apply the insertions, deletions, and moves to a given 'Map'
124−instance Ord k => Patch (PatchMapWithMove k v) where
125− type PatchTarget (PatchMapWithMove k v) = Map k v
126− 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?
127− where insertions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
128− From_Insert v -> Just v
129− From_Move k -> Map.lookup k old
130− From_Delete -> Nothing
131− deletions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
132− From_Delete -> Just ()
133− _ -> Nothing
134−
135−-- | Returns all the new elements that will be added to the 'Map'.
136−patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
137−patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
138−
139−-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
140−patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
141−patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
142− where f ni = case _nodeInfo_from ni of
143− From_Insert v -> Just v
144− From_Move _ -> Nothing
145− From_Delete -> Nothing
146−
147−-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
148−-- its values using the given ordering function. The set keys of the 'Map' is
149−-- not changed.
150−patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
151−patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
152− where unsorted = Map.toList m
153− sorted = sortBy (cmp `on` snd) unsorted
154− f (to, _) (from, _) = if to == from then Nothing else
155− Just (from, to)
156− reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
157− g (to, _) (from, _) = if to == from then Nothing else
158− let Just movingTo = Map.lookup to reverseMapping
159− in Just (to, NodeInfo (From_Move from) $ Just movingTo)
160−
161−-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
162−-- will produce a 'Map' with the same values as the second 'Map' but with the
163−-- values sorted with the given ordering function.
164−patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
165−patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
166− where newList = Map.toList newByIndexUnsorted
167− newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
168−
169−-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
170−-- will produce the second 'Map'.
171−patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
172−patchThatChangesMap oldByIndex newByIndex = patch
173− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
174− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
175− let f k v = do
176− remainingValues <- get
177− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
178− then Map.delete v remainingValues
179− else Map.insert v remainingKeys remainingValues
180− case Map.lookup v remainingValues of
181− Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
182− Just fromKs ->
183− if k `Set.member` fromKs
184− then do
185− putRemainingKeys $ Set.delete k fromKs
186− return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
187− else do
188− (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
189− putRemainingKeys remainingKeys
190− return $ NodeInfo (From_Move fromK) $ Just undefined
191− Map.traverseWithKey f newByIndex
192− unusedOldKeys = fold unusedValuesByValue
193− pointlessMove k = \case
194− From_Move k' | k == k' -> True
195− _ -> False
196− keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
197− then Just undefined
198− else Nothing
199− 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
200−
201−-- | Change the 'From' value of a 'NodeInfo'
202−nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
203−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
204−
205−-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
206−-- 'Applicative', 'Monad', etc.) action to get the new value
207−nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
208−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
209−
210−-- | Set the 'To' field of a 'NodeInfo'
211−nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
212−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
213−
214−-- |Helper data structure used for composing patches using the monoid instance.
215−data Fixup k v
216− = Fixup_Delete
217− | Fixup_Update (These (From k v) (To k))
218−
219−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
220−instance Ord k => Semigroup (PatchMapWithMove k v) where
221− PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m
222− where
223− connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
224− h :: (k, (Maybe k, From k v)) -> [(k, Fixup k v)]
225− h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
226− (Just toAfter, From_Move fromBefore)
227− | fromBefore == toAfter
228− -> [(toAfter, Fixup_Delete)]
229− | otherwise
230− -> [ (toAfter, Fixup_Update (This editBefore))
231− , (fromBefore, Fixup_Update (That mToAfter))
232− ]
233− (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
234− (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
235− (Nothing, _) -> []
236− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
237− mergeFixups _ (Fixup_Update a) (Fixup_Update b)
238− | This x <- a, That y <- b
239− = Fixup_Update $ These x y
240− | That y <- a, This x <- b
241− = Fixup_Update $ These x y
242− mergeFixups _ _ _ = error "PatchMapWithMove: incompatible fixups"
243− fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
244− combineNodeInfos _ nia nib = NodeInfo
245− { _nodeInfo_from = _nodeInfo_from nia
246− , _nodeInfo_to = _nodeInfo_to nib
247− }
248− applyFixup _ ni = \case
249− Fixup_Delete -> Nothing
250− Fixup_Update u -> Just $ NodeInfo
251− { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
252− , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
253− }
254− m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
255− getHere :: These a b -> Maybe a
256− getHere = \case
257− This a -> Just a
258− These a _ -> Just a
259− That _ -> Nothing
260− getThere :: These a b -> Maybe b
261− getThere = \case
262− This _ -> Nothing
263− These _ b -> Just b
264− That b -> Just b
265−
266−--TODO: Figure out how to implement this in terms of PatchDMapWithMove rather than duplicating it here
267−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
268−instance Ord k => Monoid (PatchMapWithMove k v) where
269− mempty = PatchMapWithMove mempty
270− mappend = (<>)
src/Reflex/Query/Base.hs+1−1
@@ -47,7 +47,7 @@ import Reflex.DynamicWriter.Class
47import Reflex.EventWriter.Base
47import Reflex.EventWriter.Base
48import Reflex.EventWriter.Class
48import Reflex.EventWriter.Class
49import Reflex.Host.Class
49import Reflex.Host.Class
50−import qualified Reflex.Patch.MapWithMove as MapWithMove
50+import qualified Data.Patch.MapWithMove as MapWithMove
51import Reflex.PerformEvent.Class
51import Reflex.PerformEvent.Class
52import Reflex.PostBuild.Class
52import Reflex.PostBuild.Class
53import Reflex.Query.Class
53import Reflex.Query.Class
src/Reflex/Spider/Internal.hs+2−2
@@ -104,8 +104,8 @@ import qualified Reflex.Class
104import qualified Reflex.Class as R
104import qualified Reflex.Class as R
105import qualified Reflex.Host.Class
105import qualified Reflex.Host.Class
106import Reflex.NotReady.Class
106import Reflex.NotReady.Class
107−import Reflex.Patch
107+import Data.Patch
108−import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
108+import qualified Data.Patch.DMapWithMove as PatchDMapWithMove
109import Reflex.PerformEvent.Base (PerformEventT)
109import Reflex.PerformEvent.Base (PerformEventT)
110
110
111#ifdef DEBUG_TRACE_EVENTS
111#ifdef DEBUG_TRACE_EVENTS
src/Reflex/Widget/Basic.hs+1−1
@@ -13,7 +13,7 @@ import Data.Map (Map)
13
13
14import Reflex.Class
14import Reflex.Class
15import Reflex.Adjustable.Class
15import Reflex.Adjustable.Class
16−import Reflex.Patch.MapWithMove
16+import Data.Patch.MapWithMove
17
17
18
18
19-- | Build sortable content in such a way that re-sorting it can cause minimal
19-- | Build sortable content in such a way that re-sorting it can cause minimal
test/GC.hs+1−1
@@ -20,7 +20,7 @@ import Data.Semigroup
20import Data.These
20import Data.These
21
21
22import Data.Functor.Misc
22import Data.Functor.Misc
23−import Reflex.Patch
23+import Data.Patch
24
24
25import qualified Reflex.Host.Class as Host
25import qualified Reflex.Host.Class as Host
26import qualified Reflex.Spider.Internal as S
26import qualified Reflex.Spider.Internal as S
test/QueryT.hs+1−1
@@ -24,7 +24,7 @@ import Data.These.Lens
24#endif
24#endif
25
25
26import Reflex
26import Reflex
27−import Reflex.Patch.MapWithMove
27+import Data.Patch.MapWithMove
28import Test.Run
28import Test.Run
29
29
30newtype MyQuery = MyQuery SelectedCount
30newtype MyQuery = MyQuery SelectedCount