on develop help sign in
Renames MonadAdjust and moves it to its own module.
commit71efa5f4c025e6f81afa29c572f8845d845db01e authorDave Laing <dave.laing.80@gmail.com> dateSat 8 Jul 2017, 17:48 (9 years ago) parentd9ef845
17 files changed+382−35
reflex.cabal+2 src/Data/Map/Misc.hs+55 src/Reflex.hs+1 src/Reflex/Adjustable/Class.hs+166 src/Reflex/Class.hs+18−8 src/Reflex/Collection.hs+121 src/Reflex/DynamicWriter.hs+2−2 src/Reflex/EventWriter.hs+3−3 src/Reflex/PerformEvent/Base.hs+1−1 src/Reflex/PostBuild/Base.hs+1−1 src/Reflex/Query/Base.hs+1−1 src/Reflex/Requester/Base.hs+2−2 src/Reflex/TriggerEvent/Base.hs+1−1 src/Reflex/Widget/Basic.hs+1−1 test/EventWriterT.hs+1−1 test/QueryT.hs+4−12 test/RequesterT.hs+2−2
reflex.cabal+2
@@ -79,12 +79,14 @@ library
7979
8080 exposed-modules:
8181 Data.AppendMap,
82+ Data.Map.Misc,
8283 Data.Functor.Misc,
8384 Data.WeakBag,
8485 Data.FastWeakBag,
8586 Data.FastMutableIntMap,
8687 Reflex,
8788 Reflex.Class,
89+ Reflex.Collection,
8890 Reflex.EventWriter,
8991 Reflex.Dynamic,
9092 Reflex.Dynamic.Uniq,
src/Data/Map/Misc.hsnew file+55
@@ -0,0 +1,55 @@
1+{-# LANGUAGE LambdaCase #-}
2+module Data.Map.Misc
3+ (
4+ -- * Working with Maps
5+ diffMapNoEq
6+ , diffMap
7+ , applyMap
8+ , mapPartitionEithers
9+ , applyMapKeysSet
10+ ) where
11+
12+import Data.Align
13+import Data.Either
14+import Data.Map (Map)
15+import qualified Data.Map as Map
16+import Data.Maybe
17+import Data.Set (Set)
18+import qualified Data.Set as Set
19+import Data.These
20+
21+diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
22+diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
23+ This _ -> Just Nothing
24+ These _ new -> Just $ Just new
25+ That new -> Just $ Just new
26+
27+diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
28+diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
29+ This _ -> Just Nothing
30+ These old new
31+ | old == new -> Nothing
32+ | otherwise -> Just $ Just new
33+ That new -> Just $ Just new
34+
35+applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
36+applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
37+ where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
38+ maybeToEither = \case
39+ Nothing -> Left ()
40+ Just r -> Right r
41+
42+mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
43+mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs)
44+ where (ls, rs) = Map.partition isLeft m
45+ fromLeft (Left l) = l
46+ fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible"
47+ fromRight (Right r) = r
48+ fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible"
49+
50+-- | Apply a map patch to a set
51+-- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m)
52+applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
53+applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
54+ where (insertions, deletions) = Map.partition isJust patch
55+
src/Reflex.hs+1
@@ -6,6 +6,7 @@ module Reflex
66 ) where
77
88import Reflex.Class as X
9+import Reflex.Collection as X
910import Reflex.EventWriter as X
1011import Reflex.Dynamic as X
1112#ifdef USE_TEMPLATE_HASKELL
src/Reflex/Adjustable/Class.hsnew file+166
@@ -0,0 +1,166 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE RankNTypes #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE RecursiveDo #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE FlexibleInstances #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+#ifdef USE_REFLEX_OPTIMIZER
12+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
13+#endif
14+module Reflex.Adjustable.Class
15+ (
16+ -- * The Adjustable typeclass
17+ Adjustable(..)
18+ , sequenceDMapWithAdjust
19+ , sequenceDMapWithAdjustWithMove
20+ , mapMapWithAdjustWithMove
21+ -- * Widgets on Collections
22+ , listHoldWithKey
23+ , listWithKey
24+ , listWithKey'
25+ , listWithKeyShallowDiff
26+ , listViewWithKey
27+ , selectViewListWithKey
28+ , selectViewListWithKey_
29+ -- * List Utils
30+ , list
31+ , simpleList
32+ ) where
33+
34+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
35+import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
36+import Data.Align
37+import Data.Map (Map)
38+import qualified Data.Map as Map
39+import Data.Dependent.Map (DMap, GCompare (..))
40+import Data.Functor.Constant
41+import Data.Functor.Misc
42+import Data.Map.Misc
43+import Data.These
44+
45+import Reflex.Class
46+import Reflex.Dynamic
47+import Reflex.PostBuild.Class
48+
49+-- | A 'Monad' that supports adjustment over time. After an action has been
50+-- run, if the given events fire, it will adjust itself so that its net effect
51+-- is as though it had originally been run with the new value. Note that there
52+-- is some issue here with persistent side-effects: obviously, IO (and some
53+-- other side-effects) cannot be undone, so it is up to the instance implementer
54+-- to determine what the best meaning for this class is in such cases.
55+class (Reflex t, Monad m) => Adjustable t m | m -> t where
56+ runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
57+ traverseIntMapWithKeyWithAdjust :: (IntMap.Key -> v -> m v') -> IntMap v -> Event t (PatchIntMap v) -> m (IntMap v', Event t (PatchIntMap v'))
58+ traverseDMapWithKeyWithAdjust :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> m (DMap k v', Event t (PatchDMap k v'))
59+ traverseDMapWithKeyWithAdjustWithMove :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> m (DMap k v', Event t (PatchDMapWithMove k v'))
60+
61+instance Adjustable t m => Adjustable t (ReaderT r m) where
62+ runWithReplace a0 a' = do
63+ r <- ask
64+ lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
65+ traverseIntMapWithKeyWithAdjust f dm0 dm' = do
66+ r <- ask
67+ lift $ traverseIntMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
68+ traverseDMapWithKeyWithAdjust f dm0 dm' = do
69+ r <- ask
70+ lift $ traverseDMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
71+ traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
72+ r <- ask
73+ lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
74+
75+sequenceDMapWithAdjust :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
76+sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
77+
78+sequenceDMapWithAdjustWithMove :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
79+sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
80+
81+mapMapWithAdjustWithMove :: forall t m k v v'. (Adjustable t m, Ord k) => (k -> v -> m v') -> Map k v -> Event t (PatchMapWithMove k v) -> m (Map k v', Event t (PatchMapWithMove k v'))
82+mapMapWithAdjustWithMove f m0 m' = do
83+ (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
84+ return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
85+
86+listHoldWithKey :: forall t m k v a. (Ord k, Adjustable t m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
87+listHoldWithKey m0 m' f = do
88+ let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
89+ dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
90+ (a0, a') <- sequenceDMapWithAdjust dm0 dm'
91+ fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time
92+
93+--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
94+listWithKey :: forall t k v m a. (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
95+listWithKey vals mkChild = do
96+ postBuild <- getPostBuild
97+ let childValChangedSelector = fanMap $ updated vals
98+ -- We keep track of changes to children values in the mkChild function we pass to listHoldWithKey
99+ -- The other changes we need to keep track of are child insertions and deletions. diffOnlyKeyChanges
100+ -- keeps track of insertions and deletions but ignores value changes, since they're already accounted for.
101+ diffOnlyKeyChanges olds news = flip Map.mapMaybe (align olds news) $ \case
102+ This _ -> Just Nothing
103+ These _ _ -> Nothing
104+ That new -> Just $ Just new
105+ rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
106+ let changeVals :: Event t (Map k (Maybe v))
107+ changeVals = attachWith diffOnlyKeyChanges (current sentVals) $ leftmost
108+ [ updated vals
109+ , tag (current vals) postBuild --TODO: This should probably be added to the attachWith, not to the updated; if we were using diffMap instead of diffMapNoEq, I think it might not work
110+ ]
111+ listHoldWithKey Map.empty changeVals $ \k v ->
112+ mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
113+
114+{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
115+listWithKey' :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
116+listWithKey' = listWithKeyShallowDiff
117+
118+-- | Display the given map of items (in key order) using the builder function provided, and update it with the given event. 'Nothing' update entries will delete the corresponding children, and 'Just' entries will create them if they do not exist or send an update event to them if they do.
119+listWithKeyShallowDiff :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
120+listWithKeyShallowDiff initialVals valsChanged mkChild = do
121+ let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
122+ sentVals <- foldDyn applyMap Map.empty $ fmap (fmap void) valsChanged
123+ let relevantPatch patch _ = case patch of
124+ Nothing -> Just Nothing -- Even if we let a Nothing through when the element doesn't already exist, this doesn't cause a problem because it is ignored
125+ Just _ -> Nothing -- We don't want to let spurious re-creations of items through
126+ listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v ->
127+ mkChild k v $ select childValChangedSelector $ Const2 k
128+
129+--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
130+-- | Create a dynamically-changing set of Event-valued widgets.
131+-- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'.
132+listViewWithKey :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
133+listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild
134+
135+listViewWithKey' :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
136+listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild
137+
138+-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
139+selectViewListWithKey :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
140+ => Dynamic t k -- ^ Current selection key
141+ -> Dynamic t (Map k v) -- ^ Dynamic key/value map
142+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
143+ -> m (Event t (k, a)) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
144+selectViewListWithKey selection vals mkChild = do
145+ let selectionDemux = demux selection -- For good performance, this value must be shared across all children
146+ selectChild <- listWithKey vals $ \k v -> do
147+ let selected = demuxed selectionDemux k
148+ selectSelf <- mkChild k v selected
149+ return $ fmap ((,) k) selectSelf
150+ return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
151+
152+selectViewListWithKey_ :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
153+ => Dynamic t k -- ^ Current selection key
154+ -> Dynamic t (Map k v) -- ^ Dynamic key/value map
155+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
156+ -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
157+selectViewListWithKey_ selection vals mkChild = fmap fst <$> selectViewListWithKey selection vals mkChild
158+
159+-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
160+-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
161+list :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
162+list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
163+
164+-- | Create a dynamically-changing set of widgets from a Dynamic list.
165+simpleList :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
166+simpleList xs mkChild = fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap (Map.fromList . zip [(1::Int)..]) xs
src/Reflex/Class.hs+18−8
@@ -1,3 +1,4 @@
1+{-# LANGUAGE ConstraintKinds #-}
12{-# LANGUAGE CPP #-}
23{-# LANGUAGE DefaultSignatures #-}
34{-# LANGUAGE FlexibleContexts #-}
@@ -120,13 +121,15 @@ module Reflex.Class
120121 , ffilter
121122 , filterLeft
122123 , filterRight
123− -- * Miscellaneous convenience functions
124− , ffor
125− , MonadAdjust (..)
124+ -- * 'Adjustable'
125+ , Adjustable(..)
126126 , sequenceDMapWithAdjust
127127 , sequenceDMapWithAdjustWithMove
128128 , mapMapWithAdjustWithMove
129+ -- * Miscellaneous convenience functions
130+ , ffor
129131 -- * Deprecated functions
132+ , MonadAdjust
130133 , appendEvents
131134 , onceE
132135 , sequenceThese
@@ -1154,19 +1157,23 @@ infixl 4 <@>
11541157(<@) = tag
11551158infixl 4 <@
11561159
1160+------------------
1161+-- Adjustable
1162+------------------
1163+
11571164-- | A 'Monad' that supports adjustment over time. After an action has been
11581165-- run, if the given events fire, it will adjust itself so that its net effect
11591166-- is as though it had originally been run with the new value. Note that there
11601167-- is some issue here with persistent side-effects: obviously, IO (and some
11611168-- other side-effects) cannot be undone, so it is up to the instance implementer
11621169-- to determine what the best meaning for this class is in such cases.
1163−class (Reflex t, Monad m) => MonadAdjust t m | m -> t where
1170+class (Reflex t, Monad m) => Adjustable t m | m -> t where
11641171 runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
11651172 traverseIntMapWithKeyWithAdjust :: (IntMap.Key -> v -> m v') -> IntMap v -> Event t (PatchIntMap v) -> m (IntMap v', Event t (PatchIntMap v'))
11661173 traverseDMapWithKeyWithAdjust :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> m (DMap k v', Event t (PatchDMap k v'))
11671174 traverseDMapWithKeyWithAdjustWithMove :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> m (DMap k v', Event t (PatchDMapWithMove k v'))
11681175
1169−instance MonadAdjust t m => MonadAdjust t (ReaderT r m) where
1176+instance Adjustable t m => Adjustable t (ReaderT r m) where
11701177 runWithReplace a0 a' = do
11711178 r <- ask
11721179 lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
@@ -1180,13 +1187,13 @@ instance MonadAdjust t m => MonadAdjust t (ReaderT r m) where
11801187 r <- ask
11811188 lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
11821189
1183−sequenceDMapWithAdjust :: (GCompare k, MonadAdjust t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
1190+sequenceDMapWithAdjust :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
11841191sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
11851192
1186−sequenceDMapWithAdjustWithMove :: (GCompare k, MonadAdjust t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
1193+sequenceDMapWithAdjustWithMove :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
11871194sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
11881195
1189−mapMapWithAdjustWithMove :: forall t m k v v'. (MonadAdjust t m, Ord k) => (k -> v -> m v') -> Map k v -> Event t (PatchMapWithMove k v) -> m (Map k v', Event t (PatchMapWithMove k v'))
1196+mapMapWithAdjustWithMove :: forall t m k v v'. (Adjustable t m, Ord k) => (k -> v -> m v') -> Map k v -> Event t (PatchMapWithMove k v) -> m (Map k v', Event t (PatchMapWithMove k v'))
11901197mapMapWithAdjustWithMove f m0 m' = do
11911198 (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
11921199 return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
@@ -1239,6 +1246,9 @@ mergeWithFoldCheap' f es =
12391246-- Deprecated functions
12401247--------------------------------------------------------------------------------
12411248
1249+{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
1250+type MonadAdjust = Adjustable
1251+
12421252-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
12431253-- occurs. If both occur at the same time they are combined using 'mappend'.
12441254{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
src/Reflex/Collection.hsnew file+121
@@ -0,0 +1,121 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE RankNTypes #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE RecursiveDo #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE FlexibleInstances #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+#ifdef USE_REFLEX_OPTIMIZER
12+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
13+#endif
14+module Reflex.Collection
15+ (
16+ -- * Widgets on Collections
17+ listHoldWithKey
18+ , listWithKey
19+ , listWithKey'
20+ , listWithKeyShallowDiff
21+ , listViewWithKey
22+ , selectViewListWithKey
23+ , selectViewListWithKey_
24+ -- * List Utils
25+ , list
26+ , simpleList
27+ ) where
28+
29+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
30+import Data.Align
31+import Data.Map (Map)
32+import qualified Data.Map as Map
33+import Data.Functor.Misc
34+import Data.Map.Misc
35+import Data.These
36+
37+import Reflex.Class
38+import Reflex.Dynamic
39+import Reflex.PostBuild.Class
40+
41+listHoldWithKey :: forall t m k v a. (Ord k, Adjustable t m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
42+listHoldWithKey m0 m' f = do
43+ let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
44+ dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
45+ (a0, a') <- sequenceDMapWithAdjust dm0 dm'
46+ fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time
47+
48+--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
49+listWithKey :: forall t k v m a. (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
50+listWithKey vals mkChild = do
51+ postBuild <- getPostBuild
52+ let childValChangedSelector = fanMap $ updated vals
53+ -- We keep track of changes to children values in the mkChild function we pass to listHoldWithKey
54+ -- The other changes we need to keep track of are child insertions and deletions. diffOnlyKeyChanges
55+ -- keeps track of insertions and deletions but ignores value changes, since they're already accounted for.
56+ diffOnlyKeyChanges olds news = flip Map.mapMaybe (align olds news) $ \case
57+ This _ -> Just Nothing
58+ These _ _ -> Nothing
59+ That new -> Just $ Just new
60+ rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
61+ let changeVals :: Event t (Map k (Maybe v))
62+ changeVals = attachWith diffOnlyKeyChanges (current sentVals) $ leftmost
63+ [ updated vals
64+ , tag (current vals) postBuild --TODO: This should probably be added to the attachWith, not to the updated; if we were using diffMap instead of diffMapNoEq, I think it might not work
65+ ]
66+ listHoldWithKey Map.empty changeVals $ \k v ->
67+ mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
68+
69+{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
70+listWithKey' :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
71+listWithKey' = listWithKeyShallowDiff
72+
73+-- | Display the given map of items (in key order) using the builder function provided, and update it with the given event. 'Nothing' update entries will delete the corresponding children, and 'Just' entries will create them if they do not exist or send an update event to them if they do.
74+listWithKeyShallowDiff :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
75+listWithKeyShallowDiff initialVals valsChanged mkChild = do
76+ let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
77+ sentVals <- foldDyn applyMap Map.empty $ fmap (fmap void) valsChanged
78+ let relevantPatch patch _ = case patch of
79+ Nothing -> Just Nothing -- Even if we let a Nothing through when the element doesn't already exist, this doesn't cause a problem because it is ignored
80+ Just _ -> Nothing -- We don't want to let spurious re-creations of items through
81+ listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v ->
82+ mkChild k v $ select childValChangedSelector $ Const2 k
83+
84+--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
85+-- | Create a dynamically-changing set of Event-valued widgets.
86+-- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'.
87+listViewWithKey :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
88+listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild
89+
90+listViewWithKey' :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
91+listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild
92+
93+-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
94+selectViewListWithKey :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
95+ => Dynamic t k -- ^ Current selection key
96+ -> Dynamic t (Map k v) -- ^ Dynamic key/value map
97+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
98+ -> m (Event t (k, a)) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
99+selectViewListWithKey selection vals mkChild = do
100+ let selectionDemux = demux selection -- For good performance, this value must be shared across all children
101+ selectChild <- listWithKey vals $ \k v -> do
102+ let selected = demuxed selectionDemux k
103+ selectSelf <- mkChild k v selected
104+ return $ fmap ((,) k) selectSelf
105+ return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
106+
107+selectViewListWithKey_ :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
108+ => Dynamic t k -- ^ Current selection key
109+ -> Dynamic t (Map k v) -- ^ Dynamic key/value map
110+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
111+ -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
112+selectViewListWithKey_ selection vals mkChild = fmap fst <$> selectViewListWithKey selection vals mkChild
113+
114+-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
115+-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
116+list :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
117+list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
118+
119+-- | Create a dynamically-changing set of widgets from a Dynamic list.
120+simpleList :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
121+simpleList xs mkChild = fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap (Map.fromList . zip [(1::Int)..]) xs
src/Reflex/DynamicWriter.hs+2−2
@@ -135,9 +135,9 @@ instance MonadState s m => MonadState s (DynamicWriterT t w m) where
135135newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
136136
137137-- | When the execution of a 'DynamicWriterT' action is adjusted using
138−-- 'MonadAdjust', the 'Dynamic' output of that action will also be updated to
138+-- 'Adjustable', the 'Dynamic' output of that action will also be updated to
139139-- match.
140−instance (MonadAdjust t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => MonadAdjust t (DynamicWriterT t w m) where
140+instance (Adjustable t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => Adjustable t (DynamicWriterT t w m) where
141141 runWithReplace a0 a' = do
142142 (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
143143 tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
src/Reflex/EventWriter.hs+3−3
@@ -140,7 +140,7 @@ instance MonadHold t m => MonadHold t (EventWriterT t w m) where
140140 {-# INLINABLE headE #-}
141141 headE = lift . headE
142142
143−instance (Reflex t, MonadAdjust t m, MonadHold t m, Semigroup w) => MonadAdjust t (EventWriterT t w m) where
143+instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
144144 runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
145145 traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElements mergeIntIncremental
146146 traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
@@ -154,8 +154,8 @@ instance Requester t m => Requester t (EventWriterT t w m) where
154154
155155-- | Given a function like 'runWithReplace' for the underlying monad, implement
156156-- 'runWithReplace' for 'EventWriterT'. This is necessary when the underlying
157−-- monad doesn't have a 'MonadAdjust' instance or to override the default
158−-- 'MonadAdjust' behavior.
157+-- monad doesn't have a 'Adjustable' instance or to override the default
158+-- 'Adjustable' behavior.
159159runWithReplaceEventWriterTWith :: forall m t w a b. (Reflex t, MonadHold t m, Semigroup w)
160160 => (forall a' b'. m a' -> Event t (m b') -> EventWriterT t w m (a', Event t b'))
161161 -> EventWriterT t w m a
src/Reflex/PerformEvent/Base.hs+1−1
@@ -72,7 +72,7 @@ instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent
7272 {-# INLINABLE performEvent #-}
7373 performEvent = PerformEventT . requestingIdentity
7474
75−instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
75+instance (ReflexHost t, PrimMonad (HostFrame t)) => Adjustable t (PerformEventT t m) where
7676 runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequesterTWith f (coerce outerA0) (coerceEvent outerA')
7777 where f :: HostFrame t a -> Event t (HostFrame t b) -> RequesterT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
7878 f a0 a' = do
src/Reflex/PostBuild/Base.hs+1−1
@@ -107,7 +107,7 @@ instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
107107 {-# INLINABLE atomicModifyRef #-}
108108 atomicModifyRef r = lift . atomicModifyRef r
109109
110−instance (Reflex t, MonadHold t m, MonadFix m, MonadAdjust t m, PerformEvent t m) => MonadAdjust t (PostBuildT t m) where
110+instance (Reflex t, MonadHold t m, MonadFix m, Adjustable t m, PerformEvent t m) => Adjustable t (PostBuildT t m) where
111111 runWithReplace a0 a' = do
112112 postBuild <- getPostBuild
113113 lift $ do
src/Reflex/Query/Base.hs+1−1
@@ -70,7 +70,7 @@ let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
7070 return (Just (AdditivePatch p))
7171-}
7272
73−instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, MonadAdjust t m) => MonadAdjust t (QueryT t q m) where
73+instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
7474 runWithReplace (QueryT a0) a' = do
7575 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
7676 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
src/Reflex/Requester/Base.hs+2−2
@@ -299,7 +299,7 @@ instance MonadReader r m => MonadReader r (RequesterT t request response m) wher
299299 local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a
300300 reader = lift . reader
301301
302−instance (Reflex t, MonadAdjust t m, MonadHold t m, MonadFix m) => MonadAdjust t (RequesterT t request response m) where
302+instance (Reflex t, Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (RequesterT t request response m) where
303303 runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
304304 traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental
305305 {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
@@ -358,7 +358,7 @@ traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIn
358358 (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
359359 return (fmapCheap (IntMap.singleton n) myRequests, result)
360360 ndm' <- numberOccurrencesFrom 1 dm'
361− (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to MonadAdjust
361+ (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to Adjustable
362362 let result0 = fmap snd children0
363363 result' = fforCheap children' $ fmap snd
364364 requests0 :: IntMap (Event t (IntMap (RequesterData request)))
src/Reflex/TriggerEvent/Base.hs+1−1
@@ -119,7 +119,7 @@ instance MonadHold t m => MonadHold t (TriggerEventT t m) where
119119 {-# INLINABLE headE #-}
120120 headE = lift . headE
121121
122−instance MonadAdjust t m => MonadAdjust t (TriggerEventT t m) where
122+instance Adjustable t m => Adjustable t (TriggerEventT t m) where
123123 {-# INLINABLE runWithReplace #-}
124124 runWithReplace (TriggerEventT a0) a' = TriggerEventT $ runWithReplace a0 (coerceEvent a')
125125 {-# INLINABLE traverseIntMapWithKeyWithAdjust #-}
src/Reflex/Widget/Basic.hs+1−1
@@ -30,7 +30,7 @@ import Reflex.Patch.MapWithMove
3030-- > (\k v -> text $ "\n" ++ show k ++ " " ++ v) -- show each element on a new line
3131-- > (Map.fromList $ zip [0..] [(3, "a"), (2, "b"), (1, "c")])
3232-- > sortEvent
33−sortableList :: forall t m k v a. (MonadHold t m, MonadFix m, MonadAdjust t m, Ord k)
33+sortableList :: forall t m k v a. (MonadHold t m, MonadFix m, Adjustable t m, Ord k)
3434 => (k -> v -> m a) -- ^ Function to render the content for each key/value pair
3535 -> Map k v -- ^ The sortable list with an initial ordering determined by the @Map@ keys in ascending order
3636 -> Event t (v -> v -> Ordering) -- ^ An event carrying a sort function for the list
test/EventWriterT.hs+1−1
@@ -35,7 +35,7 @@ testOrdering pulse = do
3535 forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
3636 return ()
3737
38−testSimultaneous :: (Reflex t, MonadAdjust t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
38+testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
3939testSimultaneous pulse = do
4040 let e0 = fmapMaybe (^? here) pulse
4141 e1 = fmapMaybe (^? there) pulse
test/QueryT.hs+4−12
@@ -81,7 +81,7 @@ testQueryT w (AppIn _ pulse) = do
8181 }
8282
8383testRunWithReplace :: ( Reflex t
84− , MonadAdjust t m
84+ , Adjustable t m
8585 , MonadHold t m
8686 , MonadFix m
8787 , MonadQuery t (Selector Int MyQuery) m)
@@ -96,7 +96,7 @@ testRunWithReplace replace increment = do
9696 return ()
9797
9898testSequenceDMapWithAdjust :: ( Reflex t
99− , MonadAdjust t m
99+ , Adjustable t m
100100 , MonadHold t m
101101 , MonadFix m
102102 , MonadQuery t (Selector Int MyQuery) m)
@@ -110,7 +110,7 @@ testSequenceDMapWithAdjust replace increment = do
110110 return ()
111111
112112testSequenceDMapWithAdjustWithMove :: ( Reflex t
113− , MonadAdjust t m
113+ , Adjustable t m
114114 , MonadHold t m
115115 , MonadFix m
116116 , MonadQuery t (Selector Int MyQuery) m)
@@ -123,16 +123,8 @@ testSequenceDMapWithAdjustWithMove replace increment = do
123123 queryDyn $ zipDynWith (\x y -> Selector (AMap.singleton (x :: Int) y)) n $ pure $ MyQuery $ SelectedCount 1
124124 return ()
125125
126−listHoldWithKey :: forall t m k v a. (Ord k, MonadHold t m, MonadAdjust t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
127−listHoldWithKey m0 m' f = do
128− let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
129− dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
130− (a0, a') <- sequenceDMapWithAdjust dm0 dm'
131− fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a'
132−
133−
134126-- scam it out to test traverseDMapWithAdjustWithMove
135−listHoldWithKeyWithMove :: forall t m k v a. (Ord k, MonadHold t m, MonadAdjust t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
127+listHoldWithKeyWithMove :: forall t m k v a. (Ord k, MonadHold t m, Adjustable t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
136128listHoldWithKeyWithMove m0 m' f = do
137129 (n0, n') <- mapMapWithAdjustWithMove f m0 $ ffor m' $ PatchMapWithMove . Map.map (\v -> NodeInfo (maybe From_Delete From_Insert v) Nothing)
138130 incrementalToDynamic <$> holdIncremental n0 n'
test/RequesterT.hs+2−2
@@ -45,7 +45,7 @@ unwrapApp x appIn = do
4545testOrdering :: ( Response m ~ Identity
4646 , Request m ~ RequestInt
4747 , Requester t m
48− , MonadAdjust t m)
48+ , Adjustable t m)
4949 => Event t ()
5050 -> m ()
5151testOrdering pulse = do
@@ -55,7 +55,7 @@ testOrdering pulse = do
5555testSimultaneous :: ( Response m ~ Identity
5656 , Request m ~ RequestInt
5757 , Requester t m
58− , MonadAdjust t m)
58+ , Adjustable t m)
5959 => Event t (These () ())
6060 -> m ()
6161testSimultaneous pulse = do