on develop help sign in
Merge branch 'develop' into patch-1
commit4b8fa39e85fcf78bf33085eef5947d332dcb5df1 authorElliot Cameron <3noch@users.noreply.github.com> dateThu 27 Aug 2020, 14:08 (6 years ago) parent66cf0cc41add85
27 files changed+437−79
ChangeLog.md+19 README.md+12−14 reflex.cabal+26−12 release.nix+2−2 src/Reflex/Adjustable/Class.hs+2−1 src/Reflex/BehaviorWriter/Base.hs+16−7 src/Reflex/BehaviorWriter/Class.hs+11−5 src/Reflex/Class.hs+22−2 src/Reflex/Dynamic.hs+14 src/Reflex/DynamicWriter/Base.hs+13−4 src/Reflex/EventWriter/Base.hs+16−5 src/Reflex/FastWeak.hs+10 src/Reflex/Host/Headless.hs+126 src/Reflex/PerformEvent/Base.hs+2 src/Reflex/PerformEvent/Class.hs+6 src/Reflex/PostBuild/Base.hs+2 src/Reflex/Profiled.hs+56−2 src/Reflex/Pure.hs+6−1 src/Reflex/Query/Base.hs+9−3 src/Reflex/Query/Class.hs+5−5 src/Reflex/Requester/Base.hs+6−1 src/Reflex/Spider/Internal.hs+38−12 src/Reflex/TriggerEvent/Base.hs+2 src/Reflex/TriggerEvent/Class.hs+6 test/Reflex/Plan/Pure.hs+1 test/Reflex/Test/CrossImpl.hs+1−1 test/Reflex/Test/Micro.hs+8−2
ChangeLog.md+19
@@ -1,5 +1,24 @@
11# Revision history for reflex
22
3+## Unreleased
4+* ([#416](https://github.com/reflex-frp/reflex/pull/416)) Add `now :: m (Event t ())` to `MonadHold`.
5+
6+## 0.7.1.0
7+
8+* ([#413](https://github.com/reflex-frp/reflex/pull/413), [#417](https://github.com/reflex-frp/reflex/pull/417)) Add `Reflex.Host.Headless` module which provides `runHeadlessApp` as an easy way to run a Reflex network in a "headless" environment.
9+* ([#420](https://github.com/reflex-frp/reflex/pull/420)) Add a [`Data.Zip.Unzip`](https://hackage.haskell.org/package/semialign-1.1/docs/Data-Zip.html#t:Unzip) instance for `Event`.
10+* ([#419](https://github.com/reflex-frp/reflex/pull/419)) Add `distributeIntMapOverDynPure` and `joinDynThroughIntMap` as convenience functions for working with `Dynamic` `IntMap`s.
11+
12+
13+## 0.7.0.0
14+
15+* Add lifting instances for most classes to `Reflex.Profiled.Profiled`. ([#398](https://github.com/reflex-frp/reflex/pull/398))
16+* Class `MonadQuery t q m` now has a `Monad m` superclass constraint. ([#400](https://github.com/reflex-frp/reflex/pull/400))
17+* **(Breaking change)** Rename class `MonadBehaviorWriter` -> `BehaviorWriter` for consistency with `EventWriter`/`DynamicWriter`. ([#401](https://github.com/reflex-frp/reflex/pull/401))
18+* Introduce deprecated alias `MonadBehaviorWriter = BehaviorWriter`. ([#401](https://github.com/reflex-frp/reflex/pull/401))
19+* Fix bug in spider where event subscriptions would be prematurely finalized due to over-aggressive inlining. ([#409](https://github.com/reflex-frp/reflex/pull/409))
20+* Add instances of `PerformEvent` and `TriggerEvent` for `MaybeT`. ([#395](https://github.com/reflex-frp/reflex/pull/395))
21+
322## 0.6.4.1
423
524* Fix a bug in the Reflex Profiled transformer where
README.md+12−14
@@ -1,25 +1,23 @@
11## [Reflex](https://reflex-frp.org/)
22
3−[![Hackage](https://img.shields.io/hackage/v/reflex.svg)](http://hackage.haskell.org/package/reflex)
3+[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/reflex.svg)](https://hackage.haskell.org/package/reflex) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/reflex/badge)](https://matrix.hackage.haskell.org/#/package/reflex) [![Travis CI](https://api.travis-ci.org/reflex-frp/reflex.svg?branch=master)](https://travis-ci.org/reflex-frp/reflex) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE)
44
5−### Practical Functional Reactive Programming
5+Interactive programs without callbacks or side-effects. Functional Reactive Programming (FRP) uses composable events and time-varying values to describe interactive systems as pure functions. Just like other pure functional code, functional reactive code is easier to get right on the first try, maintain, and reuse.
66
7−Reflex is a fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.
7+Reflex is a fully-deterministic, higher-order Functional Reactive Programming interface and an engine that efficiently implements that interface.
88
9−[Reflex-DOM](https://github.com/reflex-frp/reflex-dom) is a framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps.
10−
11−A summary of Reflex functions is available in the [quick reference](Quickref.md).
12−
13−**Visit https://reflex-frp.org/ for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
9+**Visit https://reflex-frp.org for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
1410
1511### Resources
16−[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
17−
18−[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
1912
20−[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
13+* [Official Website](https://reflex-frp.org)
14+* [Quick Reference](Quickref.md)
15+* [Reflex-DOM](https://github.com/reflex-frp/reflex-dom): A framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps.
16+* [Obelisk](https://github.com/obsidiansystems/obelisk#obelisk): A framework built on Reflex and Reflex-DOM for functional reactive web and mobile applications, with batteries included.
17+* [Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
18+* [/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
19+* [irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
2120
2221### Hacking
2322
24−Use the `./scripts/hack-on reflex` script in [Reflex Platform](https://github.com/reflex-frp/reflex-platform) to checkout the source code of `reflex` locally in `reflex-platform/reflex` directory.
25−Then do modifications to the source in place, and use the `./try-reflex` or `./scripts/work-on` scripts to create the shell to test your changes.
23+From the root of a [Reflex Platform](https://github.com/reflex-frp/reflex-platform) checkout, run `./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex`. This will check out the reflex source code into the `haskell-overlays/reflex-packages/dep/reflex` directory. You can then point that checkout at your fork, make changes, etc. Use the `./try-reflex` or `./scripts/work-on` scripts to start a shell in which you can test your changes.
reflex.cabal+26−12
@@ -1,7 +1,17 @@
11Name: reflex
2−Version: 0.6.4.1
2+Version: 0.7.1.0
33Synopsis: Higher-order Functional Reactive Programming
4−Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
4+Description:
5+ Interactive programs without callbacks or side-effects.
6+ Functional Reactive Programming (FRP) uses composable events and time-varying
7+ values to describe interactive systems as pure functions.
8+ Just like other pure functional code, functional reactive code is easier
9+ to get right on the first try, maintain, and reuse.
10+ .
11+ Reflex is a fully-deterministic, higher-order Functional Reactive Programming
12+ interface and an engine that efficiently implements that interface.
13+ .
14+ <https://reflex-frp.org>
515License: BSD3
616License-file: LICENSE
717Author: Ryan Trinkle
@@ -67,13 +77,14 @@ library
6777 constraints-extras >= 0.3 && < 0.4,
6878 containers >= 0.6 && < 0.7,
6979 data-default >= 0.5 && < 0.8,
70− dependent-map >= 0.3 && < 0.4,
80+ dependent-map >= 0.3 && < 0.5,
7181 exception-transformers == 0.4.*,
7282 lens >= 4.7 && < 5,
83+ mmorph >= 1.0 && < 1.2,
7384 monad-control >= 1.0.1 && < 1.1,
7485 mtl >= 2.1 && < 2.3,
7586 patch >= 0.0.1 && < 0.1,
76− prim-uniq >= 0.1.0.1 && < 0.2,
87+ prim-uniq >= 0.1.0.1 && < 0.3,
7788 primitive >= 0.5 && < 0.8,
7889 profunctors >= 5.3 && < 5.6,
7990 random == 1.1.*,
@@ -85,10 +96,10 @@ library
8596 time >= 1.4 && < 1.10,
8697 transformers >= 0.5.6.0 && < 0.6,
8798 unbounded-delays >= 0.1.0.0 && < 0.2,
88− witherable >= 0.3 && < 0.3.2
99+ witherable >= 0.3 && < 0.4
89100
90101 if flag(split-these)
91− build-depends: these >= 1 && <1.1,
102+ build-depends: these >= 1 && <1.2,
92103 semialign >=1 && <1.2,
93104 monoidal-containers >= 0.6 && < 0.7
94105 else
@@ -119,6 +130,7 @@ library
119130 Reflex.FastWeak,
120131 Reflex.FunctorMaybe,
121132 Reflex.Host.Class,
133+ Reflex.Host.Headless,
122134 Reflex.Network,
123135 Reflex.NotReady.Class,
124136 Reflex.PerformEvent.Base,
@@ -153,7 +165,8 @@ library
153165
154166 if flag(debug-trace-events)
155167 cpp-options: -DDEBUG_TRACE_EVENTS
156− build-depends: bytestring
168+ build-depends:
169+ bytestring >= 0.10.8 && < 0.11
157170
158171 if flag(use-reflex-optimizer)
159172 cpp-options: -DUSE_REFLEX_OPTIMIZER
@@ -169,8 +182,8 @@ library
169182 if flag(use-template-haskell)
170183 cpp-options: -DUSE_TEMPLATE_HASKELL
171184 build-depends:
172− dependent-sum >= 0.6 && < 0.7,
173− haskell-src-exts >= 1.16 && < 1.23,
185+ dependent-sum >= 0.6 && < 0.8,
186+ haskell-src-exts >= 1.16 && < 1.24,
174187 haskell-src-meta >= 0.6 && < 0.9,
175188 template-haskell >= 2.9 && < 2.16
176189 exposed-modules:
@@ -178,13 +191,14 @@ library
178191 other-extensions: TemplateHaskell
179192 else
180193 build-depends:
181− dependent-sum >= 0.6 && < 0.7
194+ dependent-sum >= 0.6 && < 0.8
182195
183196 if flag(fast-weak) && impl(ghcjs)
184197 cpp-options: -DGHCJS_FAST_WEAK
185198
186199 if impl(ghcjs)
187− build-depends: ghcjs-base
200+ build-depends:
201+ ghcjs-base == 0.2.*
188202
189203test-suite semantics
190204 default-language: Haskell2010
@@ -243,7 +257,7 @@ test-suite hlint
243257 , directory
244258 , filepath
245259 , filemanip
246− , hlint < 2.1 || >= 2.2.2
260+ , hlint (< 2.1 || >= 2.2.2) && < 3
247261 if impl(ghcjs)
248262 buildable: False
249263
release.nix+2−2
@@ -1,12 +1,12 @@
11{ reflex-platform-fun ? import ./dep/reflex-platform
2+, supportedSystems ? ["x86_64-linux" "x86_64-darwin"]
23}:
34
45let
56 native-reflex-platform = reflex-platform-fun {};
67 inherit (native-reflex-platform.nixpkgs) lib;
7− systems = ["x86_64-linux" "x86_64-darwin"];
88
9− perPlatform = lib.genAttrs systems (system: let
9+ perPlatform = lib.genAttrs supportedSystems (system: let
1010 reflex-platform = reflex-platform-fun { inherit system; };
1111 compilers = [
1212 "ghc"
src/Reflex/Adjustable/Class.hs+2−1
@@ -30,7 +30,8 @@ module Reflex.Adjustable.Class
3030
3131import Control.Monad.Identity
3232import Control.Monad.Reader
33−import Data.Dependent.Map (DMap, GCompare (..))
33+import Data.Dependent.Map (DMap)
34+import Data.GADT.Compare (GCompare(..))
3435import qualified Data.Dependent.Map as DMap
3536import Data.Functor.Constant
3637import Data.Functor.Misc
src/Reflex/BehaviorWriter/Base.hs+16−7
@@ -1,6 +1,6 @@
11{-|
22Module: Reflex.BehaviorWriter.Base
3−Description: Implementation of MonadBehaviorWriter
3+Description: Implementation of BehaviorWriter
44-}
55{-# LANGUAGE CPP #-}
66{-# LANGUAGE FlexibleInstances #-}
@@ -24,6 +24,7 @@ module Reflex.BehaviorWriter.Base
2424import Control.Monad.Exception
2525import Control.Monad.Identity
2626import Control.Monad.IO.Class
27+import Control.Monad.Morph
2728import Control.Monad.Reader
2829import Control.Monad.Ref
2930import Control.Monad.State.Strict
@@ -46,9 +47,20 @@ import Reflex.Query.Class
4647import Reflex.Requester.Class
4748import Reflex.TriggerEvent.Class
4849
49−-- | A basic implementation of 'MonadBehaviorWriter'.
50+-- | A basic implementation of 'BehaviorWriter'.
5051newtype BehaviorWriterT t w m a = BehaviorWriterT { unBehaviorWriterT :: StateT [Behavior t w] m a }
51− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
52+ -- The list is kept in reverse order
53+ deriving
54+ ( Functor
55+ , Applicative
56+ , Monad
57+ , MonadTrans
58+ , MFunctor
59+ , MonadIO
60+ , MonadFix
61+ , MonadAsyncException
62+ , MonadException
63+ )
5264
5365-- | Run a 'BehaviorWriterT' action. The behavior writer output will be provided
5466-- along with the result of the action.
@@ -73,9 +85,6 @@ withBehaviorWriterT f dw = do
7385deriving instance MonadHold t m => MonadHold t (BehaviorWriterT t w m)
7486deriving instance MonadSample t m => MonadSample t (BehaviorWriterT t w m)
7587
76−instance MonadTrans (BehaviorWriterT t w) where
77− lift = BehaviorWriterT . lift
78−
7988instance MonadRef m => MonadRef (BehaviorWriterT t w m) where
8089 type Ref (BehaviorWriterT t w m) = Ref m
8190 newRef = lift . newRef
@@ -89,7 +98,7 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (BehaviorWri
8998 newEventWithTrigger = lift . newEventWithTrigger
9099 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
91100
92−instance (Monad m, Monoid w, Reflex t) => MonadBehaviorWriter t w (BehaviorWriterT t w m) where
101+instance (Monad m, Monoid w, Reflex t) => BehaviorWriter t w (BehaviorWriterT t w m) where
93102 tellBehavior w = BehaviorWriterT $ modify (w :)
94103
95104instance MonadReader r m => MonadReader r (BehaviorWriterT t w m) where
src/Reflex/BehaviorWriter/Class.hs+11−5
@@ -1,8 +1,9 @@
11{-|
22Module: Reflex.BehaviorWriter.Class
3−Description: This module defines the 'MonadBehaviorWriter' class
3+Description: This module defines the 'BehaviorWriter' class
44-}
55{-# LANGUAGE CPP #-}
6+{-# LANGUAGE ConstraintKinds #-}
67{-# LANGUAGE FlexibleInstances #-}
78{-# LANGUAGE FunctionalDependencies #-}
89{-# LANGUAGE UndecidableInstances #-}
@@ -10,16 +11,21 @@ Description: This module defines the 'MonadBehaviorWriter' class
1011{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1112#endif
1213module Reflex.BehaviorWriter.Class
13− ( MonadBehaviorWriter (..)
14+ ( MonadBehaviorWriter
15+ , BehaviorWriter(..)
1416 ) where
1517
1618import Control.Monad.Reader (ReaderT, lift)
1719import Reflex.Class (Behavior)
1820
19−-- | 'MonadBehaviorWriter' efficiently collects 'Behavior' values using 'tellBehavior'
21+{-# DEPRECATED MonadBehaviorWriter "Use 'BehaviorWriter' instead" #-}
22+-- | Type synonym for 'BehaviorWriter'
23+type MonadBehaviorWriter = BehaviorWriter
24+
25+-- | 'BehaviorWriter' efficiently collects 'Behavior' values using 'tellBehavior'
2026-- and combines them monoidally to provide a 'Behavior' result.
21−class (Monad m, Monoid w) => MonadBehaviorWriter t w m | m -> t w where
27+class (Monad m, Monoid w) => BehaviorWriter t w m | m -> t w where
2228 tellBehavior :: Behavior t w -> m ()
2329
24−instance MonadBehaviorWriter t w m => MonadBehaviorWriter t w (ReaderT r m) where
30+instance BehaviorWriter t w m => BehaviorWriter t w (ReaderT r m) where
2531 tellBehavior = lift . tellBehavior
src/Reflex/Class.hs+22−2
@@ -180,7 +180,7 @@ import Prelude hiding (zip, zipWith)
180180import Data.These.Combinators (justThese)
181181#endif
182182#if MIN_VERSION_semialign(1,1,0)
183−import Data.Zip (Zip (..))
183+import Data.Zip (Zip (..), Unzip (..))
184184#endif
185185#endif
186186
@@ -196,7 +196,8 @@ import Data.Align
196196import Data.Bifunctor
197197import Data.Coerce
198198import Data.Default
199−import Data.Dependent.Map (DMap, DSum (..))
199+import Data.Dependent.Map (DMap)
200+import Data.Dependent.Sum (DSum (..))
200201import qualified Data.Dependent.Map as DMap
201202import Data.Functor.Compose
202203import Data.Functor.Product
@@ -420,6 +421,13 @@ class MonadSample t m => MonadHold t m where
420421 -- | Create a new 'Event' that only occurs only once, on the first occurrence of
421422 -- the supplied 'Event'.
422423 headE :: Event t a -> m (Event t a)
424+ -- | An event which only occurs at the current moment in time, such that:
425+ --
426+ -- > coincidence (pushAlways (\a -> (a <$) <$> now) e) = e
427+ --
428+ now :: m (Event t ())
429+ default now :: (m ~ f m', MonadTrans f, MonadHold t m') => m (Event t ())
430+ now = lift now
423431
424432-- | Accumulate an 'Incremental' with the supplied initial value and the firings of the provided 'Event',
425433-- using the combining function to produce a patch.
@@ -565,6 +573,7 @@ instance MonadHold t m => MonadHold t (ReaderT r m) where
565573 holdIncremental a0 = lift . holdIncremental a0
566574 buildDynamic a0 = lift . buildDynamic a0
567575 headE = lift . headE
576+ now = lift now
568577
569578instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
570579 sample = lift . sample
@@ -575,6 +584,7 @@ instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
575584 holdIncremental a0 = lift . holdIncremental a0
576585 buildDynamic a0 = lift . buildDynamic a0
577586 headE = lift . headE
587+ now = lift now
578588
579589instance MonadSample t m => MonadSample t (StateT s m) where
580590 sample = lift . sample
@@ -585,6 +595,7 @@ instance MonadHold t m => MonadHold t (StateT s m) where
585595 holdIncremental a0 = lift . holdIncremental a0
586596 buildDynamic a0 = lift . buildDynamic a0
587597 headE = lift . headE
598+ now = lift now
588599
589600instance MonadSample t m => MonadSample t (ExceptT e m) where
590601 sample = lift . sample
@@ -595,6 +606,7 @@ instance MonadHold t m => MonadHold t (ExceptT e m) where
595606 holdIncremental a0 = lift . holdIncremental a0
596607 buildDynamic a0 = lift . buildDynamic a0
597608 headE = lift . headE
609+ now = lift now
598610
599611instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
600612 sample = lift . sample
@@ -605,6 +617,7 @@ instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
605617 holdIncremental a0 = lift . holdIncremental a0
606618 buildDynamic a0 = lift . buildDynamic a0
607619 headE = lift . headE
620+ now = lift now
608621
609622instance MonadSample t m => MonadSample t (ContT r m) where
610623 sample = lift . sample
@@ -615,6 +628,7 @@ instance MonadHold t m => MonadHold t (ContT r m) where
615628 holdIncremental a0 = lift . holdIncremental a0
616629 buildDynamic a0 = lift . buildDynamic a0
617630 headE = lift . headE
631+ now = lift now
618632
619633--------------------------------------------------------------------------------
620634-- Convenience functions
@@ -1091,6 +1105,12 @@ instance Reflex t => Zip (Event t) where
10911105 zip x y = mapMaybe justThese $ align x y
10921106#endif
10931107
1108+#ifdef MIN_VERSION_semialign
1109+#if MIN_VERSION_semialign(1,1,0)
1110+instance Reflex t => Unzip (Event t) where
1111+ unzip = splitE
1112+#endif
1113+#endif
10941114
10951115-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
10961116-- the 'Behavior' is true at the time of occurrence.
src/Reflex/Dynamic.hs+14
@@ -53,10 +53,12 @@ module Reflex.Dynamic
5353 , foldDynMaybe
5454 , foldDynMaybeM
5555 , joinDynThroughMap
56+ , joinDynThroughIntMap
5657 , traceDyn
5758 , traceDynWith
5859 , splitDynPure
5960 , distributeMapOverDynPure
61+ , distributeIntMapOverDynPure
6062 , distributeDMapOverDynPure
6163 , distributeListOverDynPure
6264 , Demux
@@ -88,6 +90,8 @@ import Data.Dependent.Map (DMap)
8890import qualified Data.Dependent.Map as DMap
8991import Data.Dependent.Sum (DSum (..))
9092import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
93+import Data.IntMap (IntMap)
94+import qualified Data.IntMap as IntMap
9195import Data.Map (Map)
9296import Data.Maybe
9397import Data.Monoid ((<>))
@@ -202,6 +206,11 @@ splitDynPure d = (fmap fst d, fmap snd d)
202206distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
203207distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
204208
209+-- | Convert an 'IntMap' with 'Dynamic' elements into a 'Dynamic' of an 'IntMap' with
210+-- non-'Dynamic' elements.
211+distributeIntMapOverDynPure :: (Reflex t) => IntMap (Dynamic t v) -> Dynamic t (IntMap v)
212+distributeIntMapOverDynPure = fmap dmapToIntMap . distributeDMapOverDynPure . intMapWithFunctorToDMap
213+
205214-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
206215-- non-'Dynamic' elements, preserving the order of the elements.
207216{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
@@ -214,6 +223,11 @@ distributeListOverDynPure = distributeListOverDyn
214223joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
215224joinDynThroughMap = (distributeMapOverDynPure =<<)
216225
226+-- | Combine a 'Dynamic' of an 'IntMap' of 'Dynamic's into a 'Dynamic'
227+-- with the current values of the 'Dynamic's in a map.
228+joinDynThroughIntMap :: forall t a. (Reflex t) => Dynamic t (IntMap (Dynamic t a)) -> Dynamic t (IntMap a)
229+joinDynThroughIntMap = (distributeIntMapOverDynPure =<<)
230+
217231-- | Print the value of the 'Dynamic' when it is first read and on each
218232-- subsequent change that is observed (as 'traceEvent'), prefixed with the
219233-- provided string. This should /only/ be used for debugging.
src/Reflex/DynamicWriter/Base.hs+13−4
@@ -22,6 +22,7 @@ module Reflex.DynamicWriter.Base
2222import Control.Monad.Exception
2323import Control.Monad.Identity
2424import Control.Monad.IO.Class
25+import Control.Monad.Morph
2526import Control.Monad.Primitive
2627import Control.Monad.Reader
2728import Control.Monad.Ref
@@ -51,9 +52,6 @@ import Reflex.Query.Class
5152import Reflex.Requester.Class
5253import Reflex.TriggerEvent.Class
5354
54−instance MonadTrans (DynamicWriterT t w) where
55− lift = DynamicWriterT . lift
56−
5755mapIncrementalMapValues :: (Reflex t, Patch (p v), Patch (p v'), PatchTarget (p v) ~ f v, PatchTarget (p v') ~ f v', Functor p, Functor f) => (v -> v') -> Incremental t (p v) -> Incremental t (p v')
5856mapIncrementalMapValues f = unsafeMapIncremental (fmap f) (fmap f)
5957
@@ -90,7 +88,18 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
9088
9189-- | A basic implementation of 'DynamicWriter'.
9290newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
93− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
91+ -- The list is kept in reverse order
92+ deriving
93+ ( Functor
94+ , Applicative
95+ , Monad
96+ , MonadTrans
97+ , MFunctor
98+ , MonadIO
99+ , MonadFix
100+ , MonadAsyncException
101+ , MonadException
102+ )
94103
95104deriving instance MonadHold t m => MonadHold t (DynamicWriterT t w m)
96105deriving instance MonadSample t m => MonadSample t (DynamicWriterT t w m)
src/Reflex/EventWriter/Base.hs+16−5
@@ -35,12 +35,14 @@ import Reflex.TriggerEvent.Class
3535
3636import Control.Monad.Exception
3737import Control.Monad.Identity
38+import Control.Monad.Morph
3839import Control.Monad.Primitive
3940import Control.Monad.Reader
4041import Control.Monad.Ref
4142import Control.Monad.State.Strict
42−import Data.Dependent.Map (DMap, DSum (..))
43+import Data.Dependent.Map (DMap)
4344import qualified Data.Dependent.Map as DMap
45+import Data.Dependent.Sum (DSum (..))
4446import Data.Functor.Compose
4547import Data.Functor.Misc
4648import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
@@ -96,7 +98,17 @@ data EventWriterState t w = EventWriterState
9698
9799-- | A basic implementation of 'EventWriter'.
98100newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (EventWriterState t w) m a }
99− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
101+ deriving
102+ ( Functor
103+ , Applicative
104+ , Monad
105+ , MonadTrans
106+ , MFunctor
107+ , MonadFix
108+ , MonadIO
109+ , MonadException
110+ , MonadAsyncException
111+ )
100112
101113-- | Run a 'EventWriterT' action.
102114runEventWriterT :: forall t m w a. (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
@@ -116,9 +128,6 @@ instance (Reflex t, Monad m, Semigroup w) => EventWriter t w (EventWriterT t w m
116128 , _eventWriterState_told = (tellId myId :=> w) : _eventWriterState_told old
117129 }
118130
119−instance MonadTrans (EventWriterT t w) where
120− lift = EventWriterT . lift
121−
122131instance MonadSample t m => MonadSample t (EventWriterT t w m) where
123132 sample = lift . sample
124133
@@ -133,6 +142,8 @@ instance MonadHold t m => MonadHold t (EventWriterT t w m) where
133142 buildDynamic a0 = lift . buildDynamic a0
134143 {-# INLINABLE headE #-}
135144 headE = lift . headE
145+ {-# INLINABLE now #-}
146+ now = lift now
136147
137148instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
138149 runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
src/Reflex/FastWeak.hs+10
@@ -151,6 +151,16 @@ getFastWeakTicket w = do
151151
152152-- | Create a 'FastWeakTicket' directly from a value, creating a 'FastWeak' in the process
153153-- which can be obtained with 'getFastWeakTicketValue'.
154+--
155+-- This function is marked NOINLINE so it is opaque to GHC.
156+-- If we do not do this, then GHC will sometimes fuse the constructor away
157+-- so any weak references that are attached to the ticket will have their
158+-- finalizer run. Using the opaque constructor, GHC does not see the
159+-- constructor application, so it behaves like an IORef and cannot be fused away.
160+--
161+-- The result is also evaluated to WHNF, since forcing a thunk invalidates
162+-- the weak pointer to it in some cases.
163+{-# NOINLINE mkFastWeakTicket #-}
154164mkFastWeakTicket :: a -> IO (FastWeakTicket a)
155165-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the
156166-- associated FastWeak to actually be weak
src/Reflex/Host/Headless.hsnew file+126
@@ -0,0 +1,126 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE RankNTypes #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE ConstraintKinds #-}
6+
7+module Reflex.Host.Headless where
8+
9+import Control.Concurrent.Chan (newChan, readChan)
10+import Control.Monad.Fix (MonadFix, fix)
11+import Control.Monad.IO.Class (MonadIO, liftIO)
12+import Control.Monad.Primitive (PrimMonad)
13+import Control.Monad.Ref (MonadRef, Ref, readRef)
14+import Data.Dependent.Sum (DSum (..), (==>))
15+import Data.Foldable (for_)
16+import Data.Functor.Identity (Identity(..))
17+import Data.IORef (IORef, readIORef)
18+import Data.Maybe (catMaybes)
19+import Data.Traversable (for)
20+
21+import Reflex
22+import Reflex.Host.Class
23+
24+type MonadHeadlessApp t m =
25+ ( Adjustable t m
26+ , MonadFix m
27+ , MonadHold t m
28+ , MonadIO (HostFrame t)
29+ , MonadIO (Performable m)
30+ , MonadIO m
31+ , MonadRef (HostFrame t)
32+ , NotReady t m
33+ , PerformEvent t m
34+ , PostBuild t m
35+ , PrimMonad (HostFrame t)
36+ , Ref (HostFrame t) ~ IORef
37+ , Ref m ~ IORef
38+ , Reflex t
39+ , ReflexHost t
40+ , TriggerEvent t m
41+ )
42+
43+-- | Run a headless FRP network. Inside the action, you will most probably use
44+-- the capabilities provided by the 'TriggerEvent' and 'PerformEvent' type
45+-- classes to interface the FRP network with the outside world. Useful for
46+-- testing. Each headless network runs on its own spider timeline.
47+runHeadlessApp
48+ :: (forall t m. MonadHeadlessApp t m => m (Event t ()))
49+ -- ^ The action to be run in the headless FRP network. The FRP network is
50+ -- closed at the first occurrence of the resulting 'Event'.
51+ -> IO ()
52+runHeadlessApp guest =
53+ -- We are using the 'Spider' implementation of reflex. Running the host
54+ -- allows us to take actions on the FRP timeline.
55+ withSpiderTimeline $ runSpiderHostForTimeline $ do
56+ -- Create the "post-build" event and associated trigger. This event fires
57+ -- once, when the application starts.
58+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
59+ -- Create a queue to which we will write 'Event's that need to be
60+ -- processed.
61+ events <- liftIO newChan
62+ -- Run the "guest" application, providing the appropriate context. We'll
63+ -- pure the result of the action, and a 'FireCommand' that will be used to
64+ -- trigger events.
65+ (result, fc@(FireCommand fire)) <- do
66+ hostPerformEventT $ -- Allows the guest app to run
67+ -- 'performEvent', so that actions
68+ -- (e.g., IO actions) can be run when
69+ -- 'Event's fire.
70+
71+ flip runPostBuildT postBuild $ -- Allows the guest app to access to
72+ -- a "post-build" 'Event'
73+
74+ flip runTriggerEventT events $ -- Allows the guest app to create new
75+ -- events and triggers and write
76+ -- those triggers to a channel from
77+ -- which they will be read and
78+ -- processed.
79+ guest
80+
81+ -- Read the trigger reference for the post-build event. This will be
82+ -- 'Nothing' if the guest application hasn't subscribed to this event.
83+ mPostBuildTrigger <- readRef postBuildTriggerRef
84+
85+ -- When there is a subscriber to the post-build event, fire the event.
86+ for_ mPostBuildTrigger $ \postBuildTrigger ->
87+ fire [postBuildTrigger :=> Identity ()] $ pure ()
88+
89+ -- Subscribe to an 'Event' of that the guest application can use to
90+ -- request application shutdown. We'll check whether this 'Event' is firing
91+ -- to determine whether to terminate.
92+ shutdown <- subscribeEvent result
93+
94+ -- The main application loop. We wait for new events and fire those that
95+ -- have subscribers. If we detect a shutdown request, the application
96+ -- terminates.
97+ fix $ \loop -> do
98+ -- Read the next event (blocking).
99+ ers <- liftIO $ readChan events
100+ stop <- do
101+ -- Fire events that have subscribers.
102+ fireEventTriggerRefs fc ers $
103+ -- Check if the shutdown 'Event' is firing.
104+ readEvent shutdown >>= \case
105+ Nothing -> pure False
106+ Just _ -> pure True
107+ if or stop
108+ then pure ()
109+ else loop
110+ where
111+ -- Use the given 'FireCommand' to fire events that have subscribers
112+ -- and call the callback for the 'TriggerInvocation' of each.
113+ fireEventTriggerRefs
114+ :: MonadIO m
115+ => FireCommand t m
116+ -> [DSum (EventTriggerRef t) TriggerInvocation]
117+ -> ReadPhase m a
118+ -> m [a]
119+ fireEventTriggerRefs (FireCommand fire) ers rcb = do
120+ mes <- liftIO $
121+ for ers $ \(EventTriggerRef er :=> TriggerInvocation a _) -> do
122+ me <- readIORef er
123+ pure $ fmap (==> a) me
124+ a <- fire (catMaybes mes) rcb
125+ liftIO $ for_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
126+ pure a
src/Reflex/PerformEvent/Base.hs+2
@@ -164,6 +164,8 @@ instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
164164 buildDynamic getV0 v' = PerformEventT $ lift $ buildDynamic getV0 v'
165165 {-# INLINABLE headE #-}
166166 headE = PerformEventT . lift . headE
167+ {-# INLINABLE now #-}
168+ now = PerformEventT . lift $ now
167169
168170instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
169171 type Ref (PerformEventT t m) = Ref (HostFrame t)
src/Reflex/PerformEvent/Class.hs+6
@@ -22,6 +22,7 @@ import Reflex.Class
2222import Reflex.TriggerEvent.Class
2323
2424import Control.Monad.Reader
25+import Control.Monad.Trans.Maybe (MaybeT (..))
2526
2627-- | 'PerformEvent' represents actions that can trigger other actions based on
2728-- 'Event's.
@@ -61,3 +62,8 @@ instance PerformEvent t m => PerformEvent t (ReaderT r m) where
6162 performEvent e = do
6263 r <- ask
6364 lift $ performEvent $ flip runReaderT r <$> e
65+
66+instance PerformEvent t m => PerformEvent t (MaybeT m) where
67+ type Performable (MaybeT m) = MaybeT (Performable m)
68+ performEvent_ = lift . performEvent_ . fmapCheap (void . runMaybeT)
69+ performEvent = lift . fmap (fmapMaybe id) . performEvent . fmapCheap runMaybeT
src/Reflex/PostBuild/Base.hs+2
@@ -84,6 +84,8 @@ instance MonadHold t m => MonadHold t (PostBuildT t m) where
8484 buildDynamic a0 = lift . buildDynamic a0
8585 {-# INLINABLE headE #-}
8686 headE = lift . headE
87+ {-# INLINABLE now #-}
88+ now = lift now
8789
8890instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
8991 type Performable (PostBuildT t m) = Performable m
src/Reflex/Profiled.hs+56−2
@@ -26,8 +26,10 @@ import Control.Monad.Primitive
2626import Control.Monad.Reader
2727import Control.Monad.Ref
2828import Control.Monad.State.Strict (StateT, execStateT, modify)
29+import Data.Bifunctor
2930import Data.Coerce
30−import Data.Dependent.Map (DMap, GCompare)
31+import Data.Dependent.Map (DMap)
32+import Data.GADT.Compare (GCompare)
3133import Data.FastMutableIntMap
3234import Data.IORef
3335import Data.List
@@ -42,12 +44,20 @@ import Foreign.Ptr
4244import GHC.Foreign
4345import GHC.IO.Encoding
4446import GHC.Stack
47+import Reflex.Adjustable.Class
48+import Reflex.BehaviorWriter.Class
4549import Reflex.Class
50+import Reflex.DynamicWriter.Class
51+import Reflex.EventWriter.Class
4652import Reflex.Host.Class
53+import Reflex.NotReady.Class
4754import Reflex.PerformEvent.Class
55+import Reflex.PostBuild.Class
56+import Reflex.Query.Class
57+import Reflex.Requester.Class
58+import Reflex.TriggerEvent.Class
4859
4960import System.IO.Unsafe
50−import Unsafe.Coerce
5161
5262data ProfiledTimeline t
5363
@@ -174,10 +184,21 @@ instance MonadHold t m => MonadHold (ProfiledTimeline t) (ProfiledM m) where
174184 holdIncremental v0 (Event_Profiled v') = ProfiledM $ Incremental_Profiled <$> holdIncremental v0 v'
175185 buildDynamic (ProfiledM v0) (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> buildDynamic v0 v'
176186 headE (Event_Profiled e) = ProfiledM $ Event_Profiled <$> headE e
187+ now = ProfiledM $ Event_Profiled <$> now
177188
178189instance MonadSample t m => MonadSample (ProfiledTimeline t) (ProfiledM m) where
179190 sample (Behavior_Profiled b) = ProfiledM $ sample b
180191
192+instance Adjustable t m => Adjustable (ProfiledTimeline t) (ProfiledM m) where
193+ runWithReplace a0 a' = (fmap . fmap) coerce . lift $
194+ runWithReplace (coerce a0) (coerce $ coerce <$> a')
195+ traverseIntMapWithKeyWithAdjust f dm0 dm' = (fmap . fmap) coerce . lift $
196+ traverseIntMapWithKeyWithAdjust (\k v -> coerce $ f k v) dm0 (coerce dm')
197+ traverseDMapWithKeyWithAdjust f dm0 dm' = (fmap . fmap) coerce . lift $
198+ traverseDMapWithKeyWithAdjust (\k v -> coerce $ f k v) dm0 (coerce dm')
199+ traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = (fmap . fmap) coerce . lift $
200+ traverseDMapWithKeyWithAdjustWithMove (\k v -> coerce $ f k v) dm0 (coerce dm')
201+
181202instance MonadTrans ProfiledM where
182203 lift = ProfiledM
183204
@@ -189,6 +210,39 @@ instance PerformEvent t m => PerformEvent (ProfiledTimeline t) (ProfiledM m) whe
189210 performEvent_ = lift . performEvent_ . coerce
190211 performEvent = lift . fmap coerce . performEvent . coerce
191212
213+instance TriggerEvent t m => TriggerEvent (ProfiledTimeline t) (ProfiledM m) where
214+ newTriggerEvent = first coerce <$> lift newTriggerEvent
215+ newTriggerEventWithOnComplete = first coerce <$> lift newTriggerEventWithOnComplete
216+ newEventWithLazyTriggerWithOnComplete f = coerce <$> lift (newEventWithLazyTriggerWithOnComplete f)
217+
218+instance PostBuild t m => PostBuild (ProfiledTimeline t) (ProfiledM m) where
219+ getPostBuild = coerce <$> lift getPostBuild
220+
221+instance NotReady t m => NotReady (ProfiledTimeline t) (ProfiledM m) where
222+ notReady = lift notReady
223+ notReadyUntil = lift . notReadyUntil . coerce
224+
225+instance BehaviorWriter t w m => BehaviorWriter (ProfiledTimeline t) w (ProfiledM m) where
226+ tellBehavior = lift . tellBehavior . coerce
227+
228+instance DynamicWriter t w m => DynamicWriter (ProfiledTimeline t) w (ProfiledM m) where
229+ tellDyn = lift . tellDyn . coerce
230+
231+instance EventWriter t w m => EventWriter (ProfiledTimeline t) w (ProfiledM m) where
232+ tellEvent = lift . tellEvent . coerce
233+
234+instance MonadQuery t q m => MonadQuery (ProfiledTimeline t) q (ProfiledM m) where
235+ tellQueryIncremental = lift . tellQueryIncremental . coerce
236+ askQueryResult = coerce <$> lift askQueryResult
237+ queryIncremental = fmap coerce . lift . queryIncremental . coerce
238+
239+instance Requester t m => Requester (ProfiledTimeline t) (ProfiledM m) where
240+ type Request (ProfiledM m) = Request m
241+ type Response (ProfiledM m) = Response m
242+
243+ requesting = fmap coerce . lift . requesting . coerce
244+ requesting_ = lift . requesting_ . coerce
245+
192246instance MonadRef m => MonadRef (ProfiledM m) where
193247 type Ref (ProfiledM m) = Ref m
194248 newRef = lift . newRef
src/Reflex/Pure.hs+6−1
@@ -32,7 +32,8 @@ module Reflex.Pure
3232 ) where
3333
3434import Control.Monad
35−import Data.Dependent.Map (DMap, GCompare)
35+import Data.Dependent.Map (DMap)
36+import Data.GADT.Compare (GCompare)
3637import qualified Data.Dependent.Map as DMap
3738import Data.IntMap (IntMap)
3839import qualified Data.IntMap as IntMap
@@ -210,3 +211,7 @@ instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
210211 Just x -> fromMaybe lastValue $ apply x lastValue
211212
212213 headE = slowHeadE
214+ now t = Event $ guard . (t ==)
215+
216+
217+
src/Reflex/Query/Base.hs+9−3
@@ -22,16 +22,19 @@ module Reflex.Query.Base
2222import Control.Applicative (liftA2)
2323import Control.Monad.Exception
2424import Control.Monad.Fix
25+import Control.Monad.Morph
2526import Control.Monad.Primitive
2627import Control.Monad.Reader
2728import Control.Monad.Ref
2829import Control.Monad.State.Strict
2930import Data.Align
30−import Data.Dependent.Map (DMap, DSum (..))
31+import Data.Dependent.Map (DMap)
3132import qualified Data.Dependent.Map as DMap
33+import Data.Dependent.Sum (DSum(..))
3234import Data.Foldable
3335import Data.Functor.Compose
3436import Data.Functor.Misc
37+import Data.GADT.Compare (GCompare)
3538import Data.IntMap (IntMap)
3639import qualified Data.IntMap as IntMap
3740import Data.Map (Map)
@@ -135,7 +138,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
135138 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
136139 return (liftedResult0, liftedResult')
137140
138− traverseDMapWithKeyWithAdjust :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> QueryT t q m (DMap k v', Event t (PatchDMap k v'))
141+ traverseDMapWithKeyWithAdjust :: forall (k :: * -> *) v v'. (GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> QueryT t q m (DMap k v', Event t (PatchDMap k v'))
139142 traverseDMapWithKeyWithAdjust f dm0 dm' = do
140143 let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
141144 f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
@@ -180,7 +183,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
180183 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
181184 return (liftedResult0, liftedResult')
182185
183− traverseDMapWithKeyWithAdjustWithMove :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> QueryT t q m (DMap k v', Event t (PatchDMapWithMove k v'))
186+ traverseDMapWithKeyWithAdjustWithMove :: forall (k :: * -> *) v v'. (GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> QueryT t q m (DMap k v', Event t (PatchDMapWithMove k v'))
184187 traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
185188 let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
186189 f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
@@ -237,6 +240,9 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
237240instance MonadTrans (QueryT t q) where
238241 lift = QueryT . lift . lift . lift
239242
243+instance MFunctor (QueryT t q) where
244+ hoist = mapQueryT
245+
240246instance PrimMonad m => PrimMonad (QueryT t q m) where
241247 type PrimState (QueryT t q m) = PrimState m
242248 primitive = lift . primitive
src/Reflex/Query/Class.hs+5−5
@@ -125,7 +125,7 @@ instance Group SelectedCount where
125125
126126instance Additive SelectedCount
127127
128−-- | The Semigroup/Monoid/Group instances for a Query containing 'SelectedCount's should use
128+-- | The Semigroup\/Monoid\/Group instances for a Query containing 'SelectedCount's should use
129129-- this function which returns Nothing if the result is 0. This allows the pruning of leaves
130130-- of the 'Query' that are no longer wanted.
131131combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
@@ -133,12 +133,12 @@ combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j the
133133
134134-- | A class that allows sending of 'Query's and retrieval of 'QueryResult's. See 'queryDyn' for a commonly
135135-- used interface.
136−class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
136+class (Group q, Additive q, Query q, Monad m) => MonadQuery t q m | m -> q t where
137137 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
138138 askQueryResult :: m (Dynamic t (QueryResult q))
139139 queryIncremental :: Incremental t (AdditivePatch q) -> m (Dynamic t (QueryResult q))
140140
141−instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
141+instance MonadQuery t q m => MonadQuery t q (ReaderT r m) where
142142 tellQueryIncremental = lift . tellQueryIncremental
143143 askQueryResult = lift askQueryResult
144144 queryIncremental = lift . queryIncremental
@@ -148,11 +148,11 @@ tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
148148tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
149149
150150-- | Retrieve 'Dynamic'ally updating 'QueryResult's for a 'Dynamic'ally updating 'Query'.
151−queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
151+queryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
152152queryDyn q = do
153153 tellQueryDyn q
154154 zipDynWith crop q <$> askQueryResult
155155
156156-- | Use a query morphism to operate on a smaller version of a query.
157−subQuery :: (Reflex t, MonadQuery t q2 m, Monad m) => QueryMorphism q1 q2 -> Dynamic t q1 -> m (Dynamic t (QueryResult q1))
157+subQuery :: (Reflex t, MonadQuery t q2 m) => QueryMorphism q1 q2 -> Dynamic t q1 -> m (Dynamic t (QueryResult q1))
158158subQuery (QueryMorphism f g) x = fmap g <$> queryDyn (fmap f x)
src/Reflex/Requester/Base.hs+6−1
@@ -49,14 +49,16 @@ import Reflex.TriggerEvent.Class
4949import Control.Applicative (liftA2)
5050import Control.Monad.Exception
5151import Control.Monad.Identity
52+import Control.Monad.Morph
5253import Control.Monad.Primitive
5354import Control.Monad.Reader
5455import Control.Monad.Ref
5556import Control.Monad.State.Strict
5657import Data.Bits
5758import Data.Coerce
58−import Data.Dependent.Map (DMap, DSum (..))
59+import Data.Dependent.Map (DMap)
5960import qualified Data.Dependent.Map as DMap
61+import Data.Dependent.Sum (DSum (..))
6062import Data.Functor.Compose
6163import Data.Functor.Misc
6264import Data.IntMap.Strict (IntMap)
@@ -316,6 +318,9 @@ responseFromTag (MyTagWrap t) = do
316318instance MonadTrans (RequesterT t request response) where
317319 lift = RequesterT . lift . lift
318320
321+instance MFunctor (RequesterT t request response) where
322+ hoist f = RequesterT . hoist (hoist f) . unRequesterT
323+
319324instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
320325 type Performable (RequesterT t request response m) = Performable m
321326 performEvent_ = lift . performEvent_
src/Reflex/Spider/Internal.hs+38−12
@@ -46,8 +46,9 @@ import Control.Monad.Fail (MonadFail)
4646import qualified Control.Monad.Fail as MonadFail
4747import Data.Align
4848import Data.Coerce
49−import Data.Dependent.Map (DMap, DSum (..))
49+import Data.Dependent.Map (DMap)
5050import qualified Data.Dependent.Map as DMap
51+import Data.Dependent.Sum (DSum (..))
5152import Data.FastMutableIntMap (FastMutableIntMap, PatchIntMap (..))
5253import qualified Data.FastMutableIntMap as FastMutableIntMap
5354import Data.Foldable hiding (concat, elem, sequence_)
@@ -306,7 +307,20 @@ data CacheSubscribed x a
306307#endif
307308 }
308309
309−
310+nowSpiderEventM :: (HasSpiderTimeline x) => EventM x (R.Event (SpiderTimeline x) ())
311+nowSpiderEventM =
312+ SpiderEvent <$> now
313+
314+now :: (MonadIO m, Defer (Some Clear) m, HasSpiderTimeline x
315+ ) => m (Event x ())
316+now = do
317+ nowOrNot <- liftIO $ newIORef $ Just ()
318+ scheduleClear nowOrNot
319+ return . Event $ \_ -> do
320+ occ <- liftIO . readIORef $ nowOrNot
321+ return ( EventSubscription (return ()) eventSubscribedNow
322+ , occ
323+ )
310324
311325-- | Construct an 'Event' whose value is guaranteed not to be recomputed
312326-- repeatedly
@@ -450,16 +464,6 @@ data Subscriber x a = Subscriber
450464 , subscriberRecalculateHeight :: !(Height -> IO ())
451465 }
452466
453−--TODO: Move this comment to WeakBag
454−-- These function are constructor functions that are marked NOINLINE so they are
455−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
456−-- so any weak references that are attached to the constructors will have their
457−-- finalizer run. Using the opaque constructor, does not see the
458−-- constructor application, so it behaves like an IORef and cannot be fused away.
459−--
460−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
461−-- the weak pointer to it in some cases.
462−
463467newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
464468newSubscriberHold h = return $ Subscriber
465469 { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
@@ -603,6 +607,16 @@ eventSubscribedNever = EventSubscribed
603607 , eventSubscribedWhoCreated = return ["never"]
604608#endif
605609 }
610+eventSubscribedNow :: EventSubscribed x
611+eventSubscribedNow = EventSubscribed
612+ { eventSubscribedHeightRef = zeroRef
613+ , eventSubscribedRetained = toAny ()
614+#ifdef DEBUG_CYCLES
615+ , eventSubscribedGetParents = return []
616+ , eventSubscribedHasOwnHeightRef = False
617+ , eventSubscribedWhoCreated = return ["now"]
618+#endif
619+ }
606620
607621eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
608622eventSubscribedFan !subscribed = EventSubscribed
@@ -2506,6 +2520,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Event
25062520 {-# INLINABLE headE #-}
25072521 headE = R.slowHeadE
25082522-- headE (SpiderEvent e) = SpiderEvent <$> Reflex.Spider.Internal.headE e
2523+ {-# INLINABLE now #-}
2524+ now = nowSpiderEventM
25092525
25102526instance Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
25112527 {-# INLINABLE sample #-}
@@ -2527,6 +2543,9 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
25272543 {-# INLINABLE headE #-}
25282544 headE = R.slowHeadE
25292545-- headE (SpiderEvent e) = SpiderPushM $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2546+ {-# INLINABLE now #-}
2547+ now = SpiderPushM nowSpiderEventM
2548+
25302549
25312550instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
25322551 {-# INLINE return #-}
@@ -2590,6 +2609,9 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
25902609 buildDynamic getV0 e = runFrame . runSpiderHostFrame $ Reflex.Class.buildDynamic getV0 e
25912610 {-# INLINABLE headE #-}
25922611 headE e = runFrame . runSpiderHostFrame $ Reflex.Class.headE e
2612+ {-# INLINABLE now #-}
2613+ now = runFrame . runSpiderHostFrame $ Reflex.Class.now
2614+
25932615
25942616instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
25952617 sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
@@ -2606,6 +2628,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
26062628 {-# INLINABLE headE #-}
26072629 headE = R.slowHeadE
26082630-- headE (SpiderEvent e) = SpiderHostFrame $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2631+ {-# INLINABLE now #-}
2632+ now = SpiderHostFrame Reflex.Class.now
26092633
26102634instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
26112635 {-# INLINABLE sample #-}
@@ -2626,6 +2650,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Refle
26262650 buildDynamic getV0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.buildDynamic getV0 e
26272651 {-# INLINABLE headE #-}
26282652 headE e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.headE e
2653+ {-# INLINABLE now #-}
2654+ now = Reflex.Spider.Internal.ReadPhase Reflex.Class.now
26292655
26302656--------------------------------------------------------------------------------
26312657-- Deprecated items
src/Reflex/TriggerEvent/Base.hs+2
@@ -122,6 +122,8 @@ instance MonadHold t m => MonadHold t (TriggerEventT t m) where
122122 buildDynamic a0 = lift . buildDynamic a0
123123 {-# INLINABLE headE #-}
124124 headE = lift . headE
125+ {-# INLINABLE now #-}
126+ now = lift now
125127
126128instance Adjustable t m => Adjustable t (TriggerEventT t m) where
127129 {-# INLINABLE runWithReplace #-}
src/Reflex/TriggerEvent/Class.hs+6
@@ -12,6 +12,7 @@ import Reflex.Class
1212import Control.Monad.Reader
1313import Control.Monad.State
1414import qualified Control.Monad.State.Strict as Strict
15+import Control.Monad.Trans.Maybe (MaybeT)
1516
1617--TODO: Shouldn't have IO hard-coded
1718-- | 'TriggerEvent' represents actions that can create 'Event's that can be
@@ -47,3 +48,8 @@ instance TriggerEvent t m => TriggerEvent t (Strict.StateT s m) where
4748 newTriggerEvent = lift newTriggerEvent
4849 newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
4950 newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
51+
52+instance TriggerEvent t m => TriggerEvent t (MaybeT m) where
53+ newTriggerEvent = lift newTriggerEvent
54+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
55+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
test/Reflex/Plan/Pure.hs+1
@@ -41,6 +41,7 @@ instance MonadHold (Pure Int) PurePlan where
4141 holdIncremental initial = liftPlan . holdIncremental initial
4242 buildDynamic getInitial = liftPlan . buildDynamic getInitial
4343 headE = liftPlan . headE
44+ now = liftPlan now
4445
4546instance MonadSample (Pure Int) PurePlan where
4647 sample = liftPlan . sample
test/Reflex/Test/CrossImpl.hs+1−1
@@ -26,7 +26,7 @@ import qualified Reflex.Profiled as Prof
2626import Control.Arrow (second, (&&&))
2727import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
2828import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29−import Data.Dependent.Map (DSum (..))
29+import Data.Dependent.Sum (DSum (..))
3030import Data.Foldable
3131import Data.Map.Strict (Map)
3232import qualified Data.Map.Strict as Map
test/Reflex/Test/Micro.hs+8−2
@@ -322,8 +322,14 @@ testCases =
322322 let e = never <$ switch b
323323 return $ void e
324324 lazyHold
325−
326−
325+ , testE "now-1" $ do
326+ e1 <- events1
327+ switchHoldPromptly never . pushAlways (\a -> fmap (a <$) now) $ e1
328+ , testE "now-2" $ do
329+ e1 <- events1
330+ let e = pushAlways (\a -> if a == "a" then now else return never) e1
331+ x <- accumDyn (<>) never e
332+ return . coincidence $ updated x
327333 ] where
328334
329335 events1, events2, events3 :: TestPlan t m => m (Event t String)