on develop help sign in
Merge branch 'release/0.6.2.1' of github.com:ryantrinkle/reflex

* 'release/0.6.2.1' of github.com:ryantrinkle/reflex: (30 commits) Loosen monoidal-containers version bounds Update CONTRIBUTING.md Update CONTRIBUTING.md Use unsafePerformIO better (#325) Generalize fan following DMap (#318) Distribute more generally Generalize merge Drop *Tag classes Fix deprecation warnings related to updated Data.Some Bump dependent-sum to latest version fan: Rewrite haddock Replace hackage link with badge Use Some for more existential types (which should be efficient now that it is a newtype) Bump upper bounds Typo DRY conditional compilation Bump 'these' upper bound Typo Update changelog for holdDyn fix Make holdDyn lazy in its Event again ...

commit144de6281edb0eb9c7c9f6f1fffdfa47bb813432 authorAli Abrar <aliabrar@gmail.com> dateThu 1 Aug 2019, 15:54 (7 years ago) parent898efd645056ff
22 files changed+622−273
CONTRIBUTING.md+62 ChangeLog.md+30−11 README.md+3−2 default.nix+1−1 reflex.cabal+9−7 src/Data/AppendMap.hs+14−4 src/Data/FastMutableIntMap.hs+17 src/Data/FastWeakBag.hs+1−1 src/Data/Functor/Misc.hs+2−11 src/Reflex/Adjustable/Class.hs+30−3 src/Reflex/Class.hs+184−26 src/Reflex/Dynamic.hs+8−10 src/Reflex/DynamicWriter/Class.hs+1 src/Reflex/Patch/DMapWithMove.hs+6−7 src/Reflex/Profiled.hs+10−6 src/Reflex/Pure.hs+19−12 src/Reflex/Query/Base.hs+5−6 src/Reflex/Query/Class.hs+24−4 src/Reflex/Requester/Base.hs+2−3 src/Reflex/Spider/Internal.hs+189−155 src/Reflex/Workflow.hs+1−1 test/GC.hs+4−3
CONTRIBUTING.mdnew file+62
@@ -0,0 +1,62 @@
1+# Contribution Guide
2+
3+Contributions and issue reports are encouraged and appreciated!
4+
5+- [Opening Issues](#opening-issues)
6+- [Submitting Changes](#submitting-changes)
7+ - [Guidelines for Commit Messages](#guidelines-for-commit-messages)
8+ - [Guidelines for Pull Requests](#guidelines-for-pull-requests)
9+ - [Code Quality](#code-quality)
10+ - [Documentation](#documentation)
11+
12+## Opening Issues
13+
14+Before opening an issue, please check whether your issue has already been reported. Assuming it has not:
15+
16+* Describe the issue you're encountering or the suggestion you're making
17+* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
18+* Let us know what version of reflex you were using. If you're using a github checkout, provide the git hash.
19+* Describe how you're building reflex (i.e., via reflex-platform, cabal install, stack, obelisk, etc.). If you're using reflex-platform or obelisk, provide the git hash of your checkout.
20+
21+## Submitting Changes
22+
23+### Guidelines for Commit Messages
24+
25+#### Summary Line
26+The summary line of your commit message should summarize the changes being made. Commit messages should be written in the imperative mood and should describe what happens when the commit is applied.
27+
28+One way to think about it is that your commit message should be able to complete the sentence:
29+"When applied, this commit will..."
30+
31+#### Body
32+For breaking changes, new features, refactors, or other major changes, the body of the commit message should describe the motivation behind the change in greater detail and may include references to the issue tracker. The body shouldn't repeat code/comments from the diff.
33+
34+### Guidelines for Pull Requests
35+
36+Wherever possible, pull requests should add a single feature or fix a single bug. Pull requests should not bundle several unrelated changes.
37+
38+### Code Quality
39+
40+#### Warnings
41+
42+Your pull request should add no new warnings to the project. It should also generally not disable any warnings.
43+
44+#### Build and Test
45+
46+Make sure the project builds and that the tests pass! This will generally also be checked by CI before merge, but trying it yourself first means you'll catch problems earlier and your contribution can be merged that much sooner!
47+
48+### Documentation
49+
50+#### In the code
51+We're always striving to improve documentation. Please include [haddock](https://haskell-haddock.readthedocs.io/en/latest/index.html) documentation for any added code, and update the documentation for any code you modify.
52+
53+#### In the [Changelog](ChangeLog.md)
54+Add an entry to the changelog when your PR:
55+* Adds a feature
56+* Deprecates something
57+* Includes a breaking change
58+* Makes any other change that will impact users
59+
60+#### In the [Readme](README.md)
61+The readme is the first place a lot of people look for information about the repository. Update any parts of the readme that are affected by your PR.
62+
ChangeLog.md+30−11
@@ -1,17 +1,36 @@
11# Revision history for reflex
22
3−## 0.6.0.0 -- 2019-03-20
3+## Unreleased
44
5−* Deprecate FunctorMaybe in favor of Data.Witherable.Filterable. We still export fmapMaybe, ffilter, etc., but they all rely on Filterable now.
6−* Rename MonadDynamicWriter to DynamicWriter and add a deprecation for the old name.
7−* Remove many deprecated functions.
8−* Add a Num instance for Dynamic.
9−* Add matchRequestsWithResponses to make it easier to use Requester with protocols that don't do this matching for you.
10−* Add withRequesterT to map functions over the request and response of a RequesterT.
11−* Suppress nil patches in QueryT as an optimization. The Query type must now have an Eq instance.
12−* Add throttleBatchWithLag to Reflex.Time. See that module for details.
5+* Generalize `fan` to `fanG` to take a `DMap` with non-`Identity`
6+ values.
7+
8+* Generalize merging functions:
9+ `merge` to `mergeG`,
10+ `mergeIncremental` to `mergeIncrementalG`,
11+ `distributeDMapOverDynPure` to `distributeDMapOverDynPureG`,
12+ `mergeIncrementalWithMove` to `mergeIncrementalWithMoveG`.
13+
14+
15+## 0.6.2.0
16+
17+* Fix `holdDyn` so that it is lazy in its event argument
18+ These produce `DMap`s whose values needn't be `Identity`.
19+* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
20+* Fix `holdDyn` so that it is lazy in its event argument.
1321
1422## 0.6.1.0
1523
16−* Re-export all of Data.Map.Monoidal
17−* Fix QueryT and RequesterT tests
24+* Re-export all of `Data.Map.Monoidal`
25+* Fix `QueryT` and `RequesterT` tests
26+
27+## 0.6.0.0 -- 2019-03-20
28+
29+* Deprecate `FunctorMaybe` in favor of `Data.Witherable.Filterable`. We still export `fmapMaybe`, `ffilter`, etc., but they all rely on `Filterable` now.
30+* Rename `MonadDynamicWriter` to `DynamicWriter` and add a deprecation for the old name.
31+* Remove many deprecated functions.
32+* Add a `Num` instance for `Dynamic`.
33+* Add `matchRequestsWithResponses` to make it easier to use `Requester` with protocols that don't do this matching for you.
34+* Add `withRequesterT` to map functions over the request and response of a `RequesterT`.
35+* Suppress nil patches in `QueryT` as an optimization. The `Query` type must now have an `Eq` instance.
36+* Add `throttleBatchWithLag` to `Reflex.Time`. See that module for details.
README.md+3−2
@@ -1,4 +1,7 @@
11## [Reflex](https://reflex-frp.org/)
2+
3+[![Hackage](https://img.shields.io/hackage/v/reflex.svg)](http://hackage.haskell.org/package/reflex)
4+
25### Practical Functional Reactive Programming
36
47Reflex is a fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.
@@ -14,8 +17,6 @@ A summary of Reflex functions is available in the [quick reference](Quickref.md)
1417
1518[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
1619
17−[hackage](https://hackage.haskell.org/package/reflex)
18−
1920[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
2021
2122### Hacking
default.nix+1−1
@@ -11,7 +11,7 @@
1111}:
1212mkDerivation {
1313 pname = "reflex";
14− version = "0.6.1";
14+ version = "0.6.2.1";
1515 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
1616 libraryHaskellDepends = [
1717 base bifunctors containers dependent-map dependent-sum
reflex.cabal+9−7
@@ -1,5 +1,5 @@
11Name: reflex
2−Version: 0.6.1
2+Version: 0.6.2.1
33Synopsis: Higher-order Functional Reactive Programming
44Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
55License: BSD3
@@ -44,13 +44,15 @@ library
4444 base >= 4.9 && < 4.13,
4545 bifunctors >= 5.2 && < 5.6,
4646 comonad,
47+ constraints-extras >= 0.2,
4748 containers >= 0.5 && < 0.7,
4849 data-default >= 0.5 && < 0.8,
49− dependent-map >= 0.2.4 && < 0.3,
50+ dependent-map >= 0.3 && < 0.4,
5051 exception-transformers == 0.4.*,
52+ profunctors,
5153 lens >= 4.7 && < 5,
5254 monad-control >= 1.0.1 && < 1.1,
53− monoidal-containers == 0.4.*,
55+ monoidal-containers >= 0.4 && < 0.6,
5456 mtl >= 2.1 && < 2.3,
5557 prim-uniq >= 0.1.0.1 && < 0.2,
5658 primitive >= 0.5 && < 0.7,
@@ -61,7 +63,7 @@ library
6163 semigroups >= 0.16 && < 0.19,
6264 stm >= 2.4 && < 2.6,
6365 syb >= 0.5 && < 0.8,
64− these >= 0.4 && < 0.7.7,
66+ these >= 0.4 && < 0.9,
6567 time >= 1.4 && < 1.9,
6668 transformers >= 0.2,
6769 transformers-compat >= 0.3,
@@ -133,8 +135,8 @@ library
133135 if flag(use-template-haskell)
134136 cpp-options: -DUSE_TEMPLATE_HASKELL
135137 build-depends:
136− dependent-sum >= 0.3 && < 0.5,
137− haskell-src-exts >= 1.16 && < 1.21,
138+ dependent-sum >= 0.6 && < 0.7,
139+ haskell-src-exts >= 1.16 && < 1.22,
138140 haskell-src-meta >= 0.6 && < 0.9,
139141 template-haskell >= 2.9 && < 2.15
140142 exposed-modules:
@@ -142,7 +144,7 @@ library
142144 other-extensions: TemplateHaskell
143145 else
144146 build-depends:
145− dependent-sum == 0.4.*
147+ dependent-sum == 0.6.*
146148
147149 if flag(fast-weak) && impl(ghcjs)
148150 cpp-options: -DGHCJS_FAST_WEAK
src/Data/AppendMap.hs+14−4
@@ -7,10 +7,14 @@
77{-# LANGUAGE ScopedTypeVariables #-}
88{-# LANGUAGE TypeFamilies #-}
99{-# OPTIONS_GHC -fno-warn-orphans #-}
10−-- | 'Data.Map' with a better 'Monoid' instance
11−--
12−-- 'Data.Map' has @mappend = union@, which is left-biased. AppendMap has
13−-- @mappend = unionWith mappend@ instead.
10+-- |
11+-- Module:
12+-- Data.AppendMap
13+-- Description:
14+-- Instances and convenience functions for 'Data.Map.Monoidal'. We use
15+-- monoidal-containers to take advantage of its better monoid instance.
16+-- 'Data.Map' has @mappend = union@, which is left-biased. 'MonoidalMap'
17+-- has @mappend = unionWith mappend@ instead.
1418module Data.AppendMap
1519 ( module Data.AppendMap
1620 , module Data.Map.Monoidal
@@ -30,12 +34,15 @@ import qualified Data.Witherable as W
3034import Data.Map.Monoidal
3135
3236{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
37+-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
3338type AppendMap = MonoidalMap
3439
3540{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
41+-- | A synonym for 'getMonoidalMap'
3642_unAppendMap :: MonoidalMap k v -> Map k v
3743_unAppendMap = getMonoidalMap
3844
45+-- | Pattern synonym for 'MonoidalMap'
3946pattern AppendMap :: Map k v -> MonoidalMap k v
4047pattern AppendMap m = MonoidalMap m
4148
@@ -50,6 +57,7 @@ nonEmptyDelete k vs =
5057 then Nothing
5158 else Just deleted
5259
60+-- | Like 'mapMaybe' but indicates whether the resulting container is empty
5361mapMaybeNoNull :: (a -> Maybe b)
5462 -> MonoidalMap token a
5563 -> Maybe (MonoidalMap token b)
@@ -60,9 +68,11 @@ mapMaybeNoNull f as =
6068 else Just bs
6169
6270-- TODO: Move instances to `Reflex.Patch`
71+-- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details.
6372showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
6473showTree = coerce (Map.showTree :: Map k a -> String)
6574
75+-- | Displays a 'MonoidalMap' as a tree, using the supplied function to convert nodes to string.
6676showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
6777showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
6878
src/Data/FastMutableIntMap.hs+17
@@ -1,4 +1,9 @@
11{-# LANGUAGE TypeFamilies #-}
2+-- |
3+-- Module:
4+-- Data.FastMutableIntMap
5+-- Description:
6+-- A mutable version of 'IntMap'
27module Data.FastMutableIntMap
38 ( FastMutableIntMap
49 , new
@@ -32,33 +37,43 @@ import Data.IORef
3237import Reflex.Patch.Class
3338import Reflex.Patch.IntMap
3439
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.
3542newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
3643
44+-- | Create a new 'FastMutableIntMap' out of an 'IntMap'
3745new :: IntMap a -> IO (FastMutableIntMap a)
3846new m = FastMutableIntMap <$> newIORef m
3947
48+-- | Create a new empty 'FastMutableIntMap'
4049newEmpty :: IO (FastMutableIntMap a)
4150newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
4251
52+-- | Insert an element into a 'FastMutableIntMap' at the given key
4353insert :: FastMutableIntMap a -> Int -> a -> IO ()
4454insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
4555
56+-- | Attempt to lookup an element by key in a 'FastMutableIntMap'
4657lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
4758lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
4859
60+-- | Runs the provided action over the intersection of a 'FastMutableIntMap' and an 'IntMap'
4961forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
5062forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
5163 a <- liftIO $ readIORef r
5264 traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
5365
66+-- | Runs the provided action over the values of a 'FastMutableIntMap'
5467for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
5568for_ (FastMutableIntMap r) f = do
5669 a <- liftIO $ readIORef r
5770 traverse_ f a
5871
72+-- | Checks whether a 'FastMutableIntMap' is empty
5973isEmpty :: FastMutableIntMap a -> IO Bool
6074isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
6175
76+-- | Retrieves the size of a 'FastMutableIntMap'
6277size :: FastMutableIntMap a -> IO Int
6378size (FastMutableIntMap r) = IntMap.size <$> readIORef r
6479
@@ -69,6 +84,8 @@ getFrozenAndClear (FastMutableIntMap r) = do
6984 writeIORef r IntMap.empty
7085 return result
7186
87+-- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Reflex.Patch.IntMap'),
88+-- and returns an 'IntMap' with the modified keys and values.
7289applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
7390applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
7491 v <- readIORef r
src/Data/FastWeakBag.hs+1−1
@@ -50,7 +50,7 @@ newtype FastWeakBag a = FastWeakBag JSVal
5050#else
5151data FastWeakBag a = FastWeakBag
5252 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
53+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a))) -- ^ Map of items contained by the 'FastWeakBag'
5454 }
5555#endif
5656
src/Data/Functor/Misc.hs+2−11
@@ -52,8 +52,7 @@ import Data.IntMap (IntMap)
5252import qualified Data.IntMap as IntMap
5353import Data.Map (Map)
5454import qualified Data.Map as Map
55−import Data.Some (Some)
56−import qualified Data.Some as Some
55+import Data.Some (Some(Some))
5756import Data.These
5857import Data.Typeable hiding (Refl)
5958
@@ -79,9 +78,6 @@ deriving instance Read k => Read (Const2 k v v)
7978instance Show k => GShow (Const2 k v) where
8079 gshowsPrec n x@(Const2 _) = showsPrec n x
8180
82−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
83− showTaggedPrec (Const2 _) = showsPrec
84−
8581instance Eq k => GEq (Const2 k v) where
8682 geq (Const2 a) (Const2 b) =
8783 if a == b
@@ -124,7 +120,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
124120-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
125121-- the keys, using a function to remove the wrapping 'Functor'
126122weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
127−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some.This k, f v)) . DMap.toAscList
123+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
128124
129125--------------------------------------------------------------------------------
130126-- WrapArg
@@ -214,11 +210,6 @@ instance GShow (EitherTag l r) where
214210 LeftTag -> showString "LeftTag"
215211 RightTag -> showString "RightTag"
216212
217−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
218− showTaggedPrec t n (Identity a) = case t of
219− LeftTag -> showsPrec n a
220− RightTag -> showsPrec n a
221−
222213-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
223214eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
224215eitherToDSum = \case
src/Reflex/Adjustable/Class.hs+30−3
@@ -11,6 +11,13 @@
1111#ifdef USE_REFLEX_OPTIMIZER
1212{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1313#endif
14+-- |
15+-- Module:
16+-- Reflex.Adjustable.Class
17+-- Description:
18+-- A class for actions that can be "adjusted" over time based on some 'Event'
19+-- such that, when observed after the firing of any such 'Event', the result
20+-- is as though the action was originally run with the 'Event's value.
1421module Reflex.Adjustable.Class
1522 (
1623 -- * The Adjustable typeclass
@@ -94,13 +101,32 @@ instance Adjustable t m => Adjustable t (ReaderT r m) where
94101 r <- ask
95102 lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
96103
97−sequenceDMapWithAdjust :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
104+-- | Traverse a 'DMap' of 'Adjustable' actions, running each of them. The provided 'Event' of patches
105+-- to the 'DMap' can add, remove, or update values.
106+sequenceDMapWithAdjust
107+ :: (GCompare k, Adjustable t m)
108+ => DMap k m
109+ -> Event t (PatchDMap k m)
110+ -> m (DMap k Identity, Event t (PatchDMap k Identity))
98111sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
99112
100−sequenceDMapWithAdjustWithMove :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
113+-- | Traverses a 'DMap' of 'Adjustable' actions, running each of them. The provided 'Event' of patches
114+-- to the 'DMap' can add, remove, update, move, or swap values.
115+sequenceDMapWithAdjustWithMove
116+ :: (GCompare k, Adjustable t m)
117+ => DMap k m
118+ -> Event t (PatchDMapWithMove k m)
119+ -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
101120sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
102121
103−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'))
122+-- | Traverses a 'Map', running the provided 'Adjustable' action. The provided 'Event' of patches to the 'Map'
123+-- can add, remove, update, move, or swap values.
124+mapMapWithAdjustWithMove
125+ :: forall t m k v v'. (Adjustable t m, Ord k)
126+ => (k -> v -> m v')
127+ -> Map k v
128+ -> Event t (PatchMapWithMove k v)
129+ -> m (Map k v', Event t (PatchMapWithMove k v'))
104130mapMapWithAdjustWithMove f m0 m' = do
105131 (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
106132 return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
@@ -110,4 +136,5 @@ mapMapWithAdjustWithMove f m0 m' = do
110136--------------------------------------------------------------------------------
111137
112138{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
139+-- | Synonym for 'Adjustable'
113140type MonadAdjust = Adjustable
src/Reflex/Class.hs+184−26
@@ -17,6 +17,7 @@
1717{-# LANGUAGE TypeFamilies #-}
1818{-# LANGUAGE TypeOperators #-}
1919{-# LANGUAGE UndecidableInstances #-}
20+{-# LANGUAGE Trustworthy #-}
2021#ifdef USE_REFLEX_OPTIMIZER
2122{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2223#endif
@@ -40,12 +41,16 @@ module Reflex.Class
4041 , MonadHold (..)
4142 -- ** 'fan' related types
4243 , EventSelector (..)
44+ , EventSelectorG (..)
4345 , EventSelectorInt (..)
4446 -- * Convenience functions
4547 , constDyn
4648 , pushAlways
4749 -- ** Combining 'Event's
4850 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
4954 , mergeMap
5055 , mergeIntMap
5156 , mergeMapIncremental
@@ -60,6 +65,7 @@ module Reflex.Class
6065 , alignEventWithMaybe
6166 -- ** Breaking up 'Event's
6267 , splitE
68+ , fan
6369 , fanEither
6470 , fanThese
6571 , fanMap
@@ -84,6 +90,7 @@ module Reflex.Class
8490 , gate
8591 -- ** Combining 'Dynamic's
8692 , distributeDMapOverDynPure
93+ , distributeDMapOverDynPureG
8794 , distributeListOverDyn
8895 , distributeListOverDynWith
8996 , zipDyn
@@ -193,8 +200,7 @@ import qualified Data.IntMap.Strict as IntMap
193200import Data.List.NonEmpty (NonEmpty (..))
194201import Data.Map (Map)
195202import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
196−import Data.Some (Some)
197−import qualified Data.Some as Some
203+import Data.Some (Some(Some))
198204import Data.String
199205import Data.These
200206import Data.Type.Coercion
@@ -256,11 +262,16 @@ class ( MonadHold t (PushM t)
256262 -- | Merge a collection of events; the resulting 'Event' will only occur if at
257263 -- least one input event is occurring, and will contain all of the input keys
258264 -- that are occurring simultaneously
259− merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
260− -- | Efficiently fan-out an event to many destinations. This function should
261− -- be partially applied, and then the result applied repeatedly to create
262− -- child events
263− fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
265+
266+ --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
267+ mergeG :: GCompare k => (forall a. q a -> Event t (v a))
268+ -> DMap k q -> Event t (DMap k v)
269+
270+ -- | Efficiently fan-out an event to many destinations. You should save the
271+ -- result in a @let@-binding, and then repeatedly 'selectG' on the result to
272+ -- create child events
273+ fanG :: GCompare k => Event t (DMap k v) -> EventSelectorG t k v
274+
264275 -- | Create an 'Event' that will occur whenever the currently-selected input
265276 -- 'Event' occurs
266277 switch :: Behavior t (Event t a) -> Event t a
@@ -278,9 +289,14 @@ class ( MonadHold t (PushM t)
278289 -- that value.
279290 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
280291 -- | Create a merge whose parents can change over time
281− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
292+ mergeIncrementalG :: GCompare k
293+ => (forall a. q a -> Event t (v a))
294+ -> Incremental t (PatchDMap k q)
295+ -> Event t (DMap k v)
282296 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
283− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
297+ mergeIncrementalWithMoveG :: GCompare k
298+ => (forall a. q a -> Event t (v a))
299+ -> Incremental t (PatchDMapWithMove k q) -> Event t (DMap k v)
284300 -- | Extract the 'Behavior' component of an 'Incremental'
285301 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
286302 -- | Extract the 'Event' component of an 'Incremental'
@@ -299,7 +315,21 @@ class ( MonadHold t (PushM t)
299315 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
300316 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
301317
318+-- | Efficiently fan-out an event to many destinations. You should save the
319+-- result in a @let@-binding, and then repeatedly 'select' on the result to
320+-- create child events
321+fan :: forall t k. (Reflex t, GCompare k)
322+ => Event t (DMap k Identity) -> EventSelector t k
323+ --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
324+fan e = EventSelector (fixup (selectG (fanG e) :: k a -> Event t (Identity a)) :: forall a. k a -> Event t a)
325+ where
326+ fixup :: forall a. (k a -> Event t (Identity a)) -> k a -> Event t a
327+ fixup = case eventCoercion Coercion :: Coercion (Event t (Identity a)) (Event t a) of
328+ Coercion -> coerce
329+
302330--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
331+-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
332+-- keys simultaneously.
303333mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
304334mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
305335
@@ -368,26 +398,91 @@ class MonadSample t m => MonadHold t m where
368398 -- the supplied 'Event'.
369399 headE :: Event t a -> m (Event t a)
370400
371−accumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> p) -> PatchTarget p -> Event t b -> m (Incremental t p)
401+-- | Accumulate an 'Incremental' with the supplied initial value and the firings of the provided 'Event',
402+-- using the combining function to produce a patch.
403+accumIncremental
404+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
405+ => (PatchTarget p -> b -> p)
406+ -> PatchTarget p
407+ -> Event t b
408+ -> m (Incremental t p)
372409accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
373−accumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t p) -> PatchTarget p -> Event t b -> m (Incremental t p)
410+
411+-- | Similar to 'accumIncremental' but the combining function runs in 'PushM'
412+accumMIncremental
413+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
414+ => (PatchTarget p -> b -> PushM t p)
415+ -> PatchTarget p
416+ -> Event t b
417+ -> m (Incremental t p)
374418accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
375−accumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> Maybe p) -> PatchTarget p -> Event t b -> m (Incremental t p)
419+
420+-- | Similar to 'accumIncremental' but allows filtering of updates (by dropping updates when the
421+-- combining function produces @Nothing@)
422+accumMaybeIncremental
423+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
424+ => (PatchTarget p -> b -> Maybe p)
425+ -> PatchTarget p
426+ -> Event t b
427+ -> m (Incremental t p)
376428accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
377−accumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p)) -> PatchTarget p -> Event t b -> m (Incremental t p)
429+
430+-- | Similar to 'accumMaybeMIncremental' but the combining function runs in 'PushM'
431+accumMaybeMIncremental
432+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
433+ => (PatchTarget p -> b -> PushM t (Maybe p))
434+ -> PatchTarget p
435+ -> Event t b
436+ -> m (Incremental t p)
378437accumMaybeMIncremental f z e = do
379438 rec let e' = flip push e $ \o -> do
380439 v <- sample $ currentIncremental d'
381440 f v o
382441 d' <- holdIncremental z e'
383442 return d'
384−mapAccumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
443+
444+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event'
445+-- with a function that both accumulates and produces a value to fire
446+-- as an 'Event'. Returns both the accumulated value and the constructed
447+-- 'Event'.
448+mapAccumIncremental
449+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
450+ => (PatchTarget p -> b -> (p, c))
451+ -> PatchTarget p
452+ -> Event t b
453+ -> m (Incremental t p, Event t c)
385454mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
386−mapAccumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
455+
456+-- | Like 'mapAccumIncremental' but the combining function runs in 'PushM'
457+mapAccumMIncremental
458+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
459+ => (PatchTarget p -> b -> PushM t (p, c))
460+ -> PatchTarget p
461+ -> Event t b
462+ -> m (Incremental t p, Event t c)
387463mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
388−mapAccumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
464+
465+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event' with
466+-- a function that both optionally accumulates and optionally produces
467+-- a value to fire as a separate output 'Event'.
468+-- Note that because 'Nothing's are discarded in both cases, the output
469+-- 'Event' may fire even though the output 'Incremental' has not changed, and
470+-- the output 'Incremental' may update even when the output 'Event' is not firing.
471+mapAccumMaybeIncremental
472+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
473+ => (PatchTarget p -> b -> (Maybe p, Maybe c))
474+ -> PatchTarget p
475+ -> Event t b
476+ -> m (Incremental t p, Event t c)
389477mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
390−mapAccumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
478+
479+-- | Like 'mapAccumMaybeIncremental' but the combining function is a 'PushM' action
480+mapAccumMaybeMIncremental
481+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
482+ => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c))
483+ -> PatchTarget p
484+ -> Event t b
485+ -> m (Incremental t p, Event t c)
391486mapAccumMaybeMIncremental f z e = do
392487 rec let e' = flip push e $ \o -> do
393488 v <- sample $ currentIncremental d'
@@ -398,6 +493,7 @@ mapAccumMaybeMIncremental f z e = do
398493 d' <- holdIncremental z $ mapMaybe fst e'
399494 return (d', mapMaybe snd e')
400495
496+-- | A somewhat slow implementation of 'headE'
401497slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
402498slowHeadE e = do
403499 rec be <- hold e $ fmapCheap (const never) e'
@@ -418,6 +514,19 @@ newtype EventSelector t k = EventSelector
418514 select :: forall a. k a -> Event t a
419515 }
420516
517+newtype EventSelectorG t k v = EventSelectorG
518+ { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
519+ -- determined by the type of the key, so this can be used to fan-out
520+ -- 'Event's whose sub-'Event's have different types.
521+ --
522+ -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
523+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
524+ -- occurrences of an 'Event'.
525+ selectG :: forall a. k a -> Event t (v a)
526+ }
527+
528+-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
529+-- filtering by key.
421530newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
422531
423532--------------------------------------------------------------------------------
@@ -801,7 +910,12 @@ mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
801910mergeList [] = never
802911mergeList es = mergeWithFoldCheap' id es
803912
804−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
913+unsafeMapIncremental
914+ :: (Reflex t, Patch p, Patch p')
915+ => (PatchTarget p -> PatchTarget p')
916+ -> (p -> p')
917+ -> Incremental t p
918+ -> Incremental t p'
805919unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
806920
807921-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
@@ -918,6 +1032,9 @@ coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $
9181032 ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
9191033 ]
9201034
1035+-- | Given a 'PatchTarget' of events (e.g., a 'Map' with 'Event' values) and an event of 'Patch'es
1036+-- (e.g., a 'PatchMap' with 'Event' values), produce an 'Event' of the 'PatchTarget' type that
1037+-- fires with the patched value.
9211038switchHoldPromptOnlyIncremental
9221039 :: forall t m p pt w
9231040 . ( Reflex t
@@ -942,8 +1059,12 @@ switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch e0 e' = d
9421059
9431060instance Reflex t => Align (Event t) where
9441061 nil = never
1062+#if MIN_VERSION_these(0, 8, 0)
1063+instance Reflex t => Semialign (Event t) where
1064+#endif
9451065 align = alignEventWithMaybe Just
9461066
1067+
9471068-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
9481069-- the 'Behavior' is true at the time of occurrence.
9491070gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
@@ -998,12 +1119,21 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
9981119-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
9991120-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
10001121distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
1001−distributeDMapOverDynPure dm = case DMap.toList dm of
1122+distributeDMapOverDynPure = distributeDMapOverDynPureG coerceDynamic
1123+
1124+-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
1125+-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1126+-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1127+distributeDMapOverDynPureG
1128+ :: forall t k q v. (Reflex t, GCompare k)
1129+ => (forall a. q a -> Dynamic t (v a))
1130+ -> DMap k q -> Dynamic t (DMap k v)
1131+distributeDMapOverDynPureG nt dm = case DMap.toList dm of
10021132 [] -> constDyn DMap.empty
1003− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1133+ [k :=> v] -> DMap.singleton k <$> nt v
10041134 _ ->
1005− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
1006− edmPre = merge $ DMap.map updated dm
1135+ let getInitial = DMap.traverseWithKey (\_ -> sample . current . nt) dm
1136+ edmPre = mergeG getCompose $ DMap.map (Compose . updated . nt) dm
10071137 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
10081138 olds <- sample $ current result
10091139 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -1015,7 +1145,16 @@ distributeListOverDyn = distributeListOverDynWith id
10151145
10161146-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
10171147distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
1018−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
1148+distributeListOverDynWith f =
1149+ fmap (f . map fromDSum . DMap.toAscList) .
1150+ distributeDMapOverDynPure .
1151+ DMap.fromDistinctAscList .
1152+ zipWith toDSum [0..]
1153+ where
1154+ toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
1155+ toDSum k v = Const2 k :=> v
1156+ fromDSum :: DSum (Const2 Int a) Identity -> a
1157+ fromDSum (Const2 _ :=> Identity v) = v
10191158
10201159-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
10211160-- unless the second supplied 'Event' occurs simultaneously.
@@ -1064,9 +1203,9 @@ factorEvent
10641203 -> Event t (DSum k v)
10651204 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
10661205factorEvent k0 kv' = do
1067− key :: Behavior t (Some k) <- hold (Some.This k0) $ fmapCheap (\(k :=> _) -> Some.This k) kv'
1206+ key :: Behavior t (Some k) <- hold (Some k0) $ fmapCheap (\(k :=> _) -> Some k) kv'
10681207 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
1069− Some.This oldKey -> case newKey `geq` oldKey of
1208+ Some oldKey -> case newKey `geq` oldKey of
10701209 Just Refl -> return Nothing
10711210 Nothing -> do
10721211 newInner <- filterEventKey newKey kv'
@@ -1276,7 +1415,7 @@ mapAccumMaybeB
12761415 -> m (Behavior t a, Event t c)
12771416mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
12781417
1279−-- | LIke 'mapAccumMaybeB' except that the combining function is a 'PushM' action.
1418+-- | Like 'mapAccumMaybeB' except that the combining function is a 'PushM' action.
12801419{-# INLINE mapAccumMaybeMB #-}
12811420mapAccumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)
12821421mapAccumMaybeMB f z e = do
@@ -1467,6 +1606,23 @@ fmapCheap f = pushCheap $ return . Just . f
14671606tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
14681607tagCheap b = pushAlwaysCheap $ \_ -> sample b
14691608
1609+-- | Merge a collection of events; the resulting 'Event' will only occur if at
1610+-- least one input event is occurring, and will contain all of the input keys
1611+-- that are occurring simultaneously
1612+merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
1613+merge = mergeG coerceEvent
1614+{-# INLINE merge #-}
1615+
1616+-- | Create a merge whose parents can change over time
1617+mergeIncremental :: (Reflex t, GCompare k)
1618+ => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
1619+mergeIncremental = mergeIncrementalG coerceEvent
1620+
1621+-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
1622+mergeIncrementalWithMove :: (Reflex t, GCompare k)
1623+ => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
1624+mergeIncrementalWithMove = mergeIncrementalWithMoveG coerceEvent
1625+
14701626-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
14711627{-# INLINE mergeWithCheap #-}
14721628mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
@@ -1491,8 +1647,10 @@ mergeWithFoldCheap' f es =
14911647--------------------------------------------------------------------------------
14921648
14931649{-# DEPRECATED switchPromptly "Use 'switchHoldPromptly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1650+-- | See 'switchHoldPromptly'
14941651switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
14951652switchPromptly = switchHoldPromptly
14961653{-# DEPRECATED switchPromptOnly "Use 'switchHoldPromptOnly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1654+-- | See 'switchHoldPromptOnly'
14971655switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
14981656switchPromptOnly = switchHoldPromptOnly
src/Reflex/Dynamic.hs+8−10
@@ -203,17 +203,9 @@ distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithF
203203
204204-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
205205-- non-'Dynamic' elements, preserving the order of the elements.
206+{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
206207distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
207−distributeListOverDynPure =
208− fmap (map fromDSum . DMap.toAscList) .
209− distributeDMapOverDynPure .
210− DMap.fromDistinctAscList .
211− zipWith toDSum [0..]
212− where
213− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
214− toDSum k v = Const2 k :=> v
215− fromDSum :: DSum (Const2 Int a) Identity -> a
216− fromDSum (Const2 _ :=> Identity v) = v
208+distributeListOverDynPure = distributeListOverDyn
217209
218210--TODO: Generalize this to functors other than Maps
219211-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
@@ -301,6 +293,8 @@ maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
301293 Left _ -> Nothing
302294 Right a -> Just a
303295
296+-- | Turns a 'Dynamic t (Either a b)' into a 'Dynamic t (Either (Dynamic t a) (Dynamic t b))' such that
297+-- the outer 'Dynamic' is updated only when the 'Either' constructor changes (e.g., from 'Left' to 'Right').
304298eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
305299eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
306300 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
@@ -308,6 +302,9 @@ eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
308302 LeftTag :=> Compose a -> Left $ coerceDynamic a
309303 RightTag :=> Compose b -> Right $ coerceDynamic b
310304
305+-- | Factor a 'Dynamic t DSum' into a 'Dynamic' 'DSum' containing nested 'Dynamic' values.
306+-- The outer 'Dynamic' updates only when the key of the 'DSum' changes, while the update of the inner
307+-- 'Dynamic' represents updates within the current key.
311308factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
312309 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
313310factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
@@ -322,6 +319,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
322319 inner' <- filterEventKey k (updated d)
323320 inner <- holdDyn v inner'
324321 return $ k :=> Compose inner
322+
325323--------------------------------------------------------------------------------
326324-- Demux
327325--------------------------------------------------------------------------------
src/Reflex/DynamicWriter/Class.hs+1
@@ -17,6 +17,7 @@ import Control.Monad.Reader (ReaderT, lift)
1717import Reflex.Class (Dynamic)
1818
1919{-# DEPRECATED MonadDynamicWriter "Use 'DynamicWriter' instead" #-}
20+-- | Type synonym for 'DynamicWriter'
2021type MonadDynamicWriter = DynamicWriter
2122
2223-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
src/Reflex/Patch/DMapWithMove.hs+6−7
@@ -19,9 +19,9 @@ import Reflex.Patch.Class
1919import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
2020import qualified Reflex.Patch.MapWithMove as MapWithMove
2121
22+import Data.Constraint.Extras
2223import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
2324import qualified Data.Dependent.Map as DMap
24−import Data.Dependent.Sum (EqTag (..))
2525import Data.Functor.Constant
2626import Data.Functor.Misc
2727import Data.Functor.Product
@@ -30,8 +30,7 @@ import Data.GADT.Show (GShow, gshow)
3030import qualified Data.Map as Map
3131import Data.Maybe
3232import Data.Semigroup (Semigroup (..), (<>))
33−import Data.Some (Some)
34−import qualified Data.Some as Some
33+import Data.Some (Some(Some))
3534import Data.These
3635
3736-- | 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
@@ -105,8 +104,8 @@ validationErrorsForPatchDMapWithMove m =
105104 unbalancedMove _ = Nothing
106105
107106-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
108−instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
109− PatchDMapWithMove a == PatchDMapWithMove b = a == b
107+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
108+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
110109
111110-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
112111data Pair1 f g a = Pair1 (f a) (g a)
@@ -311,8 +310,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
311310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
312311 From_Insert v -> MapWithMove.From_Insert $ f v
313312 From_Delete -> MapWithMove.From_Delete
314− From_Move k -> MapWithMove.From_Move $ Some.This k
315− , MapWithMove._nodeInfo_to = Some.This <$> getComposeMaybe (_nodeInfo_to ni)
313+ From_Move k -> MapWithMove.From_Move $ Some k
314+ , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
316315 }
317316
318317-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
src/Reflex/Profiled.hs+10−6
@@ -8,6 +8,8 @@
88{-# LANGUAGE StandaloneDeriving #-}
99{-# LANGUAGE TypeFamilies #-}
1010{-# LANGUAGE UndecidableInstances #-}
11+{-# LANGUAGE PolyKinds #-}
12+{-# LANGUAGE RankNTypes #-}
1113-- |
1214-- Module:
1315-- Reflex.Profiled
@@ -16,7 +18,6 @@
1618-- profiling/cost-center information.
1719module Reflex.Profiled where
1820
19−import Control.Lens hiding (children)
2021import Control.Monad
2122import Control.Monad.Exception
2223import Control.Monad.Fix
@@ -33,6 +34,7 @@ import Data.Map (Map)
3334import qualified Data.Map.Strict as Map
3435import Data.Monoid ((<>))
3536import Data.Ord
37+import Data.Profunctor.Unsafe ((#.))
3638import qualified Data.Semigroup as S
3739import Data.Type.Coercion
3840import Foreign.Ptr
@@ -133,17 +135,19 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
133135 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
134136 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
135137 pull = Behavior_Profiled . pull . coerce
136− merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
137− merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
138− fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
138+ fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
139+ mergeG :: forall (k :: z -> *) q v. GCompare k
140+ => (forall a. q a -> Event (ProfiledTimeline t) (v a))
141+ -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
142+ mergeG nt = Event_Profiled #. mergeG (coerce nt)
139143 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
140144 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
141145 current (Dynamic_Profiled d) = coerce $ current d
142146 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
143147 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
144148 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
145− mergeIncremental = Event_Profiled . mergeIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMap k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMap k (Event t)))
146− mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
149+ mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
150+ mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
147151 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
148152 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
149153 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
src/Reflex/Pure.hs+19−12
@@ -5,6 +5,9 @@
55{-# LANGUAGE MultiParamTypeClasses #-}
66{-# LANGUAGE ScopedTypeVariables #-}
77{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE PolyKinds #-}
10+
811#ifdef USE_REFLEX_OPTIMIZER
912{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1013#endif
@@ -43,10 +46,11 @@ import Data.MemoTrie
4346import Data.Monoid
4447import Data.Type.Coercion
4548import Reflex.Class
49+import Data.Kind (Type)
4650
4751-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
4852-- with the type @/t/@.
49−data Pure t
53+data Pure (t :: Type)
5054
5155-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
5256-- any @/y :: t/@ such that @/'pred' x < y < x/@. The 'HasTrie' instance will be used
@@ -79,17 +83,18 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
7983 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
8084 -- warning because the GCompare instance is not used; however, removing the
8185 -- GCompare instance produces a different warning, due to that constraint
82− -- being present in the original class definition
86+ -- being present in the original class definition.
8387
84− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
85− merge events = Event $ memo $ \t ->
86− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
88+ --mergeG :: GCompare k => (forall a. q a -> Event (Pure t) (v a))
89+ -- -> DMap k q -> Event (Pure t) (DMap k v)
90+ mergeG nt events = Event $ memo $ \t ->
91+ let currentOccurrences = DMap.mapMaybeWithKey (\_ q -> case nt q of Event a -> a t) events
8792 in if DMap.null currentOccurrences
8893 then Nothing
8994 else Just currentOccurrences
9095
91− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
92− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
96+ -- fanG :: GCompare k => Event (Pure t) (DMap k v) -> EventSelectorG (Pure t) k v
97+ fanG e = EventSelectorG $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
9398
9499 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
95100 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
@@ -112,8 +117,8 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
112117 --a) -> Incremental (Pure t) p a
113118 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
114119
115− mergeIncremental = mergeIncrementalImpl
116− mergeIncrementalWithMove = mergeIncrementalImpl
120+ mergeIncrementalG = mergeIncrementalImpl
121+ mergeIncrementalWithMoveG = mergeIncrementalImpl
117122
118123 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
119124
@@ -133,9 +138,11 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
133138
134139 mergeIntIncremental = mergeIntIncrementalImpl
135140
136−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
137−mergeIncrementalImpl i = Event $ \t ->
138− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
141+mergeIncrementalImpl :: (PatchTarget p ~ DMap k q, GCompare k)
142+ => (forall a. q a -> Event (Pure t) (v a))
143+ -> Incremental (Pure t) p -> Event (Pure t) (DMap k v)
144+mergeIncrementalImpl nt i = Event $ \t ->
145+ let results = DMap.mapMaybeWithKey (\_ q -> case nt q of Event e -> e t) $ fst $ unIncremental i t
139146 in if DMap.null results
140147 then Nothing
141148 else Just results
src/Reflex/Query/Base.hs+5−6
@@ -38,8 +38,7 @@ import Data.Map (Map)
3838import qualified Data.Map as Map
3939import Data.Monoid ((<>))
4040import qualified Data.Semigroup as S
41−import Data.Some (Some)
42−import qualified Data.Some as Some
41+import Data.Some (Some(Some))
4342import Data.These
4443
4544import Reflex.Class
@@ -145,10 +144,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
145144 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
146145 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
147146 liftedBs0 :: Map (Some k) [Behavior t q]
148− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
147+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
149148 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
150149 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
151− Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some.This k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
150+ Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
152151 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
153152 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
154153 accumBehaviors :: forall m'. MonadHold t m'
@@ -189,10 +188,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
189188 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
190189 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
191190 liftedBs0 :: Map (Some k) [Behavior t q]
192− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
191+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
193192 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
194193 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
195− Map.fromDistinctAscList $ (\(k :=> mr) -> (Some.This k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
194+ Map.fromDistinctAscList $ (\(k :=> mr) -> (Some k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
196195 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
197196 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
198197 accumBehaviors' :: forall m'. MonadHold t m'
src/Reflex/Query/Class.hs+24−4
@@ -6,6 +6,13 @@
66{-# LANGUAGE MultiParamTypeClasses #-}
77{-# LANGUAGE TypeFamilies #-}
88{-# LANGUAGE UndecidableInstances #-}
9+-- |
10+-- Module:
11+-- Reflex.Query.Class
12+-- Description:
13+-- A class that ties together queries to some data source and their results,
14+-- providing methods for requesting data from the source and accumulating
15+-- streamed results.
916module Reflex.Query.Class
1017 ( Query (..)
1118 , QueryMorphism (..)
@@ -32,6 +39,10 @@ import Foreign.Storable
3239
3340import Reflex.Class
3441
42+-- | A 'Query' can be thought of as a declaration of interest in some set of data.
43+-- A 'QueryResult' is the set of data associated with that interest set.
44+-- The @crop@ function provides a way to determine what part of a given 'QueryResult'
45+-- is relevant to a given 'Query'.
3546class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
3647 type QueryResult a :: *
3748 crop :: a -> QueryResult a -> QueryResult a
@@ -40,8 +51,8 @@ instance (Ord k, Query v) => Query (MonoidalMap k v) where
4051 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
4152 crop q r = MonoidalMap.intersectionWith (flip crop) r q
4253
43−-- | NB: QueryMorphism's must be group homomorphisms when acting on the query type
44−-- and compatible with the query relationship when acting on the query result
54+-- | QueryMorphism's must be group homomorphisms when acting on the query type
55+-- and compatible with the query relationship when acting on the query result.
4556data QueryMorphism q q' = QueryMorphism
4657 { _queryMorphism_mapQuery :: q -> q'
4758 , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
@@ -54,13 +65,16 @@ instance Category QueryMorphism where
5465 , _queryMorphism_mapQueryResult = mapQueryResult qm' . mapQueryResult qm
5566 }
5667
68+-- | Apply a 'QueryMorphism' to a 'Query'
5769mapQuery :: QueryMorphism q q' -> q -> q'
5870mapQuery = _queryMorphism_mapQuery
5971
72+-- | Map a 'QueryMorphism' to a 'QueryResult'
6073mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
6174mapQueryResult = _queryMorphism_mapQueryResult
6275
63−-- | This type keeps track of the multiplicity of elements of the view selector that are being used by the app
76+-- | This type can be used to track of the frequency of interest in a given 'Query'. See note on
77+-- 'combineSelectedCounts'
6478newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
6579 deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
6680
@@ -76,10 +90,14 @@ instance Group SelectedCount where
7690
7791instance Additive SelectedCount
7892
79−-- | The Semigroup/Monoid/Group instances for a ViewSelector should use this function which returns Nothing if the result is 0. This allows the pruning of leaves that are no longer wanted.
93+-- | The Semigroup/Monoid/Group instances for a Query containing 'SelectedCount's should use
94+-- this function which returns Nothing if the result is 0. This allows the pruning of leaves
95+-- of the 'Query' that are no longer wanted.
8096combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
8197combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
8298
99+-- | A class that allows sending of 'Query's and retrieval of 'QueryResult's. See 'queryDyn' for a commonly
100+-- used interface.
83101class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
84102 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
85103 askQueryResult :: m (Dynamic t (QueryResult q))
@@ -90,9 +108,11 @@ instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
90108 askQueryResult = lift askQueryResult
91109 queryIncremental = lift . queryIncremental
92110
111+-- | Produce and send an 'Incremental' 'Query' from a 'Dynamic' 'Query'.
93112tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
94113tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
95114
115+-- | Retrieve 'Dynamic'ally updating 'QueryResult's for a 'Dynamic'ally updating 'Query'.
96116queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
97117queryDyn q = do
98118 tellQueryDyn q
src/Reflex/Requester/Base.hs+2−3
@@ -66,8 +66,7 @@ import qualified Data.Map as Map
6666import Data.Monoid ((<>))
6767import Data.Proxy
6868import qualified Data.Semigroup as S
69−import Data.Some (Some)
70−import qualified Data.Some as Some
69+import Data.Some (Some(Some))
7170import Data.Type.Equality
7271import Data.Unique.Tag
7372
@@ -441,7 +440,7 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
441440 pack = Entry
442441 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
443442 f' k (Compose (n, v)) = do
444− (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
443+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
445444 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
446445 ndm' <- numberOccurrencesFrom 1 dm'
447446 (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
src/Reflex/Spider/Internal.hs+189−155
@@ -17,9 +17,13 @@
1717{-# LANGUAGE TypeFamilies #-}
1818{-# LANGUAGE TypeOperators #-}
1919{-# LANGUAGE UndecidableInstances #-}
20+{-# LANGUAGE PolyKinds #-}
21+{-# LANGUAGE InstanceSigs #-}
22+
2023#ifdef USE_REFLEX_OPTIMIZER
2124{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2225#endif
26+{-# OPTIONS_GHC -Wunused-binds #-}
2327-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
2428-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2529module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
@@ -49,6 +53,7 @@ import Data.GADT.Compare
4953import Data.IntMap.Strict (IntMap)
5054import qualified Data.IntMap.Strict as IntMap
5155import Data.IORef
56+import Data.Kind (Type)
5257import Data.Maybe hiding (mapMaybe)
5358import Data.Monoid ((<>))
5459import Data.Proxy
@@ -74,9 +79,9 @@ import Data.Tree (Forest, Tree (..), drawForest)
7479import Data.FastWeakBag (FastWeakBag)
7580import qualified Data.FastWeakBag as FastWeakBag
7681import Data.Reflection
77−import Data.Some (Some)
78−import qualified Data.Some as Some
82+import Data.Some (Some(Some))
7983import Data.Type.Coercion
84+import Data.Profunctor.Unsafe ((#.), (.#))
8085import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
8186import qualified Data.WeakBag as WeakBag
8287import qualified Reflex.Class
@@ -193,12 +198,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
193198
194199newNodeId :: IO Int
195200newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
196−
197−{-# NOINLINE unsafeNodeId #-}
198−unsafeNodeId :: a -> Int
199−unsafeNodeId a = unsafePerformIO $ do
200− touch a
201− newNodeId
202201#endif
203202
204203--------------------------------------------------------------------------------
@@ -302,9 +301,10 @@ cacheEvent e =
302301#else
303302 Event $
304303#endif
305− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
307− in \sub -> {-# SCC "cacheEvent" #-} do
304+ unsafePerformIO $ do
305+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306+ <- newIORef emptyFastWeak
307+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
308308#ifdef DEBUG_TRACE_EVENTS
309309 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
310310#endif
@@ -370,7 +370,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
370370eventNever :: Event x a
371371eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
372372
373−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
373+eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k v -> Event x (v a)
374374eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
375375
376376eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -422,14 +422,14 @@ newSubscriberHold h = return $ Subscriber
422422 , subscriberRecalculateHeight = \_ -> return ()
423423 }
424424
425−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
425+newSubscriberFan :: forall x k v. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k v -> IO (Subscriber x (DMap k v))
426426newSubscriberFan subscribed = return $ Subscriber
427427 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
428428 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
429429 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
430430 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
431431 scheduleClear $ fanSubscribedOccurrence subscribed
432− let f _ (Pair (Identity v) subsubs) = do
432+ let f _ (Pair v subsubs) = do
433433 propagate v $ _fanSubscribedChildren_list subsubs
434434 return $ Constant ()
435435 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -577,12 +577,12 @@ eventSubscribedNever = EventSubscribed
577577#endif
578578 }
579579
580−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
580+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
581581eventSubscribedFan !subscribed = EventSubscribed
582582 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
583583 , eventSubscribedRetained = toAny subscribed
584584#ifdef DEBUG_CYCLES
585− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
585+ , eventSubscribedGetParents = return [Some $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
586586 , eventSubscribedHasOwnHeightRef = False
587587 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
588588#endif
@@ -595,7 +595,7 @@ eventSubscribedSwitch !subscribed = EventSubscribed
595595#ifdef DEBUG_CYCLES
596596 , eventSubscribedGetParents = do
597597 s <- readIORef $ switchSubscribedCurrentParent subscribed
598− return [Some.This $ _eventSubscription_subscribed s]
598+ return [Some $ _eventSubscription_subscribed s]
599599 , eventSubscribedHasOwnHeightRef = True
600600 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
601601#endif
@@ -608,8 +608,8 @@ eventSubscribedCoincidence !subscribed = EventSubscribed
608608#ifdef DEBUG_CYCLES
609609 , eventSubscribedGetParents = do
610610 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
611− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612− innerParents = maybeToList $ fmap Some.This innerSubscription
611+ let outerParent = Some $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612+ innerParents = maybeToList $ fmap Some innerSubscription
613613 return $ outerParent : innerParents
614614 , eventSubscribedHasOwnHeightRef = True
615615 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
@@ -625,13 +625,13 @@ whoCreatedEventSubscribed = eventSubscribedWhoCreated
625625
626626walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
627627walkInvalidHeightParents s0 = do
628− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
628+ subscribers <- flip execStateT mempty $ ($ Some s0) $ fix $ \loop (Some s) -> do
629629 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
630630 when (h == invalidHeight) $ do
631631 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
632− modify (Some.This s :)
632+ modify (Some s :)
633633 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
634− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
634+ forM_ subscribers $ \(Some s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
635635 return subscribers
636636#endif
637637
@@ -659,7 +659,7 @@ behaviorPull !p = Behavior $ do
659659 val <- liftIO $ readIORef $ pullValue p
660660 case val of
661661 Just subscribed -> do
662− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
662+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
663663 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
664664 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
665665 return $ pullSubscribedValue subscribed
@@ -678,7 +678,7 @@ behaviorPull !p = Behavior $ do
678678 , pullSubscribedParents = parents
679679 }
680680 liftIO $ writeIORef (pullValue p) $ Just subscribed
681− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
681+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
682682 return a
683683
684684behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
@@ -689,7 +689,7 @@ readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
689689readHoldTracked h = do
690690 result <- liftIO $ readIORef $ holdValue h
691691 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
692− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
692+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedHold h)) :))
693693 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
694694 return result
695695
@@ -705,7 +705,7 @@ readBehaviorUntracked b = do
705705
706706data Dynamic x p = Dynamic
707707 { dynamicCurrent :: !(Behavior x (PatchTarget p))
708− , dynamicUpdated :: !(Event x p)
708+ , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
709709 }
710710
711711dynamicHold :: Hold x p -> Dynamic x p
@@ -782,9 +782,9 @@ data EventEnv x
782782 , eventEnvDynInits :: !(IORef [SomeDynInit x])
783783 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
784784 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
785− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
786− , eventEnvIntClears :: !(IORef [SomeIntClear])
787− , eventEnvRootClears :: !(IORef [SomeRootClear])
785+ , eventEnvClears :: !(IORef [Some Clear]) -- Needed for Subscribe
786+ , eventEnvIntClears :: !(IORef [Some IntClear])
787+ , eventEnvRootClears :: !(IORef [Some RootClear])
788788 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
789789 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
790790 , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
@@ -856,29 +856,29 @@ putCurrentHeight h = do
856856 heightRef <- asksEventEnv eventEnvCurrentHeight
857857 liftIO $ writeIORef heightRef $! h
858858
859−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
859+instance HasSpiderTimeline x => Defer (Some Clear) (EventM x) where
860860 {-# INLINE getDeferralQueue #-}
861861 getDeferralQueue = asksEventEnv eventEnvClears
862862
863863{-# INLINE scheduleClear #-}
864−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
865−scheduleClear r = defer $ SomeClear r
864+scheduleClear :: Defer (Some Clear) m => IORef (Maybe a) -> m ()
865+scheduleClear r = defer $ Some $ Clear r
866866
867−instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
867+instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
868868 {-# INLINE getDeferralQueue #-}
869869 getDeferralQueue = asksEventEnv eventEnvIntClears
870870
871871{-# INLINE scheduleIntClear #-}
872−scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
873−scheduleIntClear r = defer $ SomeIntClear r
872+scheduleIntClear :: Defer (Some IntClear) m => IORef (IntMap a) -> m ()
873+scheduleIntClear r = defer $ Some $ IntClear r
874874
875−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
875+instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
876876 {-# INLINE getDeferralQueue #-}
877877 getDeferralQueue = asksEventEnv eventEnvRootClears
878878
879879{-# INLINE scheduleRootClear #-}
880−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
881−scheduleRootClear r = defer $ SomeRootClear r
880+scheduleRootClear :: Defer (Some RootClear) m => IORef (DMap k Identity) -> m ()
881+scheduleRootClear r = defer $ Some $ RootClear r
882882
883883instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
884884 {-# INLINE getDeferralQueue #-}
@@ -951,7 +951,7 @@ data BehaviorSubscribed x a
951951 = forall p. BehaviorSubscribedHold (Hold x p)
952952 | BehaviorSubscribedPull (PullSubscribed x a)
953953
954−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
954+newtype SomeBehaviorSubscribed x = SomeBehaviorSubscribed (Some (BehaviorSubscribed x))
955955
956956--type role PullSubscribed representational
957957data PullSubscribed x a
@@ -986,7 +986,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
986986#endif
987987 }
988988
989−data Root x (k :: * -> *)
989+data Root x k
990990 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
991991 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
992992 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1056,25 +1056,25 @@ heightBagVerify b@(HeightBag s c) = if
10561056heightBagVerify = id
10571057#endif
10581058
1059−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1060− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1061− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1062− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1059+data FanSubscribedChildren x k v a = FanSubscribedChildren
1060+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1061+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1062+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
10631063 }
10641064
1065−data FanSubscribed (x :: *) k
1066− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1067− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1068− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1065+data FanSubscribed x k v
1066+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1067+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1068+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
10691069 , fanSubscribedParent :: !(EventSubscription x)
10701070#ifdef DEBUG_NODEIDS
10711071 , fanSubscribedNodeId :: Int
10721072#endif
10731073 }
10741074
1075−data Fan x k
1076− = Fan { fanParent :: !(Event x (DMap k Identity))
1077− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1075+data Fan x k v
1076+ = Fan { fanParent :: !(Event x (DMap k v))
1077+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
10781078 }
10791079
10801080data SwitchSubscribed x a
@@ -1133,13 +1133,17 @@ instance HasSpiderTimeline x => Filterable (Event x) where
11331133
11341134instance HasSpiderTimeline x => Align (Event x) where
11351135 nil = eventNever
1136− align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1136+#if MIN_VERSION_these(0, 8, 0)
1137+instance HasSpiderTimeline x => Semialign (Event x) where
1138+#endif
1139+ align ea eb = mapMaybe dmapToThese $ mergeG coerce $ dynamicConst $
1140+ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
11371141
11381142data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
11391143 | BuildDyn !(EventM x (PatchTarget p), Event x p)
11401144 | HoldDyn !(Hold x p)
11411145
1142−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1146+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
11431147
11441148newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
11451149newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
@@ -1168,18 +1172,12 @@ buildDynamic readV0 v' = do
11681172 return d
11691173
11701174unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1171−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1172− where x = (readV0, v')
1175+unsafeBuildDynamic readV0 v' =
1176+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
11731177
11741178-- ResultM can read behaviors and events
11751179type ResultM = EventM
11761180
1177−{-# NOINLINE unsafeNewIORef #-}
1178−unsafeNewIORef :: a -> b -> IORef b
1179−unsafeNewIORef a b = unsafePerformIO $ do
1180− touch a
1181− newIORef b
1182−
11831181instance HasSpiderTimeline x => Functor (Event x) where
11841182 fmap f = push $ return . Just . f
11851183
@@ -1192,26 +1190,35 @@ push f e = cacheEvent (pushCheap f e)
11921190
11931191{-# INLINABLE pull #-}
11941192pull :: BehaviorM x a -> Behavior x a
1195−pull a = behaviorPull $ Pull
1196− { pullCompute = a
1197− , pullValue = unsafeNewIORef a Nothing
1193+pull a = unsafePerformIO $ do
1194+ ref <- newIORef Nothing
11981195#ifdef DEBUG_NODEIDS
1199− , pullNodeId = unsafeNodeId a
1196+ nid <- newNodeId
12001197#endif
1201− }
1198+ pure $ behaviorPull $ Pull
1199+ { pullCompute = a
1200+ , pullValue = ref
1201+#ifdef DEBUG_NODEIDS
1202+ , pullNodeId = nid
1203+#endif
1204+ }
12021205
12031206{-# INLINABLE switch #-}
12041207switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1205−switch a = eventSwitch $ Switch
1206− { switchParent = a
1207− , switchSubscribed = unsafeNewIORef a Nothing
1208− }
1208+switch a = unsafePerformIO $ do
1209+ ref <- newIORef Nothing
1210+ pure $ eventSwitch $ Switch
1211+ { switchParent = a
1212+ , switchSubscribed = ref
1213+ }
12091214
12101215coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1211−coincidence a = eventCoincidence $ Coincidence
1212− { coincidenceParent = a
1213− , coincidenceSubscribed = unsafeNewIORef a Nothing
1214− }
1216+coincidence a = unsafePerformIO $ do
1217+ ref <- newIORef Nothing
1218+ pure $ eventCoincidence $ Coincidence
1219+ { coincidenceParent = a
1220+ , coincidenceSubscribed = ref
1221+ }
12151222
12161223-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
12171224run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
@@ -1256,11 +1263,11 @@ scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
12561263 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
12571264 EQ -> a
12581265
1259−data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1266+newtype Clear a = Clear (IORef (Maybe a))
12601267
1261−data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
1268+newtype IntClear a = IntClear (IORef (IntMap a))
12621269
1263−data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
1270+newtype RootClear k = RootClear (IORef (DMap k Identity))
12641271
12651272data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
12661273
@@ -1415,6 +1422,9 @@ getRootSubscribed k r sub = do
14151422 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
14161423 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
14171424 writeIORef uninitRef $! uninit
1425+#ifdef DEBUG_NODEIDS
1426+ nid <- newNodeId
1427+#endif
14181428 let !subscribed = RootSubscribed
14191429 { rootSubscribedKey = k
14201430 , rootSubscribedCachedSubscribed = cached
@@ -1423,7 +1433,7 @@ getRootSubscribed k r sub = do
14231433 , rootSubscribedUninit = uninit
14241434 , rootSubscribedWeakSelf = weakSelf
14251435#ifdef DEBUG_NODEIDS
1426− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1436+ , rootSubscribedNodeId = nid
14271437#endif
14281438 }
14291439 -- If we die at the same moment that all our children die, they will
@@ -1472,16 +1482,10 @@ newFanInt = do
14721482 , _fanInt_occRef = occRef
14731483 }
14741484
1475−{-# NOINLINE unsafeNewFanInt #-}
1476−unsafeNewFanInt :: b -> FanInt x a
1477−unsafeNewFanInt b = unsafePerformIO $ do
1478− touch b
1479− newFanInt
1480−
14811485fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1482−fanInt p =
1483− let self = unsafeNewFanInt p
1484− in EventSelectorInt $ \k -> Event $ \sub -> do
1486+fanInt p = unsafePerformIO $ do
1487+ self <- newFanInt
1488+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
14851489 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
14861490 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
14871491 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1517,7 +1521,7 @@ fanInt p =
15171521 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
15181522
15191523{-# INLINABLE getFanSubscribed #-}
1520−getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
1524+getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k v -> Subscriber x (v a) -> EventM x (WeakBagTicket, FanSubscribed x k v, Maybe (v a))
15211525getFanSubscribed k f sub = do
15221526 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
15231527 case mSubscribed of
@@ -1535,13 +1539,16 @@ getFanSubscribed k f sub = do
15351539 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
15361540 occRef <- liftIO $ newIORef parentOcc
15371541 when (isJust parentOcc) $ scheduleClear occRef
1542+#ifdef DEBUG_NODEIDS
1543+ nid <- liftIO newNodeId
1544+#endif
15381545 let subscribed = FanSubscribed
15391546 { fanSubscribedCachedSubscribed = fanSubscribed f
15401547 , fanSubscribedOccurrence = occRef
15411548 , fanSubscribedParent = subscription
15421549 , fanSubscribedSubscribers = subscribersRef
15431550#ifdef DEBUG_NODEIDS
1544− , fanSubscribedNodeId = unsafeNodeId f
1551+ , fanSubscribedNodeId = nid
15451552#endif
15461553 }
15471554 let !self = (k, subscribed)
@@ -1551,7 +1558,7 @@ getFanSubscribed k f sub = do
15511558 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
15521559 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
15531560
1554−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1561+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
15551562cleanupFanSubscribed (k, subscribed) = do
15561563 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15571564 let reducedSubscribers = DMap.delete k subscribers
@@ -1563,7 +1570,7 @@ cleanupFanSubscribed (k, subscribed) = do
15631570 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
15641571
15651572{-# INLINE subscribeFanSubscribed #-}
1566−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1573+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k v -> Subscriber x (v a) -> IO WeakBagTicket
15671574subscribeFanSubscribed k subscribed sub = do
15681575 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15691576 case DMap.lookup k subscribers of
@@ -1601,6 +1608,9 @@ getSwitchSubscribed s sub = do
16011608 when (isJust parentOcc) $ scheduleClear occRef
16021609 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
16031610 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1611+#ifdef DEBUG_NODEIDS
1612+ nid <- liftIO newNodeId
1613+#endif
16041614 let !subscribed = SwitchSubscribed
16051615 { switchSubscribedCachedSubscribed = switchSubscribed s
16061616 , switchSubscribedOccurrence = occRef
@@ -1613,7 +1623,7 @@ getSwitchSubscribed s sub = do
16131623 , switchSubscribedCurrentParent = subscriptionRef
16141624 , switchSubscribedWeakSelf = weakSelf
16151625#ifdef DEBUG_NODEIDS
1616− , switchSubscribedNodeId = unsafeNodeId s
1626+ , switchSubscribedNodeId = nid
16171627#endif
16181628 }
16191629 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1658,6 +1668,9 @@ getCoincidenceSubscribed c sub = do
16581668 scheduleClear innerSubdRef
16591669 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
16601670 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1671+#ifdef DEBUG_NODEIDS
1672+ nid <- liftIO newNodeId
1673+#endif
16611674 let subscribed = CoincidenceSubscribed
16621675 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
16631676 , coincidenceSubscribedOccurrence = occRef
@@ -1668,7 +1681,7 @@ getCoincidenceSubscribed c sub = do
16681681 , coincidenceSubscribedInnerParent = innerSubdRef
16691682 , coincidenceSubscribedWeakSelf = weakSelf
16701683#ifdef DEBUG_NODEIDS
1671− , coincidenceSubscribedNodeId = unsafeNodeId c
1684+ , coincidenceSubscribedNodeId = nid
16721685#endif
16731686 }
16741687 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1685,26 +1698,34 @@ cleanupCoincidenceSubscribed subscribed = do
16851698subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
16861699subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
16871700
1688−{-# INLINE merge #-}
1689−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1690−merge d = cacheEvent (mergeCheap d)
1701+{-# INLINE mergeG #-}
1702+mergeG :: forall k q x v. (HasSpiderTimeline x, GCompare k)
1703+ => (forall a. q a -> Event x (v a))
1704+ -> Dynamic x (PatchDMap k q) -> Event x (DMap k v)
1705+mergeG nt d = cacheEvent (mergeCheap nt d)
16911706
16921707{-# INLINE mergeWithMove #-}
1693−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1694−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1708+mergeWithMove :: forall k v q x. (HasSpiderTimeline x, GCompare k)
1709+ => (forall a. q a -> Event x (v a))
1710+ -> Dynamic x (PatchDMapWithMove k q) -> Event x (DMap k v)
1711+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
16951712
16961713{-# INLINE [1] mergeCheap #-}
1697−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1698−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1714+mergeCheap
1715+ :: forall k x q v. (HasSpiderTimeline x, GCompare k)
1716+ => (forall a. q a -> Event x (v a))
1717+ -> Dynamic x (PatchDMap k q)
1718+ -> Event x (DMap k v)
1719+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
16991720 where
1700− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1721+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
17011722 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
17021723 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
17031724 (mOldSubd, newPs) <- case me of
17041725 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
17051726 Just e -> do
17061727 let s = subscriber $ return k
1707− subscription@(EventSubscription _ subd) <- subscribe e s
1728+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
17081729 newParentHeight <- liftIO $ getEventSubscribedHeight subd
17091730 let newParent = MergeSubscribedParent subscription
17101731 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1714,28 +1735,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
17141735 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
17151736 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
17161737 foldM f ([], oldParents) $ DMap.toList p
1717− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1738+
1739+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
17181740 getInitialSubscribers initialParents subscriber = do
17191741 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17201742 let s = subscriber $ return k
1721− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1743+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17221744 height <- liftIO $ getEventSubscribedHeight parentSubd
1723− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1745+ return (fmap (\x -> k :=> x) parentOcc, height, k :=> MergeSubscribedParent subscription)
17241746 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17251747 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17261748 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17271749 )
1750+
17281751 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
17291752 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
17301753
17311754{-# INLINE [1] mergeCheapWithMove #-}
1732−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1733−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1755+mergeCheapWithMove :: forall k x v q. (HasSpiderTimeline x, GCompare k)
1756+ => (forall a. q a -> Event x (v a))
1757+ -> Dynamic x (PatchDMapWithMove k q)
1758+ -> Event x (DMap k v)
1759+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
17341760 where
1735− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1761+ updateMe :: MergeUpdateFunc k v x (PatchDMapWithMove k q) (MergeSubscribedParentWithMove x k)
17361762 updateMe subscriber heightBagRef oldParents p = do
17371763 -- Prepare new parents for insertion
1738− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1764+ let subscribeParent :: forall a. k a -> Event x (v a) -> EventM x (MergeSubscribedParentWithMove x k a)
17391765 subscribeParent k e = do
17401766 keyRef <- liftIO $ newIORef k
17411767 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1744,9 +1770,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17441770 newParentHeight <- getEventSubscribedHeight subd
17451771 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
17461772 return $ MergeSubscribedParentWithMove subscription keyRef
1747− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1773+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
17481774 -- Collect old parents for deletion and update the keys of moved parents
1749− let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Event x) a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1775+ let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k q a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
17501776 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
17511777 Nothing -> do
17521778 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1757,46 +1783,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17571783 return Nothing
17581784 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
17591785 return (toDelete, applyAlways p' oldParents)
1760− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1786+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
17611787 getInitialSubscribers initialParents subscriber = do
17621788 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17631789 keyRef <- liftIO $ newIORef k
17641790 let s = subscriber $ liftIO $ readIORef keyRef
1765− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1791+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17661792 height <- liftIO $ getEventSubscribedHeight parentSubd
1767− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1793+ return (fmap (\x -> k :=> x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
17681794 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17691795 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17701796 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17711797 )
1798+
17721799 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
17731800 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
17741801
1775−type MergeUpdateFunc k x p s
1776− = (forall a. EventM x (k a) -> Subscriber x a)
1802+type MergeUpdateFunc k v x p s
1803+ = (forall a. EventM x (k a) -> Subscriber x (v a))
17771804 -> IORef HeightBag
17781805 -> DMap k s
17791806 -> p
17801807 -> EventM x ([EventSubscription x], DMap k s)
17811808
1782−type MergeInitFunc k x s
1783− = DMap k (Event x)
1784− -> (forall a. EventM x (k a) -> Subscriber x a)
1785− -> EventM x (DMap k Identity, [Height], DMap k s)
1809+type MergeInitFunc k v q x s
1810+ = DMap k q
1811+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1812+ -> EventM x (DMap k v, [Height], DMap k s)
17861813
17871814type MergeDestroyFunc k s
17881815 = DMap k s
17891816 -> IO ()
17901817
1791−data Merge x k s = Merge
1818+data Merge x k v s = Merge
17921819 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
17931820 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
17941821 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1795− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1796− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1822+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1823+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
17971824 }
17981825
1799−invalidateMergeHeight :: Merge x k s -> IO ()
1826+invalidateMergeHeight :: Merge x k v s -> IO ()
18001827invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
18011828
18021829invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1806,8 +1833,7 @@ invalidateMergeHeight' heightRef sub = do
18061833 writeIORef heightRef $! invalidHeight
18071834 subscriberInvalidateHeight sub oldHeight
18081835
1809−
1810−revalidateMergeHeight :: Merge x k s -> IO ()
1836+revalidateMergeHeight :: Merge x k v s -> IO ()
18111837revalidateMergeHeight m = do
18121838 currentHeight <- readIORef $ _merge_heightRef m
18131839 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
@@ -1823,19 +1849,19 @@ revalidateMergeHeight m = do
18231849 subscriberRecalculateHeight (_merge_sub m) height
18241850 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
18251851
1826−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1852+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
18271853scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
18281854 vals <- liftIO $ readIORef $ _merge_accumRef m
18291855 liftIO $ writeIORef (_merge_accumRef m) $! DMap.empty -- Once we're done with this, we can clear it immediately, because if there's a cacheEvent in front of us, it'll handle subsequent subscribers, and if not, we won't get subsequent subscribers
18301856 --TODO: Assert that m is not empty
18311857 subscriberPropagate (_merge_sub m) vals
18321858
1833−mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1859+mergeSubscriber :: forall x k v s a. (HasSpiderTimeline x, GCompare k) => Merge x k v s -> EventM x (k a) -> Subscriber x (v a)
18341860mergeSubscriber m getKey = Subscriber
18351861 { subscriberPropagate = \a -> do
18361862 oldM <- liftIO $ readIORef $ _merge_accumRef m
18371863 k <- getKey
1838− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1864+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k a oldM
18391865 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
18401866 liftIO $ writeIORef (_merge_accumRef m) $! newM
18411867 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1850,7 +1876,7 @@ mergeSubscriber m getKey = Subscriber
18501876 else liftIO $ do
18511877#ifdef DEBUG_CYCLES
18521878 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1853− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1879+ stacks <- forM nodesInvolvedInCycle $ \(Some es) -> whoCreatedEventSubscribed es
18541880 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
18551881#else
18561882 let cycleInfo = ""
@@ -1866,7 +1892,7 @@ mergeSubscriber m getKey = Subscriber
18661892 }
18671893
18681894--TODO: Be able to run as much of this as possible promptly
1869−updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k s -> MergeUpdateFunc k x p s -> p -> SomeMergeUpdate x
1895+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k v s -> MergeUpdateFunc k v x p s -> p -> SomeMergeUpdate x
18701896updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
18711897 where updateMe = do
18721898 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1874,9 +1900,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
18741900 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
18751901 return subscriptionsToKill
18761902
1877−{-# INLINE mergeCheap' #-}
1878−mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> Dynamic x p -> Event x (DMap k Identity)
1879−mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1903+{-# INLINE mergeGCheap' #-}
1904+mergeGCheap' :: forall k v x p s q. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k q)
1905+ => MergeInitFunc k v q x s -> MergeUpdateFunc k v x p s -> MergeDestroyFunc k s -> Dynamic x p -> Event x (DMap k v)
1906+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
18801907 initialParents <- readBehaviorUntracked $ dynamicCurrent d
18811908 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
18821909 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -2025,14 +2052,16 @@ mergeIntCheap d = Event $ \sub -> do
20252052 )
20262053
20272054newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2055+newtype EventSelectorG x k v = EventSelectorG { selectG :: forall a. k a -> Event x (v a) }
20282056
2029−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2030−fan e =
2057+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2058+fanG e = unsafePerformIO $ do
2059+ ref <- newIORef Nothing
20312060 let f = Fan
20322061 { fanParent = e
2033− , fanSubscribed = unsafeNewIORef e Nothing
2062+ , fanSubscribed = ref
20342063 }
2035− in EventSelector $ \k -> eventFan k f
2064+ pure $ EventSelectorG $ \k -> eventFan k f
20362065
20372066runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
20382067runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2093,11 +2122,11 @@ runFrame a = SpiderHost $ do
20932122 return result
20942123 result <- runEventM go
20952124 toClear <- readIORef $ eventEnvClears env
2096− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
2125+ forM_ toClear $ \(Some (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
20972126 toClearInt <- readIORef $ eventEnvIntClears env
2098− forM_ toClearInt $ \(SomeIntClear ref) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2127+ forM_ toClearInt $ \(Some (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
20992128 toClearRoot <- readIORef $ eventEnvRootClears env
2100− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2129+ forM_ toClearRoot $ \(Some (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
21012130 toAssign <- readIORef $ eventEnvAssignments env
21022131 toReconnectRef <- newIORef []
21032132 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
@@ -2457,9 +2486,9 @@ unsafeNewSpiderTimelineEnv = do
24572486
24582487-- | Create a new SpiderTimelineEnv
24592488newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2460−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2489+newSpiderTimeline = withSpiderTimeline (pure . Some)
24612490
2462−data LocalSpiderTimeline x s
2491+data LocalSpiderTimeline (x :: Type) s
24632492
24642493instance Reifies s (SpiderTimelineEnv x) =>
24652494 HasSpiderTimeline (LocalSpiderTimeline x s) where
@@ -2477,11 +2506,11 @@ withSpiderTimeline k = do
24772506 env <- unsafeNewSpiderTimelineEnv
24782507 reify env $ \s -> k $ localSpiderTimeline s env
24792508
2480−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2509+newtype SpiderPullM (x :: Type) a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24812510
24822511type ComputeM = EventM
24832512
2484−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2513+newtype SpiderPushM (x :: Type) a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24852514
24862515instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
24872516 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
@@ -2501,10 +2530,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25012530 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
25022531 {-# INLINABLE pull #-}
25032532 pull = SpiderBehavior . pull . coerce
2504− {-# INLINABLE merge #-}
2505− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2506− {-# INLINABLE fan #-}
2507− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2533+ {-# INLINABLE fanG #-}
2534+ fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2535+ {-# INLINABLE mergeG #-}
2536+ mergeG
2537+ :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2538+ => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2539+ -> DMap k q
2540+ -> R.Event (SpiderTimeline x) (DMap k v)
2541+ mergeG nt = SpiderEvent . mergeG (unSpiderEvent #. nt) . dynamicConst
25082542 {-# INLINABLE switch #-}
25092543 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
25102544 {-# INLINABLE coincidence #-}
@@ -2517,10 +2551,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25172551 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
25182552 {-# INLINABLE unsafeBuildIncremental #-}
25192553 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2520− {-# INLINABLE mergeIncremental #-}
2521− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2522− {-# INLINABLE mergeIncrementalWithMove #-}
2523− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2554+ {-# INLINABLE mergeIncrementalG #-}
2555+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2556+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2557+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
25242558 {-# INLINABLE currentIncremental #-}
25252559 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
25262560 {-# INLINABLE updatedIncremental #-}
@@ -2558,7 +2592,7 @@ instance MonadAtomicRef (EventM x) where
25582592 atomicModifyRef r f = liftIO $ atomicModifyRef r f
25592593
25602594-- | The monad for actions that manipulate a Spider timeline identified by @x@
2561−newtype SpiderHost x a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2595+newtype SpiderHost (x :: Type) a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25622596
25632597instance Monad (SpiderHost x) where
25642598 {-# INLINABLE (>>=) #-}
@@ -2580,7 +2614,7 @@ runSpiderHost (SpiderHost a) = a
25802614runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
25812615runSpiderHostForTimeline (SpiderHost a) _ = a
25822616
2583−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2617+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
25842618 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25852619
25862620instance Monad (SpiderHostFrame x) where
src/Reflex/Workflow.hs+1−1
@@ -41,7 +41,7 @@ workflowView w0 = do
4141 eReplace <- fmap switch $ hold never $ fmap snd eResult
4242 return $ fmap fst eResult
4343
44−-- | Map a function over a 'Workflow', possibly changing the resturn type.
44+-- | Map a function over a 'Workflow', possibly changing the return type.
4545mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
4646mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
4747
test/GC.hs+4−3
@@ -27,6 +27,7 @@ import qualified Reflex.Spider.Internal as S
2727
2828import System.Exit
2929import System.Mem
30+import Data.Coerce
3031
3132main :: IO ()
3233main = do
@@ -46,7 +47,7 @@ hostPerf ref = S.runSpiderHost $ do
4647 eventToPerform <- Host.runHostFrame $ do
4748 (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
4849 <- S.SpiderHostFrame
49− $ fmap ( S.merge
50+ $ fmap ( S.mergeG coerce
5051 . S.dynamicHold)
5152 $ S.hold DMap.empty
5253 -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
@@ -55,8 +56,8 @@ hostPerf ref = S.runSpiderHost $ do
5556 { S.subscriberPropagate = S.subscriberPropagate sub
5657 }
5758 return (s, o))
58− $ runIdentity <$> S.select
59− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
59+ $ runIdentity . runIdentity <$> S.selectG
60+ (S.fanG $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
6061 (WrapArg Request)
6162 return $ alignWith (mergeThese (<>))
6263 (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do