From f4c2b4af794c1ce0e3d91ca9cf9c1e00c30f0ac5 Mon Sep 17 00:00:00 2001 From: Sam Erie Date: Thu, 20 Feb 2020 09:38:05 -0900 Subject: [PATCH 01/69] Quickref align functions Only `alignEventWithMaybe` is exposed by [Reflex.Class module](https://github.com/reflex-frp/reflex/blob/develop/src/Reflex/Class.hs#L49). If `align` and `alignWith` were meant to be exposed that would be better fix, but I was looking to use them based on Quickref, and (unless I am missing something) this document is not reflecting what Reflex actually exposes for align functions. --- Quickref.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Quickref.md b/Quickref.md index fd867c38..a9e7acd5 100644 --- a/Quickref.md +++ b/Quickref.md @@ -50,15 +50,14 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex [ ] <@ :: Behavior a -> Event b -> Event a -- Combine multiple Events -[ ] <> :: Semigroup a => Event a -> Event a -> Event a -[ ] difference :: Event a -> Event b -> Event a -[ ] align :: Event a -> Event b -> Event (These a b) -[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c -[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a -[ ] leftmost :: [Event a] -> Event a -[ ] mergeList :: [Event a] -> Event (NonEmpty a) -[ ] merge :: GCompare k => DMap k Event -> Event (DMap k Identity) -[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a) +[ ] <> :: Semigroup a => Event a -> Event a -> Event a +[ ] difference :: Event a -> Event b -> Event a +[ ] alignEventWithMaybe :: (These a b -> Maybe c) -> Event a -> Event b -> Event c +[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a +[ ] leftmost :: [Event a] -> Event a +[ ] mergeList :: [Event a] -> Event (NonEmpty a) +[ ] merge :: GCompare k => DMap k Event -> Event (DMap k Identity) +[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a) -- Efficient one-to-many fanout [ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a) From 7b5df7b19c575339b654ea8ab48c545e2d64a6a0 Mon Sep 17 00:00:00 2001 From: Malte Brandy Date: Fri, 19 Jun 2020 14:15:16 +0200 Subject: [PATCH 02/69] Fix hlint 3 test and a GC test * hlint released a new version * hlint 3 is not compatible with ghc 8.4 because newst ghc-lib fails to compile. * the Refl constructor does not seem to get exported in newer versions of some dependency --- reflex.cabal | 5 ++++- src/Reflex/PerformEvent/Class.hs | 1 - src/Reflex/PostBuild/Base.hs | 1 - src/Reflex/PostBuild/Class.hs | 1 - src/Reflex/Spider/Internal.hs | 2 +- test/GC.hs | 1 + test/hlint.hs | 3 ++- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/reflex.cabal b/reflex.cabal index 56d37b37..ff1610f7 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -256,7 +256,10 @@ test-suite hlint , directory , filepath , filemanip - , hlint (< 2.1 || >= 2.2.2) && < 3 + if impl(ghc >= 8.8) + build-depends: hlint >= 3 + else + build-depends: hlint (< 2.1 || >= 2.2.2) && < 3 if impl(ghcjs) buildable: False diff --git a/src/Reflex/PerformEvent/Class.hs b/src/Reflex/PerformEvent/Class.hs index 51e2bc30..4a6abd09 100644 --- a/src/Reflex/PerformEvent/Class.hs +++ b/src/Reflex/PerformEvent/Class.hs @@ -8,7 +8,6 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} diff --git a/src/Reflex/PostBuild/Base.hs b/src/Reflex/PostBuild/Base.hs index 9ad7472d..5dba681b 100644 --- a/src/Reflex/PostBuild/Base.hs +++ b/src/Reflex/PostBuild/Base.hs @@ -9,7 +9,6 @@ {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} diff --git a/src/Reflex/PostBuild/Class.hs b/src/Reflex/PostBuild/Class.hs index 8b65a9d0..dac1516b 100644 --- a/src/Reflex/PostBuild/Class.hs +++ b/src/Reflex/PostBuild/Class.hs @@ -6,7 +6,6 @@ {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs index 14c522e1..e3282e20 100644 --- a/src/Reflex/Spider/Internal.hs +++ b/src/Reflex/Spider/Internal.hs @@ -9,7 +9,6 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -20,6 +19,7 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE MultiWayIf #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} diff --git a/test/GC.hs b/test/GC.hs index ff859b7c..1cae3026 100644 --- a/test/GC.hs +++ b/test/GC.hs @@ -18,6 +18,7 @@ import Data.GADT.Compare import Data.IORef import Data.Semigroup import Data.These +import Data.Type.Equality ((:~:)(Refl)) import Data.Functor.Misc import Data.Patch diff --git a/test/hlint.hs b/test/hlint.hs index 869e412c..1d0e319d 100644 --- a/test/hlint.hs +++ b/test/hlint.hs @@ -1,7 +1,7 @@ module Main where import Control.Monad -import Language.Haskell.HLint3 (hlint) +import Language.Haskell.HLint (hlint) import System.Directory import System.Exit (exitFailure, exitSuccess) import System.FilePath @@ -22,6 +22,7 @@ main = do , "--ignore=Use unless" , "--ignore=Reduce duplication" , "--cpp-define=USE_TEMPLATE_HASKELL" + , "--cpp-define=DEBUG" , "--ignore=Use tuple-section" ] recurseInto = and <$> sequence From 66cf0ccc71a8882f4edd2bdd6659e87295d2e6ce Mon Sep 17 00:00:00 2001 From: Sam Erie Date: Thu, 27 Aug 2020 09:57:31 -0800 Subject: [PATCH 03/69] Noted origin of align functions in Quickref --- Quickref.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Quickref.md b/Quickref.md index a9e7acd5..33e4aa4b 100644 --- a/Quickref.md +++ b/Quickref.md @@ -50,14 +50,16 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex [ ] <@ :: Behavior a -> Event b -> Event a -- Combine multiple Events -[ ] <> :: Semigroup a => Event a -> Event a -> Event a -[ ] difference :: Event a -> Event b -> Event a -[ ] alignEventWithMaybe :: (These a b -> Maybe c) -> Event a -> Event b -> Event c -[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a -[ ] leftmost :: [Event a] -> Event a -[ ] mergeList :: [Event a] -> Event (NonEmpty a) -[ ] merge :: GCompare k => DMap k Event -> Event (DMap k Identity) -[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a) +[ ] <> :: Semigroup a => Event a -> Event a -> Event a +[ ] difference :: Event a -> Event b -> Event a +[ ] align :: Event a -> Event b -> Event (These a b) +[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c + -- Note align functions from Data.Align in semialign package +[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a +[ ] leftmost :: [Event a] -> Event a +[ ] mergeList :: [Event a] -> Event (NonEmpty a) +[ ] merge :: GCompare k => DMap k Event -> Event (DMap k Identity) +[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a) -- Efficient one-to-many fanout [ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a) From d230632427fc1b7031163567c97f20050610c122 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie Date: Sun, 18 Oct 2020 21:11:20 +1300 Subject: [PATCH 04/69] Fixes for ghc 8.10 --- src/Reflex/Profiled.hs | 2 +- src/Reflex/Spider/Internal.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Reflex/Profiled.hs b/src/Reflex/Profiled.hs index 86821507..68181a9e 100644 --- a/src/Reflex/Profiled.hs +++ b/src/Reflex/Profiled.hs @@ -147,7 +147,7 @@ instance Reflex t => Reflex (ProfiledTimeline t) where pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e pull = Behavior_Profiled . pull . coerce fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e) - mergeG :: forall (k :: z -> *) q v. GCompare k + mergeG :: forall z (k :: z -> *) q v. GCompare k => (forall a. q a -> Event (ProfiledTimeline t) (v a)) -> DMap k q -> Event (ProfiledTimeline t) (DMap k v) mergeG nt = Event_Profiled #. mergeG (coerce nt) diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs index 68527c86..26fd5f18 100644 --- a/src/Reflex/Spider/Internal.hs +++ b/src/Reflex/Spider/Internal.hs @@ -2775,7 +2775,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e)) {-# INLINABLE mergeG #-} mergeG - :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k + :: forall k2 (k :: k2 -> *) q (v :: k2 -> *). GCompare k => (forall a. q a -> R.Event (SpiderTimeline x) (v a)) -> DMap k q -> R.Event (SpiderTimeline x) (DMap k v) From b3f55d74eef1d3cf119e8eb2e7fa632b2649b343 Mon Sep 17 00:00:00 2001 From: Tuan Le Date: Tue, 20 Oct 2020 08:33:01 +0200 Subject: [PATCH 05/69] fix: check on headless exit on post build Don't enter the headless main loop if there's a shutdown request upon post build. --- reflex.cabal | 8 ++++++++ src/Reflex/Host/Headless.hs | 20 +++++++++++--------- test/Headless.hs | 10 ++++++++++ 3 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 test/Headless.hs diff --git a/reflex.cabal b/reflex.cabal index 9688d749..79b54d60 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -342,6 +342,14 @@ test-suite RequesterT Reflex.Plan.Pure Test.Run +test-suite Headless + default-language: Haskell2010 + type: exitcode-stdio-1.0 + main-is: Headless.hs + hs-source-dirs: test + build-depends: base + , reflex + test-suite Adjustable default-language: Haskell2010 type: exitcode-stdio-1.0 diff --git a/src/Reflex/Host/Headless.hs b/src/Reflex/Host/Headless.hs index e18663e5..0b4b9256 100644 --- a/src/Reflex/Host/Headless.hs +++ b/src/Reflex/Host/Headless.hs @@ -7,6 +7,7 @@ module Reflex.Host.Headless where import Control.Concurrent.Chan (newChan, readChan) +import Control.Monad (unless) import Control.Monad.Fix (MonadFix, fix) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Primitive (PrimMonad) @@ -15,7 +16,7 @@ import Data.Dependent.Sum (DSum (..), (==>)) import Data.Foldable (for_) import Data.Functor.Identity (Identity(..)) import Data.IORef (IORef, readIORef) -import Data.Maybe (catMaybes) +import Data.Maybe (catMaybes, fromMaybe) import Data.Traversable (for) import Reflex @@ -82,32 +83,33 @@ runHeadlessApp guest = -- 'Nothing' if the guest application hasn't subscribed to this event. mPostBuildTrigger <- readRef postBuildTriggerRef - -- When there is a subscriber to the post-build event, fire the event. - for_ mPostBuildTrigger $ \postBuildTrigger -> - fire [postBuildTrigger :=> Identity ()] $ pure () - -- Subscribe to an 'Event' of that the guest application can use to -- request application shutdown. We'll check whether this 'Event' is firing -- to determine whether to terminate. shutdown <- subscribeEvent result + -- When there is a subscriber to the post-build event, fire the event. + soa <- for mPostBuildTrigger $ \postBuildTrigger -> + fire [postBuildTrigger :=> Identity ()] $ isFiring shutdown + -- The main application loop. We wait for new events and fire those that -- have subscribers. If we detect a shutdown request, the application -- terminates. - fix $ \loop -> do + unless (or (fromMaybe [] soa)) $ fix $ \loop -> do -- Read the next event (blocking). ers <- liftIO $ readChan events stop <- do -- Fire events that have subscribers. fireEventTriggerRefs fc ers $ -- Check if the shutdown 'Event' is firing. - readEvent shutdown >>= \case - Nothing -> pure False - Just _ -> pure True + isFiring shutdown if or stop then pure () else loop where + isFiring ev = readEvent ev >>= \case + Nothing -> pure False + Just _ -> pure True -- Use the given 'FireCommand' to fire events that have subscribers -- and call the callback for the 'TriggerInvocation' of each. fireEventTriggerRefs diff --git a/test/Headless.hs b/test/Headless.hs new file mode 100644 index 00000000..bfd32790 --- /dev/null +++ b/test/Headless.hs @@ -0,0 +1,10 @@ +module Main where + +import Reflex +import Reflex.Host.Headless (runHeadlessApp) + +main :: IO () +main = do + runHeadlessApp $ do + pb <- getPostBuild + performEvent (pure <$> pb) From c7ea1d5828aa9e478e2cbb62fa24406fd6431c18 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 5 Nov 2020 21:09:21 -0500 Subject: [PATCH 06/69] Update changelog --- ChangeLog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 2063eddf..f0da4cb0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,8 @@ # Revision history for reflex -## Unreleased +## 0.7.2.0 * ([#416](https://github.com/reflex-frp/reflex/pull/416)) Add `now :: m (Event t ())` to `MonadHold`. +* Extend some dependency version bounds ## 0.7.1.0 From 977f7a0e6bc8dc5e2020c231e11a6ee21ba7a300 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 5 Nov 2020 21:19:59 -0500 Subject: [PATCH 07/69] Fix GC test --- test/GC.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/GC.hs b/test/GC.hs index ff859b7c..66eb63d2 100644 --- a/test/GC.hs +++ b/test/GC.hs @@ -18,6 +18,7 @@ import Data.GADT.Compare import Data.IORef import Data.Semigroup import Data.These +import Data.Type.Equality import Data.Functor.Misc import Data.Patch From f507c19eca59bde68eacfc6ad572014a334a2a8f Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 5 Nov 2020 21:20:21 -0500 Subject: [PATCH 08/69] Bump version number --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index 79b54d60..de775823 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.7.1.0 +Version: 0.7.2.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 57dc0e76e5e0f03d2143e699141b3ed5a81c1020 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 5 Nov 2020 23:03:42 -0500 Subject: [PATCH 09/69] Update changelog --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index f0da4cb0..f9dc7d2f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,7 @@ ## 0.7.2.0 * ([#416](https://github.com/reflex-frp/reflex/pull/416)) Add `now :: m (Event t ())` to `MonadHold`. * Extend some dependency version bounds +* Fix HLint 3 test ## 0.7.1.0 From 3369fceadb132a980ab1ccbed7471ba36a9ef642 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 6 Nov 2020 10:37:53 -0500 Subject: [PATCH 10/69] v0.8 --- ChangeLog.md | 4 ++++ reflex.cabal | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index f9dc7d2f..de3089cc 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## 0.8.0.0 + +* Replace 0.7.2.0 with 0.8.0.0 to reflect the `MonadHold` interface change. Deprecates 0.7.2.0. + ## 0.7.2.0 * ([#416](https://github.com/reflex-frp/reflex/pull/416)) Add `now :: m (Event t ())` to `MonadHold`. * Extend some dependency version bounds diff --git a/reflex.cabal b/reflex.cabal index 41d64727..f1d71126 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.7.2.0 +Version: 0.8.0.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 39d4f2de57440eacebb1c188cbd88ea897089dbd Mon Sep 17 00:00:00 2001 From: Malte Brandy Date: Tue, 17 Nov 2020 01:36:49 +0100 Subject: [PATCH 11/69] Bump template-haskell bound --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index f1d71126..6dbc777e 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -185,7 +185,7 @@ library dependent-sum >= 0.6 && < 0.8, haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.16 + template-haskell >= 2.9 && < 2.17 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell From ba3ddcc7f452169f1fb6dec61afe0962886fad50 Mon Sep 17 00:00:00 2001 From: Malte Brandy Date: Wed, 18 Nov 2020 20:24:37 +0100 Subject: [PATCH 12/69] Bump base bound for ghc 8.10 --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index 6dbc777e..8b947fcf 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -71,7 +71,7 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.9 && < 4.14, + base >= 4.9 && < 4.15, bifunctors >= 5.2 && < 5.6, comonad >= 5.0.4 && < 5.1, constraints-extras >= 0.3 && < 0.4, From 04e43cda2e113eb9df5df5d13613a7b6744fd14f Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 12 Mar 2021 21:18:41 -0500 Subject: [PATCH 13/69] Fix demuxed doc --- src/Reflex/Dynamic.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Reflex/Dynamic.hs b/src/Reflex/Dynamic.hs index 3d9d82bc..cbe9825d 100644 --- a/src/Reflex/Dynamic.hs +++ b/src/Reflex/Dynamic.hs @@ -348,7 +348,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where -- -- > demuxed (demux d) k === fmap (== k) d -- --- However, when getDemuxed is used multiple times, the complexity is only +-- However, when 'demuxed' is used multiple times, the complexity is only -- /O(log(n))/, rather than /O(n)/ for fmap. data Demux t k = Demux { demuxValue :: Behavior t k , demuxSelector :: EventSelector t (Const2 k Bool) From 97a7c802407e9dbe595482cbc570c4dcd7e918cf Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 15 Apr 2021 20:13:03 -0400 Subject: [PATCH 14/69] Create haskell.yml --- .github/workflows/haskell.yml | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/haskell.yml diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml new file mode 100644 index 00000000..fba94b1f --- /dev/null +++ b/.github/workflows/haskell.yml @@ -0,0 +1,45 @@ +name: github-action + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + ghc: ['8.0.2', '8.2.2', '8.4.4', '8.6.5', '8.8.4', '8.10.2'] + os: ['ubuntu-latest', 'macos-latest'] + exclude: + # There are some linker warnings in 802 on darwin that + # cause compilation to fail + # See https://github.com/NixOS/nixpkgs/issues/25139 + - ghc: '8.0.2' + os: 'macos-latest' + runs-on: ${{ matrix.os }} + + name: GHC ${{ matrix.ghc }} on ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-haskell@v1 + with: + ghc-version: ${{ matrix.ghc }} + - name: Cache + uses: actions/cache@v1 + env: + cache-name: cache-cabal + with: + path: ~/.cabal + key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}- + ${{ runner.os }}-${{ matrix.ghc }}-build- + ${{ runner.os }}-${{ matrix.ghc }}- + ${{ runner.os }} + + - name: Install dependencies + run: | + cabal update + cabal build --only-dependencies --enable-tests --enable-benchmarks + - name: Build + run: cabal build --enable-tests --enable-benchmarks all + - name: Run tests + run: cabal test all From 2dacc213dfc503ade0422a48cc7b7660fdef4280 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 15 Apr 2021 21:45:52 -0400 Subject: [PATCH 15/69] Remove cabal.project.freeze --- cabal.project.freeze | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 cabal.project.freeze diff --git a/cabal.project.freeze b/cabal.project.freeze deleted file mode 100644 index db06f8ef..00000000 --- a/cabal.project.freeze +++ /dev/null @@ -1,2 +0,0 @@ -constraints: any.text < 1.2.4.0 - , hlint < 2.2.6 || > 2.2.6 From 71caccf3be1eb8dcca89e20103cf7ad1f86f6666 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 15 Apr 2021 21:53:45 -0400 Subject: [PATCH 16/69] Add ghc 8.10.2 to tested list --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index 8b947fcf..22b2941b 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -28,7 +28,7 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1, + GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2, GHCJS ==8.4 flag use-reflex-optimizer From 57aa08f6cf669ce6033b1b7a1661f64e92bce20e Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 15 Apr 2021 22:12:34 -0400 Subject: [PATCH 17/69] Drop support for ghc 8.2.* and earlier --- .github/workflows/haskell.yml | 2 +- .travis.yml | 4 ---- ChangeLog.md | 5 +++++ reflex.cabal | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index fba94b1f..03c53cfd 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -6,7 +6,7 @@ jobs: build: strategy: matrix: - ghc: ['8.0.2', '8.2.2', '8.4.4', '8.6.5', '8.8.4', '8.10.2'] + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2'] os: ['ubuntu-latest', 'macos-latest'] exclude: # There are some linker warnings in 802 on darwin that diff --git a/.travis.yml b/.travis.yml index eaeff0db..34ba6102 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,10 +35,6 @@ matrix: addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}} - compiler: ghc-8.4.4 addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}} - - compiler: ghc-8.2.2 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}} - - compiler: ghc-8.0.2 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}} before_install: - | if [ "$TRAVIS_OS_NAME" = "linux" ]; then diff --git a/ChangeLog.md b/ChangeLog.md index de3089cc..cd2df663 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,10 @@ # Revision history for reflex +## 0.8.1.0 + +* Add support for GHC 8.10 +* Drop support for GHC 8.2.* and earlier + ## 0.8.0.0 * Replace 0.7.2.0 with 0.8.0.0 to reflect the `MonadHold` interface change. Deprecates 0.7.2.0. diff --git a/reflex.cabal b/reflex.cabal index 22b2941b..46bcde8c 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.8.0.0 +Version: 0.8.1.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. @@ -28,7 +28,7 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2, + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2, GHCJS ==8.4 flag use-reflex-optimizer From 4c2d1ed3c5b8d5bc76fb4fdc1fd1d4308f47eb09 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 15 Apr 2021 22:21:39 -0400 Subject: [PATCH 18/69] Allow newer profunctors --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index 46bcde8c..d347a7d3 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -86,7 +86,7 @@ library patch >= 0.0.1 && < 0.1, prim-uniq >= 0.1.0.1 && < 0.3, primitive >= 0.5 && < 0.8, - profunctors >= 5.3 && < 5.6, + profunctors >= 5.3 && < 5.7, random == 1.1.*, ref-tf == 0.4.*, reflection == 2.1.*, From 4f69ecabff6d7125e0fd58600f9de0a1acffa521 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 16 Apr 2021 05:28:48 -0400 Subject: [PATCH 19/69] ci: Remove travis --- .travis.yml | 173 ---------------------------------------------------- README.md | 2 +- 2 files changed, 1 insertion(+), 174 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 34ba6102..00000000 --- a/.travis.yml +++ /dev/null @@ -1,173 +0,0 @@ -# This Travis job script has been generated by a script via -# -# haskell-ci '--config=cabal.haskell-ci' 'cabal.project' -# -# For more information, see https://github.com/haskell-CI/haskell-ci -# -# version: 0.3.20190814 -# -language: c -dist: xenial -sudo: required -git: - # whether to recursively clone submodules - submodules: false -cache: - directories: - - $HOME/.cabal/packages - - $HOME/.cabal/store -before_cache: - - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log - # remove files that are regenerated by 'cabal update' - - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.* - - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx - - rm -rfv $CABALHOME/packages/head.hackage -matrix: - include: - - compiler: ghcjs-8.4 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}} - - compiler: ghc-8.8.1 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}} - - compiler: ghc-8.6.5 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}} - - compiler: ghc-8.4.4 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}} -before_install: - - | - if [ "$TRAVIS_OS_NAME" = "linux" ]; then - sudo add-apt-repository -y ppa:hvr/ghcjs; - curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add - - sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main' - sudo apt-get update; - sudo apt-get install $CC cabal-install-3.0 nodejs; - fi - - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//') - - WITHCOMPILER="-w $HC" - - | - if echo $CC | grep -q ghcjs; then - GHCJS=true - HC=${HC}js - WITHCOMPILER="--ghcjs ${WITHCOMPILER}js" - else - GHCJS=false; - fi - - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi - - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi - - HCPKG="$HC-pkg" - - unset CC - - CABAL=/opt/ghc/bin/cabal - - CABALHOME=$HOME/.cabal - - export PATH="$CABALHOME/bin:$PATH" - - TOP=$(pwd) - - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')" - - echo $HCNUMVER - - CABAL="$CABAL -vnormal+nowrap+markoutput" - - set -o pipefail - - | - echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk - echo 'BEGIN { state = "output"; }' >> .colorful.awk - echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk - echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk - echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk - echo ' if (state == "cabal") {' >> .colorful.awk - echo ' print blue($0)' >> .colorful.awk - echo ' } else {' >> .colorful.awk - echo ' print $0' >> .colorful.awk - echo ' }' >> .colorful.awk - echo '}' >> .colorful.awk - - cat .colorful.awk - - | - color_cabal_output () { - awk -f $TOP/.colorful.awk - } - - echo text | color_cabal_output -install: - - ${CABAL} --version - - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" - - node --version - - echo $GHCJS - - TEST=--enable-tests - - BENCH=--enable-benchmarks - - BENCH=--disable-benchmarks - - HEADHACKAGE=false - - rm -f $CABALHOME/config - - | - echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config - echo "remote-build-reporting: anonymous" >> $CABALHOME/config - echo "write-ghc-environment-files: always" >> $CABALHOME/config - echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config - echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config - echo "world-file: $CABALHOME/world" >> $CABALHOME/config - echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config - echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config - echo "installdir: $CABALHOME/bin" >> $CABALHOME/config - echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config - echo "store-dir: $CABALHOME/store" >> $CABALHOME/config - echo "install-dirs user" >> $CABALHOME/config - echo " prefix: $CABALHOME" >> $CABALHOME/config - echo "repository hackage.haskell.org" >> $CABALHOME/config - echo " url: http://hackage.haskell.org/" >> $CABALHOME/config - - cat $CABALHOME/config - - rm -fv cabal.project cabal.project.local cabal.project.freeze - - travis_retry ${CABAL} v2-update -v - - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi - # Generate cabal.project - - rm -rf cabal.project cabal.project.local cabal.project.freeze - - touch cabal.project - - | - echo "packages: ." >> cabal.project - - | - - cat cabal.project || true - - cat cabal.project.local || true - - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi - - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output - - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'" - - rm cabal.project.freeze -script: - - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) - # Packaging... - - ${CABAL} v2-sdist all | color_cabal_output - # Unpacking... - - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ - - cd ${DISTDIR} || false - - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \; - - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \; - - PKGDIR_reflex="$(find . -maxdepth 1 -type d -regex '.*/reflex-[0-9.]*')" - # Generate cabal.project - - rm -rf cabal.project cabal.project.local cabal.project.freeze - - touch cabal.project - - | - echo "packages: ${PKGDIR_reflex}" >> cabal.project - - | - - cat cabal.project || true - - cat cabal.project.local || true - # Building... - # this builds all libraries and executables (without tests/benchmarks) - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output - # Building with tests and benchmarks... - # build & run tests, build benchmarks - - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output - # Testing... - - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi - # cabal check... - - (cd ${PKGDIR_reflex} && ${CABAL} -vnormal check) - # haddock... - - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi - # Constraint sets - - rm -rf cabal.project.local - # Constraint set no-th - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex -use-template-haskell' all | color_cabal_output - # Constraint set old-these - - if $GHCJS || ! $GHCJS && [ $HCNUMVER -lt 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output ; fi - # Constraint set old-witherable - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output - # Constraint set debug-propagation - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-propagation' all | color_cabal_output - # Constraint set debug-cycles - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-cycles' all | color_cabal_output - -# REGENDATA ["--config=cabal.haskell-ci","cabal.project"] -# EOF diff --git a/README.md b/README.md index 31177f78..cf07c47b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## [Reflex](https://reflex-frp.org/) -[![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) +[![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) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE) 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. From 804a0802b9d0b6e9527de373cb214f34672b6803 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 14 Jun 2021 18:20:00 -0400 Subject: [PATCH 20/69] Backport GHC 10 support to reflex 0.7 Drop support for < 8.4, bumping base lower bound accordingly. (I tested that in fact does not work.) --- .github/workflows/haskell.yml | 41 +++++++ .travis.yml | 177 ------------------------------- ChangeLog.md | 6 ++ README.md | 2 +- cabal.project.freeze | 2 - reflex.cabal | 30 +++--- src/Reflex/Adjustable/Class.hs | 3 +- src/Reflex/Class.hs | 3 +- src/Reflex/EventWriter/Base.hs | 3 +- src/Reflex/PerformEvent/Class.hs | 1 - src/Reflex/PostBuild/Base.hs | 1 - src/Reflex/PostBuild/Class.hs | 1 - src/Reflex/Profiled.hs | 5 +- src/Reflex/Pure.hs | 3 +- src/Reflex/Query/Base.hs | 8 +- src/Reflex/Requester/Base.hs | 3 +- src/Reflex/Spider/Internal.hs | 7 +- test/GC.hs | 1 + test/Reflex/Test/CrossImpl.hs | 2 +- test/hlint.hs | 3 +- 20 files changed, 91 insertions(+), 211 deletions(-) create mode 100644 .github/workflows/haskell.yml delete mode 100644 .travis.yml delete mode 100644 cabal.project.freeze diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml new file mode 100644 index 00000000..8b751c90 --- /dev/null +++ b/.github/workflows/haskell.yml @@ -0,0 +1,41 @@ +name: github-action + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2'] + os: ['ubuntu-latest', 'macos-latest'] + runs-on: ${{ matrix.os }} + + name: GHC ${{ matrix.ghc }} on ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-haskell@v1 + with: + ghc-version: ${{ matrix.ghc }} + - name: Cache + uses: actions/cache@v1 + env: + cache-name: cache-cabal + with: + path: ~/.cabal + key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}- + ${{ runner.os }}-${{ matrix.ghc }}-build- + ${{ runner.os }}-${{ matrix.ghc }}- + ${{ runner.os }} + + - name: Install dependencies + run: | + cabal update + cabal build --only-dependencies --enable-tests --enable-benchmarks + - name: Build + run: cabal build --enable-tests --enable-benchmarks all + - name: Run tests + run: cabal test all + - name: Build Docs + run: cabal haddock diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eaeff0db..00000000 --- a/.travis.yml +++ /dev/null @@ -1,177 +0,0 @@ -# This Travis job script has been generated by a script via -# -# haskell-ci '--config=cabal.haskell-ci' 'cabal.project' -# -# For more information, see https://github.com/haskell-CI/haskell-ci -# -# version: 0.3.20190814 -# -language: c -dist: xenial -sudo: required -git: - # whether to recursively clone submodules - submodules: false -cache: - directories: - - $HOME/.cabal/packages - - $HOME/.cabal/store -before_cache: - - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log - # remove files that are regenerated by 'cabal update' - - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.* - - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar - - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx - - rm -rfv $CABALHOME/packages/head.hackage -matrix: - include: - - compiler: ghcjs-8.4 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}} - - compiler: ghc-8.8.1 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}} - - compiler: ghc-8.6.5 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}} - - compiler: ghc-8.4.4 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}} - - compiler: ghc-8.2.2 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}} - - compiler: ghc-8.0.2 - addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}} -before_install: - - | - if [ "$TRAVIS_OS_NAME" = "linux" ]; then - sudo add-apt-repository -y ppa:hvr/ghcjs; - curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add - - sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main' - sudo apt-get update; - sudo apt-get install $CC cabal-install-3.0 nodejs; - fi - - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//') - - WITHCOMPILER="-w $HC" - - | - if echo $CC | grep -q ghcjs; then - GHCJS=true - HC=${HC}js - WITHCOMPILER="--ghcjs ${WITHCOMPILER}js" - else - GHCJS=false; - fi - - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi - - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi - - HCPKG="$HC-pkg" - - unset CC - - CABAL=/opt/ghc/bin/cabal - - CABALHOME=$HOME/.cabal - - export PATH="$CABALHOME/bin:$PATH" - - TOP=$(pwd) - - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')" - - echo $HCNUMVER - - CABAL="$CABAL -vnormal+nowrap+markoutput" - - set -o pipefail - - | - echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk - echo 'BEGIN { state = "output"; }' >> .colorful.awk - echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk - echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk - echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk - echo ' if (state == "cabal") {' >> .colorful.awk - echo ' print blue($0)' >> .colorful.awk - echo ' } else {' >> .colorful.awk - echo ' print $0' >> .colorful.awk - echo ' }' >> .colorful.awk - echo '}' >> .colorful.awk - - cat .colorful.awk - - | - color_cabal_output () { - awk -f $TOP/.colorful.awk - } - - echo text | color_cabal_output -install: - - ${CABAL} --version - - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" - - node --version - - echo $GHCJS - - TEST=--enable-tests - - BENCH=--enable-benchmarks - - BENCH=--disable-benchmarks - - HEADHACKAGE=false - - rm -f $CABALHOME/config - - | - echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config - echo "remote-build-reporting: anonymous" >> $CABALHOME/config - echo "write-ghc-environment-files: always" >> $CABALHOME/config - echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config - echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config - echo "world-file: $CABALHOME/world" >> $CABALHOME/config - echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config - echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config - echo "installdir: $CABALHOME/bin" >> $CABALHOME/config - echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config - echo "store-dir: $CABALHOME/store" >> $CABALHOME/config - echo "install-dirs user" >> $CABALHOME/config - echo " prefix: $CABALHOME" >> $CABALHOME/config - echo "repository hackage.haskell.org" >> $CABALHOME/config - echo " url: http://hackage.haskell.org/" >> $CABALHOME/config - - cat $CABALHOME/config - - rm -fv cabal.project cabal.project.local cabal.project.freeze - - travis_retry ${CABAL} v2-update -v - - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi - # Generate cabal.project - - rm -rf cabal.project cabal.project.local cabal.project.freeze - - touch cabal.project - - | - echo "packages: ." >> cabal.project - - | - - cat cabal.project || true - - cat cabal.project.local || true - - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi - - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output - - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'" - - rm cabal.project.freeze -script: - - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) - # Packaging... - - ${CABAL} v2-sdist all | color_cabal_output - # Unpacking... - - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ - - cd ${DISTDIR} || false - - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \; - - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \; - - PKGDIR_reflex="$(find . -maxdepth 1 -type d -regex '.*/reflex-[0-9.]*')" - # Generate cabal.project - - rm -rf cabal.project cabal.project.local cabal.project.freeze - - touch cabal.project - - | - echo "packages: ${PKGDIR_reflex}" >> cabal.project - - | - - cat cabal.project || true - - cat cabal.project.local || true - # Building... - # this builds all libraries and executables (without tests/benchmarks) - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output - # Building with tests and benchmarks... - # build & run tests, build benchmarks - - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output - # Testing... - - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi - # cabal check... - - (cd ${PKGDIR_reflex} && ${CABAL} -vnormal check) - # haddock... - - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi - # Constraint sets - - rm -rf cabal.project.local - # Constraint set no-th - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex -use-template-haskell' all | color_cabal_output - # Constraint set old-these - - if $GHCJS || ! $GHCJS && [ $HCNUMVER -lt 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output ; fi - # Constraint set old-witherable - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output - # Constraint set debug-propagation - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-propagation' all | color_cabal_output - # Constraint set debug-cycles - - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-cycles' all | color_cabal_output - -# REGENDATA ["--config=cabal.haskell-ci","cabal.project"] -# EOF diff --git a/ChangeLog.md b/ChangeLog.md index d4cc79ca..ba534421 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,11 @@ # Revision history for reflex +## 0.7.1.1 + +* Add support for GHC 8.10 +* Extend some dependency version bounds +* Fix HLint 3 test + ## 0.7.1.0 * ([#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. diff --git a/README.md b/README.md index 5a607f05..ea815fe5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## [Reflex](https://reflex-frp.org/) -[![Hackage](https://img.shields.io/hackage/v/reflex.svg)](http://hackage.haskell.org/package/reflex) +[![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) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE) ### Practical Functional Reactive Programming diff --git a/cabal.project.freeze b/cabal.project.freeze deleted file mode 100644 index db06f8ef..00000000 --- a/cabal.project.freeze +++ /dev/null @@ -1,2 +0,0 @@ -constraints: any.text < 1.2.4.0 - , hlint < 2.2.6 || > 2.2.6 diff --git a/reflex.cabal b/reflex.cabal index 81459bfd..d34cf642 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.7.1.0 +Version: 0.7.1.1 Synopsis: Higher-order Functional Reactive Programming Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system License: BSD3 @@ -18,8 +18,8 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1, - GHCJS ==8.4 + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2, + GHCJS ==8.6 flag use-reflex-optimizer description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental. @@ -61,21 +61,22 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.9 && < 4.14, + base >= 4.11 && < 4.15, bifunctors >= 5.2 && < 5.6, comonad >= 5.0.4 && < 5.1, constraints-extras >= 0.3 && < 0.4, containers >= 0.6 && < 0.7, data-default >= 0.5 && < 0.8, - dependent-map >= 0.3 && < 0.4, + dependent-map >= 0.3 && < 0.5, exception-transformers == 0.4.*, lens >= 4.7 && < 5, + mmorph >= 1.0 && < 1.2, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, patch >= 0.0.1 && < 0.1, - prim-uniq >= 0.1.0.1 && < 0.2, + prim-uniq >= 0.1.0.1 && < 0.3, primitive >= 0.5 && < 0.8, - profunctors >= 5.3 && < 5.6, + profunctors >= 5.3 && < 5.7, random == 1.1.*, ref-tf == 0.4.*, reflection == 2.1.*, @@ -85,10 +86,10 @@ library time >= 1.4 && < 1.10, transformers >= 0.5.6.0 && < 0.6, unbounded-delays >= 0.1.0.0 && < 0.2, - witherable >= 0.3 && < 0.3.2 + witherable >= 0.3 && < 0.4 if flag(split-these) - build-depends: these >= 1 && <1.1, + build-depends: these >= 1 && <1.2, semialign >=1 && <1.2, monoidal-containers >= 0.6 && < 0.7 else @@ -170,10 +171,10 @@ library if flag(use-template-haskell) cpp-options: -DUSE_TEMPLATE_HASKELL build-depends: - dependent-sum >= 0.6 && < 0.7, - haskell-src-exts >= 1.16 && < 1.23, + dependent-sum >= 0.6 && < 0.8, + haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.16 + template-haskell >= 2.9 && < 2.17 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell @@ -244,7 +245,10 @@ test-suite hlint , directory , filepath , filemanip - , hlint (< 2.1 || >= 2.2.2) && < 3 + if impl(ghc >= 8.8) + build-depends: hlint >= 3 + else + build-depends: hlint (< 2.1 || >= 2.2.2) && < 3 if impl(ghcjs) buildable: False diff --git a/src/Reflex/Adjustable/Class.hs b/src/Reflex/Adjustable/Class.hs index ab14122c..c58e3f28 100644 --- a/src/Reflex/Adjustable/Class.hs +++ b/src/Reflex/Adjustable/Class.hs @@ -30,7 +30,8 @@ module Reflex.Adjustable.Class import Control.Monad.Identity import Control.Monad.Reader -import Data.Dependent.Map (DMap, GCompare (..)) +import Data.Dependent.Map (DMap) +import Data.GADT.Compare (GCompare(..)) import qualified Data.Dependent.Map as DMap import Data.Functor.Constant import Data.Functor.Misc diff --git a/src/Reflex/Class.hs b/src/Reflex/Class.hs index fefc7fa1..c9aa76c4 100644 --- a/src/Reflex/Class.hs +++ b/src/Reflex/Class.hs @@ -196,7 +196,8 @@ import Data.Align import Data.Bifunctor import Data.Coerce import Data.Default -import Data.Dependent.Map (DMap, DSum (..)) +import Data.Dependent.Map (DMap) +import Data.Dependent.Sum (DSum (..)) import qualified Data.Dependent.Map as DMap import Data.Functor.Compose import Data.Functor.Product diff --git a/src/Reflex/EventWriter/Base.hs b/src/Reflex/EventWriter/Base.hs index 517af0db..3043e6f9 100644 --- a/src/Reflex/EventWriter/Base.hs +++ b/src/Reflex/EventWriter/Base.hs @@ -39,8 +39,9 @@ import Control.Monad.Primitive import Control.Monad.Reader import Control.Monad.Ref import Control.Monad.State.Strict -import Data.Dependent.Map (DMap, DSum (..)) +import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap +import Data.Dependent.Sum (DSum (..)) import Data.Functor.Compose import Data.Functor.Misc import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..)) diff --git a/src/Reflex/PerformEvent/Class.hs b/src/Reflex/PerformEvent/Class.hs index 51e2bc30..4a6abd09 100644 --- a/src/Reflex/PerformEvent/Class.hs +++ b/src/Reflex/PerformEvent/Class.hs @@ -8,7 +8,6 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} diff --git a/src/Reflex/PostBuild/Base.hs b/src/Reflex/PostBuild/Base.hs index 7ed0e5af..b0ead0f2 100644 --- a/src/Reflex/PostBuild/Base.hs +++ b/src/Reflex/PostBuild/Base.hs @@ -9,7 +9,6 @@ {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} diff --git a/src/Reflex/PostBuild/Class.hs b/src/Reflex/PostBuild/Class.hs index 8b65a9d0..dac1516b 100644 --- a/src/Reflex/PostBuild/Class.hs +++ b/src/Reflex/PostBuild/Class.hs @@ -6,7 +6,6 @@ {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} diff --git a/src/Reflex/Profiled.hs b/src/Reflex/Profiled.hs index e7789ed1..82bc7cb8 100644 --- a/src/Reflex/Profiled.hs +++ b/src/Reflex/Profiled.hs @@ -28,7 +28,8 @@ import Control.Monad.Ref import Control.Monad.State.Strict (StateT, execStateT, modify) import Data.Bifunctor import Data.Coerce -import Data.Dependent.Map (DMap, GCompare) +import Data.Dependent.Map (DMap) +import Data.GADT.Compare (GCompare) import Data.FastMutableIntMap import Data.IORef import Data.List @@ -146,7 +147,7 @@ instance Reflex t => Reflex (ProfiledTimeline t) where pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e pull = Behavior_Profiled . pull . coerce fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e) - mergeG :: forall (k :: z -> *) q v. GCompare k + mergeG :: forall z (k :: z -> *) q v. GCompare k => (forall a. q a -> Event (ProfiledTimeline t) (v a)) -> DMap k q -> Event (ProfiledTimeline t) (DMap k v) mergeG nt = Event_Profiled #. mergeG (coerce nt) diff --git a/src/Reflex/Pure.hs b/src/Reflex/Pure.hs index 05247696..f5487749 100644 --- a/src/Reflex/Pure.hs +++ b/src/Reflex/Pure.hs @@ -32,7 +32,8 @@ module Reflex.Pure ) where import Control.Monad -import Data.Dependent.Map (DMap, GCompare) +import Data.Dependent.Map (DMap) +import Data.GADT.Compare (GCompare) import qualified Data.Dependent.Map as DMap import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap diff --git a/src/Reflex/Query/Base.hs b/src/Reflex/Query/Base.hs index 5ee67759..b7edd821 100644 --- a/src/Reflex/Query/Base.hs +++ b/src/Reflex/Query/Base.hs @@ -27,11 +27,13 @@ import Control.Monad.Reader import Control.Monad.Ref import Control.Monad.State.Strict import Data.Align -import Data.Dependent.Map (DMap, DSum (..)) +import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap +import Data.Dependent.Sum (DSum(..)) import Data.Foldable import Data.Functor.Compose import Data.Functor.Misc +import Data.GADT.Compare (GCompare) import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap import Data.Map (Map) @@ -135,7 +137,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch return (liftedResult0, liftedResult') - 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')) + 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')) traverseDMapWithKeyWithAdjust f dm0 dm' = do let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a) f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v @@ -180,7 +182,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch return (liftedResult0, liftedResult') - 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')) + 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')) traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a) f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v diff --git a/src/Reflex/Requester/Base.hs b/src/Reflex/Requester/Base.hs index 95440eb2..ab81f38e 100644 --- a/src/Reflex/Requester/Base.hs +++ b/src/Reflex/Requester/Base.hs @@ -55,8 +55,9 @@ import Control.Monad.Ref import Control.Monad.State.Strict import Data.Bits import Data.Coerce -import Data.Dependent.Map (DMap, DSum (..)) +import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap +import Data.Dependent.Sum (DSum (..)) import Data.Functor.Compose import Data.Functor.Misc import Data.IntMap.Strict (IntMap) diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs index e6eaab81..6b58f734 100644 --- a/src/Reflex/Spider/Internal.hs +++ b/src/Reflex/Spider/Internal.hs @@ -9,7 +9,6 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -20,6 +19,7 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE MultiWayIf #-} #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} @@ -46,8 +46,9 @@ import Control.Monad.Fail (MonadFail) import qualified Control.Monad.Fail as MonadFail import Data.Align import Data.Coerce -import Data.Dependent.Map (DMap, DSum (..)) +import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap +import Data.Dependent.Sum (DSum (..)) import Data.FastMutableIntMap (FastMutableIntMap, PatchIntMap (..)) import qualified Data.FastMutableIntMap as FastMutableIntMap import Data.Foldable hiding (concat, elem, sequence_) @@ -2739,7 +2740,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e)) {-# INLINABLE mergeG #-} mergeG - :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k + :: forall k2 (k :: k2 -> *) q (v :: k2 -> *). GCompare k => (forall a. q a -> R.Event (SpiderTimeline x) (v a)) -> DMap k q -> R.Event (SpiderTimeline x) (DMap k v) diff --git a/test/GC.hs b/test/GC.hs index ff859b7c..1cae3026 100644 --- a/test/GC.hs +++ b/test/GC.hs @@ -18,6 +18,7 @@ import Data.GADT.Compare import Data.IORef import Data.Semigroup import Data.These +import Data.Type.Equality ((:~:)(Refl)) import Data.Functor.Misc import Data.Patch diff --git a/test/Reflex/Test/CrossImpl.hs b/test/Reflex/Test/CrossImpl.hs index 6ace2d3d..4d9abed9 100644 --- a/test/Reflex/Test/CrossImpl.hs +++ b/test/Reflex/Test/CrossImpl.hs @@ -26,7 +26,7 @@ import qualified Reflex.Profiled as Prof import Control.Arrow (second, (&&&)) import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_) import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_) -import Data.Dependent.Map (DSum (..)) +import Data.Dependent.Sum (DSum (..)) import Data.Foldable import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map diff --git a/test/hlint.hs b/test/hlint.hs index 869e412c..1d0e319d 100644 --- a/test/hlint.hs +++ b/test/hlint.hs @@ -1,7 +1,7 @@ module Main where import Control.Monad -import Language.Haskell.HLint3 (hlint) +import Language.Haskell.HLint (hlint) import System.Directory import System.Exit (exitFailure, exitSuccess) import System.FilePath @@ -22,6 +22,7 @@ main = do , "--ignore=Use unless" , "--ignore=Reduce duplication" , "--cpp-define=USE_TEMPLATE_HASKELL" + , "--cpp-define=DEBUG" , "--ignore=Use tuple-section" ] recurseInto = and <$> sequence From 75e8fd5d1b3b14694fd2c759dc467b70520cf0a9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 15 Jun 2021 17:17:19 -0400 Subject: [PATCH 21/69] Also mention for 0.7.1.1 that GHC < 8.4 is dropped --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index ba534421..8c983562 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,7 @@ ## 0.7.1.1 * Add support for GHC 8.10 +* Drop support for GHC < 8.4 * Extend some dependency version bounds * Fix HLint 3 test From 5967c4b1296600b1808d0abc7010dd8bb4f5515c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 15 Jun 2021 17:26:31 -0400 Subject: [PATCH 22/69] Mmorph shouldn't be needed yet This as an oversight in backporting the GHC 8.10 support. --- reflex.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index d34cf642..3f92cedc 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -70,7 +70,6 @@ library dependent-map >= 0.3 && < 0.5, exception-transformers == 0.4.*, lens >= 4.7 && < 5, - mmorph >= 1.0 && < 1.2, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, patch >= 0.0.1 && < 0.1, From 6fa856ba38d764f7d84a41d8c7ae81534a623b7e Mon Sep 17 00:00:00 2001 From: Tom Smalley Date: Thu, 17 Jun 2021 12:43:25 +0100 Subject: [PATCH 23/69] Relax hlint bounds for old GHCs --- ChangeLog.md | 4 ++++ reflex.cabal | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index cd2df663..e261696b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## Unreleased + +* Allow newer hlint for older GHCs, and add upper bound for newer GHCs + ## 0.8.1.0 * Add support for GHC 8.10 diff --git a/reflex.cabal b/reflex.cabal index d347a7d3..5399ba33 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -258,9 +258,9 @@ test-suite hlint , filepath , filemanip if impl(ghc >= 8.8) - build-depends: hlint >= 3 + build-depends: hlint >= 3 && < 4 else - build-depends: hlint (< 2.1 || >= 2.2.2) && < 3 + build-depends: hlint (< 2.1 || >= 2.2.2) && < 4 if impl(ghcjs) buildable: False From bcee850f64049e01c78c4a2d97916908359332dc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Sep 2021 15:51:26 -0400 Subject: [PATCH 24/69] Bump version number for release --- ChangeLog.md | 2 +- reflex.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 87aded5f..504a3221 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,6 @@ # Revision history for reflex -## Unreleased +## 0.8.1.1 * Allow newer hlint for older GHCs, and add upper bound for newer GHCs diff --git a/reflex.cabal b/reflex.cabal index f0b47ae9..531aaf39 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.8.1.0 +Version: 0.8.1.1 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 86ab4e2b3c41ef521ca1e90e403c0b126ac70f64 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Sep 2021 16:15:06 -0400 Subject: [PATCH 25/69] Read me: Update heading levels --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cf07c47b..2b6ba088 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## [Reflex](https://reflex-frp.org/) +# [Reflex](https://reflex-frp.org/) [![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) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE) @@ -8,7 +8,7 @@ Reflex is a fully-deterministic, higher-order Functional Reactive Programming in **Visit https://reflex-frp.org for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).** -### Resources +## Resources * [Official Website](https://reflex-frp.org) * [Quick Reference](Quickref.md) @@ -18,6 +18,6 @@ Reflex is a fully-deterministic, higher-order Functional Reactive Programming in * [/r/reflexfrp](https://www.reddit.com/r/reflexfrp) * [irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4) -### Hacking +## Hacking 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. From 721d5a5a178fa7f0149a65a8d68d302acb19a80c Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Tue, 26 Oct 2021 16:50:20 -0400 Subject: [PATCH 26/69] requester: Add matchResponseMapWithRequests This allows batch processing of responses --- reflex.cabal | 1 + src/Reflex/Requester/Base.hs | 117 +++++++++++++++++++++++++++-------- 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/reflex.cabal b/reflex.cabal index 531aaf39..6362dfa0 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -74,6 +74,7 @@ library base >= 4.11 && < 4.15, bifunctors >= 5.2 && < 5.6, comonad >= 5.0.4 && < 5.1, + constraints >= 0.10 && <0.14, constraints-extras >= 0.3 && < 0.4, containers >= 0.6 && < 0.7, data-default >= 0.5 && < 0.8, diff --git a/src/Reflex/Requester/Base.hs b/src/Reflex/Requester/Base.hs index e6799dd6..6366b07b 100644 --- a/src/Reflex/Requester/Base.hs +++ b/src/Reflex/Requester/Base.hs @@ -1,6 +1,7 @@ -- | This module provides 'RequesterT', the standard implementation of -- 'Requester'. {-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE CPP #-} {-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE ExistentialQuantification #-} @@ -32,6 +33,7 @@ module Reflex.Requester.Base , requesterDataToList , singletonRequesterData , matchResponsesWithRequests + , matchResponseMapWithRequests , multiEntry , unMultiEntry , requesting' @@ -56,6 +58,7 @@ import Control.Monad.Ref import Control.Monad.State.Strict import Data.Bits import Data.Coerce +import Data.Constraint import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap import Data.Dependent.Sum (DSum (..)) @@ -81,19 +84,51 @@ newtype TagMap (f :: * -> *) = TagMap (IntMap Any) newtype RequesterData f = RequesterData (TagMap (Entry f)) +emptyRequesterData :: RequesterData f +emptyRequesterData = RequesterData $ TagMap IntMap.empty + data RequesterDataKey a where RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead - RequesterDataKey_Multi2 :: {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a + RequesterDataKey_Multi2 :: GCompare k => {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f singletonRequesterData rdk v = case rdk of RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v - RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v + RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Multi2Contents + { _multi2Contents_values = Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v + , _multi2Contents_dict = Dict + } RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v +mergeRequesterData :: RequesterData f -> RequesterData f -> RequesterData f +mergeRequesterData (RequesterData a) (RequesterData b) = RequesterData $ mergeTagMap a b + +mergeTagMap :: forall f. TagMap (Entry f) -> TagMap (Entry f) -> TagMap (Entry f) +mergeTagMap (TagMap m) (TagMap n) = + TagMap $ IntMap.unionWithKey (g' combiner) m n + where + combiner :: forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a + combiner k (Entry a) (Entry b) = Entry $ case myKeyType k of + MyTagType_Single -> a + MyTagType_Multi -> IntMap.unionWith mergeRequesterData a b + MyTagType_Multi2 -> case _multi2Contents_dict a of + Dict -> Multi2Contents + { _multi2Contents_values = Map.unionWith (IntMap.unionWith mergeRequesterData) (_multi2Contents_values a) (_multi2Contents_values b) + , _multi2Contents_dict = Dict + } + MyTagType_Multi3 -> IntMap.unionWith (IntMap.unionWith mergeRequesterData) a b + g' :: (forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a) -> Int -> Any -> Any -> Any + g' f rawKey a b = + let k = MyTag rawKey :: MyTag a + fromAny :: Any -> Entry f a + fromAny = unsafeCoerce + toAny :: Entry f a -> Any + toAny = unsafeCoerce + in toAny $ f k (fromAny a) (fromAny b) + requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f] requesterDataToList (RequesterData m) = do k :=> Entry e <- tagMapToList m @@ -103,11 +138,12 @@ requesterDataToList (RequesterData m) = do (k', e') <- IntMap.toList e k'' :=> e'' <- requesterDataToList e' return $ RequesterDataKey_Multi k k' k'' :=> e'' - MyTagType_Multi2 -> do - (k', e') <- Map.toList e - (k'', e'') <- IntMap.toList e' - k''' :=> e''' <- requesterDataToList e'' - return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e''' + MyTagType_Multi2 -> case _multi2Contents_dict e of + Dict -> do + (k', e') <- Map.toList $ _multi2Contents_values e + (k'', e'') <- IntMap.toList e' + k''' :=> e''' <- requesterDataToList e'' + return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e''' MyTagType_Multi3 -> do (k', e') <- IntMap.toList e (k'', e'') <- IntMap.toList e' @@ -135,7 +171,13 @@ traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWith go k (Entry request) = Entry <$> case myKeyType k of MyTagType_Single -> f request MyTagType_Multi -> traverse (traverseRequesterData f) request - MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request + MyTagType_Multi2 -> case request of + Multi2Contents { _multi2Contents_values = request', _multi2Contents_dict = Dict } -> do + v <- traverse (traverse (traverseRequesterData f)) request' + pure $ Multi2Contents + { _multi2Contents_values = v + , _multi2Contents_dict = Dict + } MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request -- | 'traverseRequesterData' with its arguments flipped @@ -179,9 +221,14 @@ instance MyTagTypeOffset Multi3 where type family EntryContents request a where EntryContents request (Single a) = request a EntryContents request Multi = IntMap (RequesterData request) - EntryContents request (Multi2 k) = Map (Some k) (IntMap (RequesterData request)) + EntryContents request (Multi2 k) = Multi2Contents k request EntryContents request Multi3 = IntMap (IntMap (RequesterData request)) +data Multi2Contents k request = Multi2Contents + { _multi2Contents_dict :: {-# UNPACK #-} !(Dict (GCompare k)) -- This is a Dict instead of an existential context because we only want to use it in certain circumstances + , _multi2Contents_values :: {-# UNPACK #-} !(Map (Some k) (IntMap (RequesterData request))) + } + newtype Entry request x = Entry { unEntry :: EntryContents request x } {-# INLINE singleEntry #-} @@ -310,7 +357,7 @@ tagRequest req = do return t {-# INLINE responseFromTag #-} -responseFromTag :: Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x)) +responseFromTag :: forall m t request response x. Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x)) responseFromTag (MyTagWrap t) = do responses :: EventSelectorInt t Any <- RequesterT ask return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t @@ -440,9 +487,9 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response))) responses = fanMap $ fmapCheap unpack response unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response)) - unpack = unEntry + unpack = _multi2Contents_values . unEntry pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k) - pack = Entry + pack m = Entry $ Multi2Contents { _multi2Contents_values = m, _multi2Contents_dict = Dict } f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a) f' k (Compose (n, v)) = do (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k)) @@ -463,9 +510,6 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN data Decoder rawResponse response = forall a. Decoder (RequesterDataKey a) (rawResponse -> response a) --- | Matches incoming responses with previously-sent requests --- and uses the provided request "decoder" function to process --- incoming responses. matchResponsesWithRequests :: forall t rawRequest rawResponse request response m. ( MonadFix m @@ -484,7 +528,30 @@ matchResponsesWithRequests ) -- ^ A map of outgoing wire-format requests and an event of responses keyed -- by the 'RequesterData' key of the associated outgoing request -matchResponsesWithRequests f send recv = do +matchResponsesWithRequests f send recv = matchResponseMapWithRequests f send $ uncurry Map.singleton <$> recv + +-- | Matches incoming responses with previously-sent requests +-- and uses the provided request "decoder" function to process +-- incoming responses. +matchResponseMapWithRequests + :: forall t rawRequest rawResponse request response m. + ( MonadFix m + , MonadHold t m + , Reflex t + ) + => (forall a. request a -> (rawRequest, rawResponse -> response a)) + -- ^ Given a request (from 'Requester'), produces the wire format of the + -- request and a function used to process the associated response + -> Event t (RequesterData request) + -- ^ The outgoing requests + -> Event t (Map Int rawResponse) + -- ^ A map of incoming responses, tagged by an identifying key + -> m ( Event t (Map Int rawRequest) + , Event t (RequesterData response) + ) + -- ^ A map of outgoing wire-format requests and an event of responses keyed + -- by the 'RequesterData' key of the associated outgoing request +matchResponseMapWithRequests f send recv = do rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <- holdIncremental mempty $ leftmost @@ -526,17 +593,15 @@ matchResponsesWithRequests f send recv = do processIncoming :: Incremental t (PatchMap Int (Decoder rawResponse response)) -- A map of outstanding expected responses - -> Event t (Int, rawResponse) + -> Event t (Map Int rawResponse) -- A incoming response paired with its identifying key -> Event t (RequesterData response, PatchMap Int v) -- The decoded response and a patch that clears the outstanding responses queue - processIncoming waitingFor inc = flip push inc $ \(n, rawRsp) -> do + processIncoming waitingFor inc = flip push inc $ \rspMap -> do wf <- sample $ currentIncremental waitingFor - case Map.lookup n wf of - Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen.. - Just (Decoder k rspF) -> do - let rsp = rspF rawRsp - return $ Just - ( singletonRequesterData k rsp - , PatchMap $ Map.singleton n Nothing - ) + let match rawRsp (Decoder k rspF) = + let rsp = rspF rawRsp + in singletonRequesterData k rsp + matches = Map.intersectionWith match rspMap wf + pure $ if Map.null matches then Nothing else Just + (Map.foldl' mergeRequesterData emptyRequesterData matches, PatchMap $ Nothing <$ matches) From ecd219aab381deafa9aeb13019a2ebe10c3e443e Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Tue, 26 Oct 2021 17:04:20 -0400 Subject: [PATCH 27/69] Update changelog --- ChangeLog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 504a3221..f7ca5f55 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## Unreleased + +* Add `matchResponseMapWithRequests`, which it similar to `matchResponsesWithRequests` but allows processing of multiple responses at once. + ## 0.8.1.1 * Allow newer hlint for older GHCs, and add upper bound for newer GHCs From 823afd9424234cbe0134051f09a6710e54509cec Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Thu, 28 Oct 2021 16:16:57 -0400 Subject: [PATCH 28/69] v0.8.2.0 --- ChangeLog.md | 2 +- reflex.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index f7ca5f55..a137d711 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,6 @@ # Revision history for reflex -## Unreleased +## 0.8.2.0 * Add `matchResponseMapWithRequests`, which it similar to `matchResponsesWithRequests` but allows processing of multiple responses at once. diff --git a/reflex.cabal b/reflex.cabal index 6362dfa0..d929f481 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.8.1.1 +Version: 0.8.2.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 022999ec4fd38917b2e9819a9288c9ae26361949 Mon Sep 17 00:00:00 2001 From: Frank Staals Date: Sat, 13 Nov 2021 12:27:20 +0100 Subject: [PATCH 29/69] relaxed witherable upper bound --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index d929f481..fef9b460 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -97,7 +97,7 @@ library time >= 1.4 && < 1.10, transformers >= 0.5.6.0 && < 0.6, unbounded-delays >= 0.1.0.0 && < 0.2, - witherable >= 0.3 && < 0.4 + witherable >= 0.3 && < 0.5 if flag(split-these) build-depends: these >= 1 && <1.2, From b66b162a48a63a66e706d007c0c230f07b5a2393 Mon Sep 17 00:00:00 2001 From: Rosario Pulella Date: Thu, 17 Mar 2022 13:33:14 -0400 Subject: [PATCH 30/69] Docs fixes --- src/Reflex/PerformEvent/Class.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Reflex/PerformEvent/Class.hs b/src/Reflex/PerformEvent/Class.hs index 4a6abd09..ec80e706 100644 --- a/src/Reflex/PerformEvent/Class.hs +++ b/src/Reflex/PerformEvent/Class.hs @@ -1,4 +1,4 @@ --- | This module defines 'PerformEvent' and 'TriggerEvent', which mediate the +-- | This module defines 'PerformEvent', which mediates the -- interaction between a "Reflex"-based program and the external side-effecting -- actions such as 'IO'. {-# LANGUAGE CPP #-} From d7324682c10b8f0deb339b095bea70c626e7c840 Mon Sep 17 00:00:00 2001 From: Rosario Pulella Date: Thu, 17 Mar 2022 15:01:29 -0400 Subject: [PATCH 31/69] Fix spacing --- src/Reflex/Workflow.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Reflex/Workflow.hs b/src/Reflex/Workflow.hs index faba2353..71317a1e 100644 --- a/src/Reflex/Workflow.hs +++ b/src/Reflex/Workflow.hs @@ -25,6 +25,7 @@ import Reflex.NotReady.Class import Reflex.PostBuild.Class -- | A widget in a workflow +-- -- When the 'Event' returned by a 'Workflow' fires, the current 'Workflow' is replaced by the one inside the firing 'Event'. A series of 'Workflow's must share the same return type. newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) } From 1ef1374f76fe9abfcab8a83dd00f0f51582b68ab Mon Sep 17 00:00:00 2001 From: Doug Beardsley Date: Wed, 20 Apr 2022 14:48:13 -0400 Subject: [PATCH 32/69] Add EventWriter instance for RequesterT --- src/Reflex/Requester/Base.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Reflex/Requester/Base.hs b/src/Reflex/Requester/Base.hs index 6366b07b..d67b0535 100644 --- a/src/Reflex/Requester/Base.hs +++ b/src/Reflex/Requester/Base.hs @@ -42,6 +42,7 @@ module Reflex.Requester.Base import Reflex.Class import Reflex.Adjustable.Class import Reflex.Dynamic +import Reflex.EventWriter.Class import Reflex.Host.Class import Reflex.PerformEvent.Class import Reflex.PostBuild.Class @@ -298,6 +299,9 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m) deriving instance PostBuild t m => PostBuild t (RequesterT t request response m) deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m) +instance EventWriter t w m => EventWriter t w (RequesterT t request response m) where + tellEvent = lift . tellEvent + instance PrimMonad m => PrimMonad (RequesterT t request response m) where type PrimState (RequesterT t request response m) = PrimState m primitive = lift . primitive From f9bc61a372ba94712f690fe454573dd9468c8c21 Mon Sep 17 00:00:00 2001 From: Malte Brandy Date: Tue, 15 Mar 2022 23:15:27 +0100 Subject: [PATCH 33/69] Bump bounds for stackage-nightly and ghc 9.0 --- .github/workflows/haskell.yml | 6 +++--- reflex.cabal | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index 8b751c90..6dfb85cf 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -6,14 +6,14 @@ jobs: build: strategy: matrix: - ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2'] + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2', '9.0.1'] os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} name: GHC ${{ matrix.ghc }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-haskell@v1 + - uses: haskell/actions/setup@v1 with: ghc-version: ${{ matrix.ghc }} - name: Cache @@ -36,6 +36,6 @@ jobs: - name: Build run: cabal build --enable-tests --enable-benchmarks all - name: Run tests - run: cabal test all + run: cabal test --enable-tests all - name: Build Docs run: cabal haddock diff --git a/reflex.cabal b/reflex.cabal index d929f481..ec76cb37 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -28,7 +28,7 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2, + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2 || ==9.0.1, GHCJS ==8.6 flag use-reflex-optimizer @@ -71,7 +71,7 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.11 && < 4.15, + base >= 4.11 && < 4.16, bifunctors >= 5.2 && < 5.6, comonad >= 5.0.4 && < 5.1, constraints >= 0.10 && <0.14, @@ -80,7 +80,7 @@ library data-default >= 0.5 && < 0.8, dependent-map >= 0.3 && < 0.5, exception-transformers == 0.4.*, - lens >= 4.7 && < 5, + lens >= 4.7 && < 5.1, mmorph >= 1.0 && < 1.2, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, @@ -88,8 +88,8 @@ library prim-uniq >= 0.1.0.1 && < 0.3, primitive >= 0.5 && < 0.8, profunctors >= 5.3 && < 5.7, - random == 1.1.*, - ref-tf == 0.4.*, + random >= 1.1 && < 1.3, + ref-tf >= 0.4 && < 0.6, reflection == 2.1.*, semigroupoids >= 4.0 && < 6, stm >= 2.4 && < 2.6, @@ -100,8 +100,8 @@ library witherable >= 0.3 && < 0.4 if flag(split-these) - build-depends: these >= 1 && <1.2, - semialign >=1 && <1.2, + build-depends: these >= 1 && <1.3, + semialign >=1 && <1.3, monoidal-containers >= 0.6 && < 0.7 else build-depends: these >= 0.4 && <0.9, @@ -186,7 +186,7 @@ library dependent-sum >= 0.6 && < 0.8, haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.17 + template-haskell >= 2.9 && < 2.18 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell From c144e8614934112a8c1a287b9bf66eac88aabf83 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Jun 2022 10:11:05 -0400 Subject: [PATCH 34/69] Reorder definitions for RequesterT test for GHC 9.0 This fixes the test. --- test/RequesterT.hs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/RequesterT.hs b/test/RequesterT.hs index 2f34a586..9bd19e0d 100644 --- a/test/RequesterT.hs +++ b/test/RequesterT.hs @@ -44,6 +44,17 @@ import Test.Run data RequestInt a where RequestInt :: Int -> RequestInt Int +data TestRequest a where + TestRequest_Reverse :: String -> TestRequest String + TestRequest_Increment :: Int -> TestRequest Int + +deriveArgDict ''TestRequest + +instance Show (TestRequest a) where + show = \case + TestRequest_Reverse str -> "reverse " <> str + TestRequest_Increment i -> "increment " <> show i + main :: IO () main = do os1 <- runApp' (unwrapApp testOrdering) $ @@ -196,10 +207,6 @@ delayedPulse pulse = void $ flip runWithReplace (pure () <$ pulse) $ do (_, pulse') <- runWithReplace (pure ()) $ pure (RequestInt 1) <$ pulse requestingIdentity pulse' -data TestRequest a where - TestRequest_Reverse :: String -> TestRequest String - TestRequest_Increment :: Int -> TestRequest Int - testMatchRequestsWithResponses :: forall m t req a . ( MonadFix m @@ -226,10 +233,3 @@ testMatchRequestsWithResponses pulse = mdo ( whichever @Show @req @a $ show r , \x -> has @Read r $ readMaybe x ) - -deriveArgDict ''TestRequest - -instance Show (TestRequest a) where - show = \case - TestRequest_Reverse str -> "reverse " <> str - TestRequest_Increment i -> "increment " <> show i From 3dc5953b46a706f911f511c3236506671e05a94c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Jun 2022 11:04:57 -0400 Subject: [PATCH 35/69] Make first revision --- reflex.cabal | 1 + 1 file changed, 1 insertion(+) diff --git a/reflex.cabal b/reflex.cabal index fef9b460..e4f5c1e3 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,6 @@ Name: reflex Version: 0.8.2.0 +x-revision: 1 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 481ea5299f3c2f656b6d7847985a13ebd132b01f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Jun 2022 11:05:30 -0400 Subject: [PATCH 36/69] Make second revision --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index 327c0d3c..8d9850b4 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,6 +1,6 @@ Name: reflex Version: 0.8.2.0 -x-revision: 1 +x-revision: 2 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From f800e529d5277b1d01059f3f4025bae5c2960540 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Jun 2022 04:23:34 -0400 Subject: [PATCH 37/69] * -> Type to fix warnings --- src/Reflex/Class.hs | 13 +++++++------ src/Reflex/Dynamic.hs | 11 ++++++----- src/Reflex/Host/Class.hs | 9 +++++---- src/Reflex/PerformEvent/Class.hs | 10 ++++++---- src/Reflex/Profiled.hs | 3 ++- src/Reflex/Query/Base.hs | 5 +++-- src/Reflex/Query/Class.hs | 7 ++++--- src/Reflex/Requester/Base.hs | 13 +++++++------ src/Reflex/Requester/Class.hs | 5 +++-- src/Reflex/Spider/Internal.hs | 2 +- 10 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/Reflex/Class.hs b/src/Reflex/Class.hs index 1ea6f0f0..fbdbfce5 100644 --- a/src/Reflex/Class.hs +++ b/src/Reflex/Class.hs @@ -209,6 +209,7 @@ import Data.Functor.Misc import Data.Functor.Plus import Data.IntMap.Strict (IntMap) import qualified Data.IntMap.Strict as IntMap +import Data.Kind (Type) import Data.List.NonEmpty (NonEmpty (..)) import Data.Map (Map) import Data.Semigroup (Semigroup (..)) @@ -240,25 +241,25 @@ class ( MonadHold t (PushM t) ) => Reflex t where -- | A container for a value that can change over time. 'Behavior's can be -- sampled at will, but it is not possible to be notified when they change - data Behavior t :: * -> * + data Behavior t :: Type -> Type -- | A stream of occurrences. During any given frame, an 'Event' is either -- occurring or not occurring; if it is occurring, it will contain a value of -- the given type (its "occurrence type") - data Event t :: * -> * + data Event t :: Type -> Type -- | A container for a value that can change over time and allows -- notifications on changes. Basically a combination of a 'Behavior' and an -- 'Event', with a rule that the 'Behavior' will change if and only if the -- 'Event' fires. - data Dynamic t :: * -> * + data Dynamic t :: Type -> Type -- | An 'Incremental' is a more general form of a 'Dynamic'. -- Instead of always fully replacing the value, only parts of it can be patched. -- This is only needed for performance critical code via `mergeIncremental` to make small -- changes to large values. - data Incremental t :: * -> * + data Incremental t :: Type -> Type -- | A monad for doing complex push-based calculations efficiently - type PushM t :: * -> * + type PushM t :: Type -> Type -- | A monad for doing complex pull-based calculations efficiently - type PullM t :: * -> * + type PullM t :: Type -> Type -- | An 'Event' with no occurrences never :: Event t a -- | Create a 'Behavior' that always has the given value diff --git a/src/Reflex/Dynamic.hs b/src/Reflex/Dynamic.hs index cbe9825d..649c7a87 100644 --- a/src/Reflex/Dynamic.hs +++ b/src/Reflex/Dynamic.hs @@ -92,6 +92,7 @@ import Data.Dependent.Sum (DSum (..)) import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..)) import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap +import Data.Kind (Type) import Data.Map (Map) import Data.Maybe import Data.Monoid ((<>)) @@ -381,7 +382,7 @@ demuxed d k = -- | A heterogeneous list whose type and length are fixed statically. This is -- reproduced from the 'HList' package due to integration issues, and because -- very little other functionality from that library is needed. -data HList (l::[*]) where +data HList (l::[Type]) where HNil :: HList '[] HCons :: e -> HList l -> HList (e ': l) @@ -437,7 +438,7 @@ data HListPtr l a where deriving instance Eq (HListPtr l a) deriving instance Ord (HListPtr l a) -fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f +fhlistToDMap :: forall (f :: Type -> Type) l. FHList f l -> DMap (HListPtr l) f fhlistToDMap = DMap.fromList . go where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f] go = \case @@ -477,8 +478,8 @@ distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ f -- | Indicates that all elements in a type-level list are applications of the -- same functor. -class AllAreFunctors (f :: a -> *) (l :: [a]) where - type FunctorList f l :: [*] +class AllAreFunctors (f :: a -> Type) (l :: [a]) where + type FunctorList f l :: [Type] toFHList :: HList (FunctorList f l) -> FHList f l fromFHList :: FHList f l -> HList (FunctorList f l) @@ -513,7 +514,7 @@ collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ to -- | Poor man's 'Generic's for product types only. class IsHList a where - type HListElems a :: [*] + type HListElems a :: [Type] toHList :: a -> HList (HListElems a) fromHList :: HList (HListElems a) -> a diff --git a/src/Reflex/Host/Class.hs b/src/Reflex/Host/Class.hs index 96ca065c..4f5a5b72 100644 --- a/src/Reflex/Host/Class.hs +++ b/src/Reflex/Host/Class.hs @@ -42,6 +42,7 @@ import qualified Control.Monad.Trans.State.Strict as Strict import Control.Monad.Trans.Writer (WriterT) import Data.Dependent.Sum (DSum (..)) import Data.GADT.Compare +import Data.Kind (Type) -- | Framework implementation support class for the reflex implementation -- represented by @t@. @@ -52,9 +53,9 @@ class ( Reflex t , MonadFix (HostFrame t) , MonadSubscribeEvent t (HostFrame t) ) => ReflexHost t where - type EventTrigger t :: * -> * - type EventHandle t :: * -> * - type HostFrame t :: * -> * + type EventTrigger t :: Type -> Type + type EventHandle t :: Type -> Type + type HostFrame t :: Type -> Type -- | Monad in which Events can be 'subscribed'. This forces all underlying -- event sources to be initialized, so that the event will fire whenever it @@ -114,7 +115,7 @@ class ( ReflexHost t , MonadSample t (ReadPhase m) , MonadHold t (ReadPhase m) ) => MonadReflexHost t m | m -> t where - type ReadPhase m :: * -> * + type ReadPhase m :: Type -> Type -- | Propagate some events firings and read the values of events afterwards. -- -- This function will create a new frame to fire the given events. It will diff --git a/src/Reflex/PerformEvent/Class.hs b/src/Reflex/PerformEvent/Class.hs index ec80e706..cd240a13 100644 --- a/src/Reflex/PerformEvent/Class.hs +++ b/src/Reflex/PerformEvent/Class.hs @@ -17,18 +17,20 @@ module Reflex.PerformEvent.Class , performEventAsync ) where -import Reflex.Class -import Reflex.TriggerEvent.Class - import Control.Monad.Reader import Control.Monad.Trans.Maybe (MaybeT (..)) +import Data.Kind (Type) + +import Reflex.Class +import Reflex.TriggerEvent.Class + -- | 'PerformEvent' represents actions that can trigger other actions based on -- 'Event's. class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where -- | The type of action to be triggered; this is often not the same type as -- the triggering action. - type Performable m :: * -> * + type Performable m :: Type -> Type -- | Perform the action contained in the given 'Event' whenever the 'Event' -- fires. Return the result in another 'Event'. Note that the output 'Event' -- will generally occur later than the input 'Event', since most 'Performable' diff --git a/src/Reflex/Profiled.hs b/src/Reflex/Profiled.hs index 68181a9e..f579df1b 100644 --- a/src/Reflex/Profiled.hs +++ b/src/Reflex/Profiled.hs @@ -33,6 +33,7 @@ import Data.GADT.Compare (GCompare) import Data.FastMutableIntMap import Data.IORef import Data.List +import Data.Kind (Type) import Data.Map (Map) import qualified Data.Map.Strict as Map import Data.Monoid ((<>)) @@ -147,7 +148,7 @@ instance Reflex t => Reflex (ProfiledTimeline t) where pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e pull = Behavior_Profiled . pull . coerce fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e) - mergeG :: forall z (k :: z -> *) q v. GCompare k + mergeG :: forall z (k :: z -> Type) q v. GCompare k => (forall a. q a -> Event (ProfiledTimeline t) (v a)) -> DMap k q -> Event (ProfiledTimeline t) (DMap k v) mergeG nt = Event_Profiled #. mergeG (coerce nt) diff --git a/src/Reflex/Query/Base.hs b/src/Reflex/Query/Base.hs index ec1ab548..ef09e051 100644 --- a/src/Reflex/Query/Base.hs +++ b/src/Reflex/Query/Base.hs @@ -37,6 +37,7 @@ import Data.Functor.Misc import Data.GADT.Compare (GCompare) import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap +import Data.Kind (Type) import Data.Map (Map) import qualified Data.Map as Map import Data.Monoid ((<>)) @@ -138,7 +139,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch return (liftedResult0, liftedResult') - 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')) + traverseDMapWithKeyWithAdjust :: forall (k :: Type -> Type) 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')) traverseDMapWithKeyWithAdjust f dm0 dm' = do let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a) f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v @@ -183,7 +184,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch return (liftedResult0, liftedResult') - 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')) + traverseDMapWithKeyWithAdjustWithMove :: forall (k :: Type -> Type) 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')) traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a) f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v diff --git a/src/Reflex/Query/Class.hs b/src/Reflex/Query/Class.hs index 794e72e7..0e333ced 100644 --- a/src/Reflex/Query/Class.hs +++ b/src/Reflex/Query/Class.hs @@ -27,19 +27,20 @@ module Reflex.Query.Class , mapQueryResult ) where +import Control.Applicative import Control.Category (Category) import qualified Control.Category as Cat import Control.Monad.Reader import Data.Bits import Data.Data import Data.Ix +import Data.Kind (Type) import Data.Map.Monoidal (MonoidalMap) import qualified Data.Map.Monoidal as MonoidalMap import Data.Semigroup (Semigroup(..)) -import Foreign.Storable import Data.Void import Data.Monoid hiding ((<>)) -import Control.Applicative +import Foreign.Storable import Reflex.Class @@ -48,7 +49,7 @@ import Reflex.Class -- The @crop@ function provides a way to determine what part of a given 'QueryResult' -- is relevant to a given 'Query'. class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where - type QueryResult a :: * + type QueryResult a :: Type crop :: a -> QueryResult a -> QueryResult a instance (Ord k, Query v) => Query (MonoidalMap k v) where diff --git a/src/Reflex/Requester/Base.hs b/src/Reflex/Requester/Base.hs index 6366b07b..ab65f1ce 100644 --- a/src/Reflex/Requester/Base.hs +++ b/src/Reflex/Requester/Base.hs @@ -66,6 +66,7 @@ import Data.Functor.Compose import Data.Functor.Misc import Data.IntMap.Strict (IntMap) import qualified Data.IntMap.Strict as IntMap +import Data.Kind (Type) import Data.Map (Map) import qualified Data.Map as Map import Data.Monoid ((<>)) @@ -80,7 +81,7 @@ import Unsafe.Coerce --TODO: Make this module type-safe -newtype TagMap (f :: * -> *) = TagMap (IntMap Any) +newtype TagMap (f :: Type -> Type) = TagMap (IntMap Any) newtype RequesterData f = RequesterData (TagMap (Entry f)) @@ -184,7 +185,7 @@ traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWith forRequesterData :: forall request response m. Applicative m => RequesterData request -> (forall a. request a -> m (response a)) -> m (RequesterData response) forRequesterData r f = traverseRequesterData f r -data MyTagType :: * -> * where +data MyTagType :: Type -> Type where MyTagType_Single :: MyTagType (Single a) MyTagType_Multi :: MyTagType Multi MyTagType_Multi2 :: MyTagType (Multi2 k) @@ -200,7 +201,7 @@ myKeyType (MyTag k) = case k .&. 0x3 of data Single a data Multi -data Multi2 (k :: * -> *) +data Multi2 (k :: Type -> Type) data Multi3 class MyTagTypeOffset x where @@ -247,7 +248,7 @@ unMultiEntry = unEntry -- WARNING: This type should never be exposed. In particular, this is extremely unsound if a MyTag from one run of runRequesterT is ever compared against a MyTag from another newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum) -newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum) +newtype MyTagWrap (f :: Type -> Type) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum) {-# INLINE castMyTagWrap #-} castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x) @@ -279,13 +280,13 @@ instance GCompare (MyTagWrap f) where EQ -> unsafeCoerce GEQ GT -> GGT -data RequesterState t (request :: * -> *) = RequesterState +data RequesterState t (request :: Type -> Type) = RequesterState { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom , _requesterState_requests :: ![(Int, Event t Any)] } -- | A basic implementation of 'Requester'. -newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a } +newtype RequesterT t request (response :: Type -> Type) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException -- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2 #if MIN_VERSION_base(4,9,1) diff --git a/src/Reflex/Requester/Class.hs b/src/Reflex/Requester/Class.hs index db4ddb99..679e6abd 100644 --- a/src/Reflex/Requester/Class.hs +++ b/src/Reflex/Requester/Class.hs @@ -22,6 +22,7 @@ import Control.Monad.Identity import Control.Monad.Reader import qualified Control.Monad.State.Lazy as Lazy import Control.Monad.State.Strict +import Data.Kind (Type) import Reflex.Class -- | A 'Requester' action can trigger requests of type @Request m a@ based on @@ -32,9 +33,9 @@ import Reflex.Class -- 'Reflex.PerformEvent.Class.performEvent'. class (Reflex t, Monad m) => Requester t m | m -> t where -- | The type of requests that this 'Requester' can emit - type Request m :: * -> * + type Request m :: Type -> Type -- | The type of responses that this 'Requester' can receive - type Response m :: * -> * + type Response m :: Type -> Type -- | Emit a request whenever the given 'Event' fires, and return responses in -- the resulting 'Event'. requesting :: Event t (Request m a) -> m (Event t (Response m a)) diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs index b6d83fed..ff4b4835 100644 --- a/src/Reflex/Spider/Internal.hs +++ b/src/Reflex/Spider/Internal.hs @@ -2775,7 +2775,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e)) {-# INLINABLE mergeG #-} mergeG - :: forall k2 (k :: k2 -> *) q (v :: k2 -> *). GCompare k + :: forall k2 (k :: k2 -> Type) q (v :: k2 -> Type). GCompare k => (forall a. q a -> R.Event (SpiderTimeline x) (v a)) -> DMap k q -> R.Event (SpiderTimeline x) (DMap k v) From c9175f4733dba055a778d1a42baf85c5f6b9bb17 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Jun 2022 03:40:21 -0400 Subject: [PATCH 38/69] Fix build for 9.2.2 --- .github/workflows/haskell.yml | 2 +- ChangeLog.md | 4 ++++ reflex.cabal | 10 +++++----- src/Reflex/Dynamic/TH.hs | 9 ++++++++- src/Reflex/FunctorMaybe.hs | 4 ++-- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index 6dfb85cf..0b16d958 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -6,7 +6,7 @@ jobs: build: strategy: matrix: - ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2', '9.0.1'] + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2', '9.0.1', '9.2.2'] os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} diff --git a/ChangeLog.md b/ChangeLog.md index a137d711..84fd3f65 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## Unreleased + +Fix build for GHC 9.2 + ## 0.8.2.0 * Add `matchResponseMapWithRequests`, which it similar to `matchResponsesWithRequests` but allows processing of multiple responses at once. diff --git a/reflex.cabal b/reflex.cabal index 8d9850b4..1e776bec 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -29,7 +29,7 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2 || ==9.0.1, + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2 || ==9.0.1 || ==9.2.2, GHCJS ==8.6 flag use-reflex-optimizer @@ -72,7 +72,7 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.11 && < 4.16, + base >= 4.11 && < 4.17, bifunctors >= 5.2 && < 5.6, comonad >= 5.0.4 && < 5.1, constraints >= 0.10 && <0.14, @@ -81,7 +81,7 @@ library data-default >= 0.5 && < 0.8, dependent-map >= 0.3 && < 0.5, exception-transformers == 0.4.*, - lens >= 4.7 && < 5.1, + lens >= 4.7 && < 5.2, mmorph >= 1.0 && < 1.2, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, @@ -95,7 +95,7 @@ library semigroupoids >= 4.0 && < 6, stm >= 2.4 && < 2.6, syb >= 0.5 && < 0.8, - time >= 1.4 && < 1.10, + time >= 1.4 && < 1.12, transformers >= 0.5.6.0 && < 0.6, unbounded-delays >= 0.1.0.0 && < 0.2, witherable >= 0.3 && < 0.5 @@ -187,7 +187,7 @@ library dependent-sum >= 0.6 && < 0.8, haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.18 + template-haskell >= 2.9 && < 2.19 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell diff --git a/src/Reflex/Dynamic/TH.hs b/src/Reflex/Dynamic/TH.hs index 06109d24..4f7b4271 100644 --- a/src/Reflex/Dynamic/TH.hs +++ b/src/Reflex/Dynamic/TH.hs @@ -45,9 +45,16 @@ qDynPure qe = do (e', exprsReversed) <- runStateT (gmapM f e) [] let exprs = reverse exprsReversed arg = foldr (\a b -> ConE 'FHCons `AppE` snd a `AppE` b) (ConE 'FHNil) exprs - param = foldr (\a b -> ConP 'HCons [VarP (fst a), b]) (ConP 'HNil []) exprs + param = foldr (\a b -> conPCompat 'HCons [VarP (fst a), b]) (conPCompat 'HNil []) exprs [| $(return $ LamE [param] e') <$> distributeFHListOverDynPure $(return arg) |] +conPCompat :: Name -> [Pat] -> Pat +#if MIN_VERSION_template_haskell(2, 18, 0) +conPCompat name = ConP name [] +#else +conPCompat = ConP +#endif + -- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a -- 'qDyn' quotation. unqDyn :: Q Exp -> Q Exp diff --git a/src/Reflex/FunctorMaybe.hs b/src/Reflex/FunctorMaybe.hs index 223d5e07..84656db5 100644 --- a/src/Reflex/FunctorMaybe.hs +++ b/src/Reflex/FunctorMaybe.hs @@ -16,7 +16,7 @@ module Reflex.FunctorMaybe import Data.IntMap (IntMap) import Data.Map (Map) -#if MIN_VERSION_base(4,9,0) +#if !MIN_VERSION_base(4,16,0) import Data.Semigroup (Option(..)) #endif import Data.Witherable @@ -33,7 +33,7 @@ class FunctorMaybe f where instance FunctorMaybe Maybe where fmapMaybe = mapMaybe -#if MIN_VERSION_base(4,9,0) +#if !MIN_VERSION_base(4,16,0) deriving instance FunctorMaybe Option #endif From 4cd322604596ac652f35bbe72c1ad8fe42f2efdc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 Jun 2022 14:12:28 -0400 Subject: [PATCH 39/69] Avoid TH compat CPP Instead of using the prone-to-breakage TH AST, use quoting and splicing. --- src/Reflex/Dynamic/TH.hs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Reflex/Dynamic/TH.hs b/src/Reflex/Dynamic/TH.hs index 4f7b4271..b7759bef 100644 --- a/src/Reflex/Dynamic/TH.hs +++ b/src/Reflex/Dynamic/TH.hs @@ -44,16 +44,15 @@ qDynPure qe = do _ -> gmapM f d (e', exprsReversed) <- runStateT (gmapM f e) [] let exprs = reverse exprsReversed - arg = foldr (\a b -> ConE 'FHCons `AppE` snd a `AppE` b) (ConE 'FHNil) exprs - param = foldr (\a b -> conPCompat 'HCons [VarP (fst a), b]) (conPCompat 'HNil []) exprs - [| $(return $ LamE [param] e') <$> distributeFHListOverDynPure $(return arg) |] - -conPCompat :: Name -> [Pat] -> Pat -#if MIN_VERSION_template_haskell(2, 18, 0) -conPCompat name = ConP name [] -#else -conPCompat = ConP -#endif + arg = foldr + (\(_, expr) rest -> [e| FHCons $(pure expr) $rest |]) + [e| FHNil |] + exprs + param = foldr + (\(name, _) rest -> [p| HCons $(pure $ VarP name) $rest |]) + [p| HNil |] + exprs + [| (\ $param -> $(pure e')) <$> distributeFHListOverDynPure $arg |] -- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a -- 'qDyn' quotation. From 2c4e9e2bdf2a62b46e52a86d20b871ca4539e5be Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Tue, 28 Jun 2022 22:47:39 -0400 Subject: [PATCH 40/69] Use patch >= 0.0.7 --- ChangeLog.md | 3 ++- reflex.cabal | 7 ++++--- src/Reflex/Query/Base.hs | 11 ++++++----- src/Reflex/Query/Class.hs | 5 +++-- test/QueryT.hs | 5 +++-- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 84fd3f65..baac3b57 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,7 +2,8 @@ ## Unreleased -Fix build for GHC 9.2 +* Fix build for GHC 9.2 +* Require patch >= 0.0.7.0 ## 0.8.2.0 diff --git a/reflex.cabal b/reflex.cabal index 1e776bec..958a6136 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,6 +1,5 @@ Name: reflex -Version: 0.8.2.0 -x-revision: 2 +Version: 0.8.2.1 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. @@ -75,6 +74,7 @@ library base >= 4.11 && < 4.17, bifunctors >= 5.2 && < 5.6, comonad >= 5.0.4 && < 5.1, + commutative-semigroups >= 0.1 && <0.2, constraints >= 0.10 && <0.14, constraints-extras >= 0.3 && < 0.4, containers >= 0.6 && < 0.7, @@ -85,7 +85,7 @@ library mmorph >= 1.0 && < 1.2, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, - patch >= 0.0.1 && < 0.1, + patch >= 0.0.7 && < 0.1, prim-uniq >= 0.1.0.1 && < 0.3, primitive >= 0.5 && < 0.8, profunctors >= 5.3 && < 5.7, @@ -376,6 +376,7 @@ test-suite QueryT main-is: QueryT.hs hs-source-dirs: test build-depends: base + , commutative-semigroups , containers , dependent-map , dependent-sum diff --git a/src/Reflex/Query/Base.hs b/src/Reflex/Query/Base.hs index ef09e051..59eaa0db 100644 --- a/src/Reflex/Query/Base.hs +++ b/src/Reflex/Query/Base.hs @@ -42,6 +42,7 @@ import Data.Map (Map) import qualified Data.Map as Map import Data.Monoid ((<>)) import qualified Data.Semigroup as S +import Data.Semigroup.Commutative import Data.Some (Some(Some)) import Data.These @@ -64,7 +65,7 @@ newtype QueryT t q m a = QueryT { unQueryT :: StateT [Behavior t q] (EventWriter deriving instance MonadHold t m => MonadHold t (QueryT t q m) deriving instance MonadSample t m => MonadSample t (QueryT t q m) -runQueryT :: (MonadFix m, Additive q, Group q, Reflex t) => QueryT t q m a -> Dynamic t (QueryResult q) -> m (a, Incremental t (AdditivePatch q)) +runQueryT :: (MonadFix m, Commutative q, Group q, Reflex t) => QueryT t q m a -> Dynamic t (QueryResult q) -> m (a, Incremental t (AdditivePatch q)) runQueryT (QueryT a) qr = do ((r, bs), es) <- runReaderT (runEventWriterT (runStateT a mempty)) qr return (r, unsafeBuildIncremental (foldlM (\b c -> (b <>) <$> sample c) mempty bs) (fmapCheap AdditivePatch es)) @@ -80,7 +81,7 @@ getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w maskMempty :: (Eq a, Monoid a) => a -> Maybe a maskMempty x = if x == mempty then Nothing else Just x -instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where +instance (Reflex t, MonadFix m, Group q, Commutative q, Query q, Eq q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where runWithReplace (QueryT a0) a' = do ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a' let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q @@ -283,7 +284,7 @@ instance (S.Semigroup a, Monad m) => S.Semigroup (QueryT t q m a) where (<>) = liftA2 (S.<>) -- | withQueryT's QueryMorphism argument needs to be a group homomorphism in order to behave correctly -withQueryT :: (MonadFix m, PostBuild t m, Group q, Group q', Additive q, Additive q', Query q') +withQueryT :: (MonadFix m, PostBuild t m, Group q, Group q', Commutative q, Commutative q', Query q') => QueryMorphism q q' -> QueryT t q m a -> QueryT t q' m a @@ -300,7 +301,7 @@ mapQueryT :: (forall b. m b -> n b) -> QueryT t q m a -> QueryT t q n a mapQueryT f (QueryT a) = QueryT $ mapStateT (mapEventWriterT (mapReaderT f)) a -- | dynWithQueryT's (Dynamic t QueryMorphism) argument needs to be a group homomorphism at all times in order to behave correctly -dynWithQueryT :: (MonadFix m, PostBuild t m, Group q, Additive q, Group q', Additive q', Query q') +dynWithQueryT :: (MonadFix m, PostBuild t m, Group q, Commutative q, Group q', Commutative q', Query q') => Dynamic t (QueryMorphism q q') -> QueryT t q m a -> QueryT t q' m a @@ -325,7 +326,7 @@ dynWithQueryT f q = do return $ Just $ AdditivePatch $ mconcat [ g a bOld, negateG (g aOld bOld), g a b] in unsafeBuildIncremental (g <$> sample (current da) <*> sample (currentIncremental ib)) ec -instance (Monad m, Group q, Additive q, Query q, Reflex t) => MonadQuery t q (QueryT t q m) where +instance (Monad m, Group q, Commutative q, Query q, Reflex t) => MonadQuery t q (QueryT t q m) where tellQueryIncremental q = do QueryT (modify (currentIncremental q:)) QueryT (lift (tellEvent (fmapCheap unAdditivePatch (updatedIncremental q)))) diff --git a/src/Reflex/Query/Class.hs b/src/Reflex/Query/Class.hs index 0e333ced..dc6a6888 100644 --- a/src/Reflex/Query/Class.hs +++ b/src/Reflex/Query/Class.hs @@ -38,6 +38,7 @@ import Data.Kind (Type) import Data.Map.Monoidal (MonoidalMap) import qualified Data.Map.Monoidal as MonoidalMap import Data.Semigroup (Semigroup(..)) +import Data.Semigroup.Commutative import Data.Void import Data.Monoid hiding ((<>)) import Foreign.Storable @@ -124,7 +125,7 @@ instance Monoid SelectedCount where instance Group SelectedCount where negateG (SelectedCount a) = SelectedCount (negate a) -instance Additive SelectedCount +instance Commutative SelectedCount -- | The Semigroup\/Monoid\/Group instances for a Query containing 'SelectedCount's should use -- this function which returns Nothing if the result is 0. This allows the pruning of leaves @@ -134,7 +135,7 @@ combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j the -- | A class that allows sending of 'Query's and retrieval of 'QueryResult's. See 'queryDyn' for a commonly -- used interface. -class (Group q, Additive q, Query q, Monad m) => MonadQuery t q m | m -> q t where +class (Group q, Commutative q, Query q, Monad m) => MonadQuery t q m | m -> q t where tellQueryIncremental :: Incremental t (AdditivePatch q) -> m () askQueryResult :: m (Dynamic t (QueryResult q)) queryIncremental :: Incremental t (AdditivePatch q) -> m (Dynamic t (QueryResult q)) diff --git a/test/QueryT.hs b/test/QueryT.hs index 47566510..2262e38d 100644 --- a/test/QueryT.hs +++ b/test/QueryT.hs @@ -17,6 +17,7 @@ import Data.Map (Map) import qualified Data.Map as Map import Data.Map.Monoidal (MonoidalMap) import Data.Semigroup +import Data.Semigroup.Commutative import Data.These #if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0)) @@ -28,7 +29,7 @@ import Data.Patch.MapWithMove import Test.Run newtype MyQuery = MyQuery SelectedCount - deriving (Show, Read, Eq, Ord, Monoid, Semigroup, Additive, Group) + deriving (Show, Read, Eq, Ord, Monoid, Semigroup, Commutative, Group) instance Query MyQuery where type QueryResult MyQuery = () @@ -62,7 +63,7 @@ instance (Ord k, Eq a, Monoid a, Align (MonoidalMap k)) => Monoid (Selector k a) instance (Eq a, Ord k, Group a, Align (MonoidalMap k)) => Group (Selector k a) where negateG = fmap negateG -instance (Eq a, Ord k, Group a, Align (MonoidalMap k)) => Additive (Selector k a) +instance (Eq a, Ord k, Group a, Align (MonoidalMap k)) => Commutative (Selector k a) main :: IO () main = do From f2296c8071073348e98a0a77b2bfa0c0f788db1c Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 1 Jul 2022 10:26:04 -0400 Subject: [PATCH 41/69] ci: Update dependencies --- dep/reflex-platform/default.nix | 7 +------ dep/reflex-platform/github.json | 5 +++-- dep/reflex-platform/thunk.nix | 9 +++++++++ release.nix | 12 ++++++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 dep/reflex-platform/thunk.nix diff --git a/dep/reflex-platform/default.nix b/dep/reflex-platform/default.nix index 7a047786..2b4d4ab1 100644 --- a/dep/reflex-platform/default.nix +++ b/dep/reflex-platform/default.nix @@ -1,7 +1,2 @@ # DO NOT HAND-EDIT THIS FILE -import ((import {}).fetchFromGitHub ( - let json = builtins.fromJSON (builtins.readFile ./github.json); - in { inherit (json) owner repo rev sha256; - private = json.private or false; - } -)) +import (import ./thunk.nix) \ No newline at end of file diff --git a/dep/reflex-platform/github.json b/dep/reflex-platform/github.json index 66cd618f..9f4cbaa2 100644 --- a/dep/reflex-platform/github.json +++ b/dep/reflex-platform/github.json @@ -2,6 +2,7 @@ "owner": "reflex-frp", "repo": "reflex-platform", "branch": "develop", - "rev": "f628398d076243a0851b27e625b37f65dff9b89b", - "sha256": "0sl0hf1glgyb1vmf2mhw4r9ipmcqk1y19d3wsic7dix2jwywzrh9" + "private": false, + "rev": "ac66356c8839d1dc16cc60887c2db5988a60e6c4", + "sha256": "0zk8pf72lid6cqq4mlr1mcwh6zd5lz9i83kw519aci6mfba1afvq" } diff --git a/dep/reflex-platform/thunk.nix b/dep/reflex-platform/thunk.nix new file mode 100644 index 00000000..bbf2dc18 --- /dev/null +++ b/dep/reflex-platform/thunk.nix @@ -0,0 +1,9 @@ +# DO NOT HAND-EDIT THIS FILE +let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: + if !fetchSubmodules && !private then builtins.fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; + } else (import {}).fetchFromGitHub { + inherit owner repo rev sha256 fetchSubmodules private; + }; + json = builtins.fromJSON (builtins.readFile ./github.json); +in fetch json \ No newline at end of file diff --git a/release.nix b/release.nix index 25b5b830..3d1b11c4 100644 --- a/release.nix +++ b/release.nix @@ -27,6 +27,18 @@ let inherit system; __useTemplateHaskell = variation == "reflex"; # TODO hack haskellOverlays = [ + (self: super: { + commutative-semigroups = self.callHackageDirect { + pkg = "commutative-semigroups"; + ver = "0.1.0.0"; + sha256 = "0xmv20n3iqjc64xi3c91bwqrg8x79sgipmflmk21zz4rj9jdkv8i"; + } {}; + patch = self.callHackageDirect { + pkg = "patch"; + ver = "0.0.7.0"; + sha256 = "0yr2hk3fpwjxi1z0n384k3aq9b3z00c02bbwqybcj3n20l4k17l6"; + } {}; + }) # Use this package's source for reflex (self: super: { _dep = super._dep // { From 8216e8f4a958cf4222227b73c4cacce86b1637a8 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Wed, 6 Jul 2022 14:51:28 -0400 Subject: [PATCH 42/69] Update changelog --- ChangeLog.md | 2 +- reflex.cabal | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index baac3b57..255247c0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,6 @@ # Revision history for reflex -## Unreleased +## 0.8.2.1 * Fix build for GHC 9.2 * Require patch >= 0.0.7.0 diff --git a/reflex.cabal b/reflex.cabal index 958a6136..f275e9e0 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -98,12 +98,12 @@ library time >= 1.4 && < 1.12, transformers >= 0.5.6.0 && < 0.6, unbounded-delays >= 0.1.0.0 && < 0.2, - witherable >= 0.3 && < 0.5 + witherable >= 0.4 && < 0.5 if flag(split-these) build-depends: these >= 1 && <1.3, semialign >=1 && <1.3, - monoidal-containers >= 0.6 && < 0.7 + monoidal-containers >= 0.6.2.0 && < 0.7 else build-depends: these >= 0.4 && <0.9, monoidal-containers == 0.4.0.0 From 70b938ea638bc70468ae8042d8638ce8d812bd14 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Wed, 6 Jul 2022 14:53:25 -0400 Subject: [PATCH 43/69] Require newer witherable --- ChangeLog.md | 4 ++++ reflex.cabal | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 255247c0..08e54456 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## 0.8.2.2 + +* Require witherable >= 0.4 and, hence, a newer monoidal-containers + ## 0.8.2.1 * Fix build for GHC 9.2 diff --git a/reflex.cabal b/reflex.cabal index f275e9e0..c94738d6 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.8.2.1 +Version: 0.8.2.2 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 56d6274353237dda526039df5b1bc9308427bcab Mon Sep 17 00:00:00 2001 From: Ryan Trinkle Date: Sun, 9 Oct 2022 15:13:44 -0400 Subject: [PATCH 44/69] Expose RequesterT internals --- ChangeLog.md | 4 + reflex.cabal | 1 + src/Reflex/Requester/Base.hs | 588 +------------------------ src/Reflex/Requester/Base/Internal.hs | 589 ++++++++++++++++++++++++++ 4 files changed, 595 insertions(+), 587 deletions(-) create mode 100644 src/Reflex/Requester/Base/Internal.hs diff --git a/ChangeLog.md b/ChangeLog.md index a137d711..ab8706e1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## Unreleased + +* Expose all Requester internals in Reflex.Requester.Base.Internal + ## 0.8.2.0 * Add `matchResponseMapWithRequests`, which it similar to `matchResponsesWithRequests` but allows processing of multiple responses at once. diff --git a/reflex.cabal b/reflex.cabal index d929f481..d4ef4f6d 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -143,6 +143,7 @@ library Reflex.Query.Base, Reflex.Query.Class, Reflex.Requester.Base, + Reflex.Requester.Base.Internal, Reflex.Requester.Class, Reflex.Spider, Reflex.Spider.Internal, diff --git a/src/Reflex/Requester/Base.hs b/src/Reflex/Requester/Base.hs index 6366b07b..b8a00b2c 100644 --- a/src/Reflex/Requester/Base.hs +++ b/src/Reflex/Requester/Base.hs @@ -1,24 +1,3 @@ --- | This module provides 'RequesterT', the standard implementation of --- 'Requester'. -{-# LANGUAGE AllowAmbiguousTypes #-} -{-# LANGUAGE ApplicativeDo #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE ExistentialQuantification #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE Rank2Types #-} -{-# LANGUAGE RecursiveDo #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE UndecidableInstances #-} -#ifdef USE_REFLEX_OPTIMIZER -{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} -#endif module Reflex.Requester.Base ( RequesterT (..) , runRequesterT @@ -39,569 +18,4 @@ module Reflex.Requester.Base , requesting' ) where -import Reflex.Class -import Reflex.Adjustable.Class -import Reflex.Dynamic -import Reflex.Host.Class -import Reflex.PerformEvent.Class -import Reflex.PostBuild.Class -import Reflex.Requester.Class -import Reflex.TriggerEvent.Class - -import Control.Applicative (liftA2) -import Control.Monad.Exception -import Control.Monad.Identity -import Control.Monad.Morph -import Control.Monad.Primitive -import Control.Monad.Reader -import Control.Monad.Ref -import Control.Monad.State.Strict -import Data.Bits -import Data.Coerce -import Data.Constraint -import Data.Dependent.Map (DMap) -import qualified Data.Dependent.Map as DMap -import Data.Dependent.Sum (DSum (..)) -import Data.Functor.Compose -import Data.Functor.Misc -import Data.IntMap.Strict (IntMap) -import qualified Data.IntMap.Strict as IntMap -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Monoid ((<>)) -import Data.Proxy -import qualified Data.Semigroup as S -import Data.Some (Some(Some)) -import Data.Type.Equality -import Data.Unique.Tag - -import GHC.Exts (Any) -import Unsafe.Coerce - ---TODO: Make this module type-safe - -newtype TagMap (f :: * -> *) = TagMap (IntMap Any) - -newtype RequesterData f = RequesterData (TagMap (Entry f)) - -emptyRequesterData :: RequesterData f -emptyRequesterData = RequesterData $ TagMap IntMap.empty - -data RequesterDataKey a where - RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a - RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead - RequesterDataKey_Multi2 :: GCompare k => {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a - RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a - -singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f -singletonRequesterData rdk v = case rdk of - RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v - RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v - RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Multi2Contents - { _multi2Contents_values = Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v - , _multi2Contents_dict = Dict - } - RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v - -mergeRequesterData :: RequesterData f -> RequesterData f -> RequesterData f -mergeRequesterData (RequesterData a) (RequesterData b) = RequesterData $ mergeTagMap a b - -mergeTagMap :: forall f. TagMap (Entry f) -> TagMap (Entry f) -> TagMap (Entry f) -mergeTagMap (TagMap m) (TagMap n) = - TagMap $ IntMap.unionWithKey (g' combiner) m n - where - combiner :: forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a - combiner k (Entry a) (Entry b) = Entry $ case myKeyType k of - MyTagType_Single -> a - MyTagType_Multi -> IntMap.unionWith mergeRequesterData a b - MyTagType_Multi2 -> case _multi2Contents_dict a of - Dict -> Multi2Contents - { _multi2Contents_values = Map.unionWith (IntMap.unionWith mergeRequesterData) (_multi2Contents_values a) (_multi2Contents_values b) - , _multi2Contents_dict = Dict - } - MyTagType_Multi3 -> IntMap.unionWith (IntMap.unionWith mergeRequesterData) a b - g' :: (forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a) -> Int -> Any -> Any -> Any - g' f rawKey a b = - let k = MyTag rawKey :: MyTag a - fromAny :: Any -> Entry f a - fromAny = unsafeCoerce - toAny :: Entry f a -> Any - toAny = unsafeCoerce - in toAny $ f k (fromAny a) (fromAny b) - -requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f] -requesterDataToList (RequesterData m) = do - k :=> Entry e <- tagMapToList m - case myKeyType k of - MyTagType_Single -> return $ RequesterDataKey_Single k :=> e - MyTagType_Multi -> do - (k', e') <- IntMap.toList e - k'' :=> e'' <- requesterDataToList e' - return $ RequesterDataKey_Multi k k' k'' :=> e'' - MyTagType_Multi2 -> case _multi2Contents_dict e of - Dict -> do - (k', e') <- Map.toList $ _multi2Contents_values e - (k'', e'') <- IntMap.toList e' - k''' :=> e''' <- requesterDataToList e'' - return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e''' - MyTagType_Multi3 -> do - (k', e') <- IntMap.toList e - (k'', e'') <- IntMap.toList e' - k''' :=> e''' <- requesterDataToList e'' - return $ RequesterDataKey_Multi3 k k' k'' k''' :=> e''' - -singletonTagMap :: forall f a. MyTag a -> f a -> TagMap f -singletonTagMap (MyTag k) v = TagMap $ IntMap.singleton k $ (unsafeCoerce :: f a -> Any) v - -tagMapToList :: forall f. TagMap f -> [DSum MyTag f] -tagMapToList (TagMap m) = f <$> IntMap.toList m - where f :: (Int, Any) -> DSum MyTag f - f (k, v) = MyTag k :=> (unsafeCoerce :: Any -> f a) v - -traverseTagMapWithKey :: forall t f g. Applicative t => (forall a. MyTag a -> f a -> t (g a)) -> TagMap f -> t (TagMap g) -traverseTagMapWithKey f (TagMap m) = TagMap <$> IntMap.traverseWithKey g m - where - g :: Int -> Any -> t Any - g k v = (unsafeCoerce :: g a -> Any) <$> f (MyTag k) ((unsafeCoerce :: Any -> f a) v) - --- | Runs in reverse to accommodate for the fact that we accumulate it in reverse -traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequesterData request -> m (RequesterData response) -traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWithKey go m --TODO: reverse this, since our tags are in reverse order - where go :: forall x. MyTag x -> Entry request x -> m (Entry response x) - go k (Entry request) = Entry <$> case myKeyType k of - MyTagType_Single -> f request - MyTagType_Multi -> traverse (traverseRequesterData f) request - MyTagType_Multi2 -> case request of - Multi2Contents { _multi2Contents_values = request', _multi2Contents_dict = Dict } -> do - v <- traverse (traverse (traverseRequesterData f)) request' - pure $ Multi2Contents - { _multi2Contents_values = v - , _multi2Contents_dict = Dict - } - MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request - --- | 'traverseRequesterData' with its arguments flipped -forRequesterData :: forall request response m. Applicative m => RequesterData request -> (forall a. request a -> m (response a)) -> m (RequesterData response) -forRequesterData r f = traverseRequesterData f r - -data MyTagType :: * -> * where - MyTagType_Single :: MyTagType (Single a) - MyTagType_Multi :: MyTagType Multi - MyTagType_Multi2 :: MyTagType (Multi2 k) - MyTagType_Multi3 :: MyTagType Multi3 - -myKeyType :: MyTag x -> MyTagType x -myKeyType (MyTag k) = case k .&. 0x3 of - 0x0 -> unsafeCoerce MyTagType_Single - 0x1 -> unsafeCoerce MyTagType_Multi - 0x2 -> unsafeCoerce MyTagType_Multi2 - 0x3 -> unsafeCoerce MyTagType_Multi3 - t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t - -data Single a -data Multi -data Multi2 (k :: * -> *) -data Multi3 - -class MyTagTypeOffset x where - myTagTypeOffset :: proxy x -> Int - -instance MyTagTypeOffset (Single a) where - myTagTypeOffset _ = 0x0 - -instance MyTagTypeOffset Multi where - myTagTypeOffset _ = 0x1 - -instance MyTagTypeOffset (Multi2 k) where - myTagTypeOffset _ = 0x2 - -instance MyTagTypeOffset Multi3 where - myTagTypeOffset _ = 0x3 - -type family EntryContents request a where - EntryContents request (Single a) = request a - EntryContents request Multi = IntMap (RequesterData request) - EntryContents request (Multi2 k) = Multi2Contents k request - EntryContents request Multi3 = IntMap (IntMap (RequesterData request)) - -data Multi2Contents k request = Multi2Contents - { _multi2Contents_dict :: {-# UNPACK #-} !(Dict (GCompare k)) -- This is a Dict instead of an existential context because we only want to use it in certain circumstances - , _multi2Contents_values :: {-# UNPACK #-} !(Map (Some k) (IntMap (RequesterData request))) - } - -newtype Entry request x = Entry { unEntry :: EntryContents request x } - -{-# INLINE singleEntry #-} -singleEntry :: f a -> Entry f (Single a) -singleEntry = Entry - -{-# INLINE multiEntry #-} -multiEntry :: IntMap (RequesterData f) -> Entry f Multi -multiEntry = Entry - -{-# INLINE unMultiEntry #-} -unMultiEntry :: Entry f Multi -> IntMap (RequesterData f) -unMultiEntry = unEntry - --- | We use a hack here to pretend we have x ~ request a; we don't want to use a GADT, because GADTs (even with zero-size existential contexts) can't be newtypes --- WARNING: This type should never be exposed. In particular, this is extremely unsound if a MyTag from one run of runRequesterT is ever compared against a MyTag from another -newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum) - -newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum) - -{-# INLINE castMyTagWrap #-} -castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x) -castMyTagWrap = coerce - -instance GEq MyTag where - (MyTag a) `geq` (MyTag b) = - if a == b - then Just $ unsafeCoerce Refl - else Nothing - -instance GCompare MyTag where - (MyTag a) `gcompare` (MyTag b) = - case a `compare` b of - LT -> GLT - EQ -> unsafeCoerce GEQ - GT -> GGT - -instance GEq (MyTagWrap f) where - (MyTagWrap a) `geq` (MyTagWrap b) = - if a == b - then Just $ unsafeCoerce Refl - else Nothing - -instance GCompare (MyTagWrap f) where - (MyTagWrap a) `gcompare` (MyTagWrap b) = - case a `compare` b of - LT -> GLT - EQ -> unsafeCoerce GEQ - GT -> GGT - -data RequesterState t (request :: * -> *) = RequesterState - { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom - , _requesterState_requests :: ![(Int, Event t Any)] - } - --- | A basic implementation of 'Requester'. -newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a } - deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException --- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2 -#if MIN_VERSION_base(4,9,1) - , MonadAsyncException -#endif - ) - -deriving instance MonadSample t m => MonadSample t (RequesterT t request response m) -deriving instance MonadHold t m => MonadHold t (RequesterT t request response m) -deriving instance PostBuild t m => PostBuild t (RequesterT t request response m) -deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m) - -instance PrimMonad m => PrimMonad (RequesterT t request response m) where - type PrimState (RequesterT t request response m) = PrimState m - primitive = lift . primitive - --- TODO: Monoid and Semigroup can likely be derived once StateT has them. -instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where - mempty = pure mempty - mappend = liftA2 mappend - -instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where - (<>) = liftA2 (S.<>) - - --- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever --- requests are made, and responses should be provided in the input 'Event'. --- The 'Tag' keys will be used to return the responses to the same place the --- requests were issued. -runRequesterT :: (Reflex t, Monad m) - => RequesterT t request response m a - -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse - -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild -runRequesterT (RequesterT a) responses = do - (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fanInt $ - coerceEvent responses - return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s) - --- | Map a function over the request and response of a 'RequesterT' -withRequesterT - :: (Reflex t, MonadFix m) - => (forall x. req x -> req' x) -- ^ The function to map over the request - -> (forall x. rsp' x -> rsp x) -- ^ The function to map over the response - -> RequesterT t req rsp m a -- ^ The internal 'RequesterT' whose input and output will be transformed - -> RequesterT t req' rsp' m a -- ^ The resulting 'RequesterT' -withRequesterT freq frsp child = do - rec let rsp = fmap (runIdentity . traverseRequesterData (Identity . frsp)) rsp' - (a, req) <- lift $ runRequesterT child rsp - rsp' <- fmap (flip selectInt 0 . fanInt . fmapCheap unMultiEntry) $ requesting' $ - fmapCheap (multiEntry . IntMap.singleton 0) $ fmap (runIdentity . traverseRequesterData (Identity . freq)) req - return a - -instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where - type Request (RequesterT t request response m) = request - type Response (RequesterT t request response m) = response - requesting = fmap coerceEvent . responseFromTag . castMyTagWrap <=< tagRequest . (coerceEvent :: Event t (request a) -> Event t (Entry request (Single a))) - requesting_ = void . tagRequest . fmapCheap singleEntry - -{-# INLINE tagRequest #-} -tagRequest :: forall m x t request response. (Monad m, MyTagTypeOffset x) => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x)) -tagRequest req = do - old <- RequesterT get - let n = _requesterState_nextMyTag old .|. myTagTypeOffset (Proxy :: Proxy x) - t = MyTagWrap n - RequesterT $ put $ RequesterState - { _requesterState_nextMyTag = _requesterState_nextMyTag old - 0x4 - , _requesterState_requests = (n, (unsafeCoerce :: Event t (Entry request x) -> Event t Any) req) : _requesterState_requests old - } - return t - -{-# INLINE responseFromTag #-} -responseFromTag :: forall m t request response x. Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x)) -responseFromTag (MyTagWrap t) = do - responses :: EventSelectorInt t Any <- RequesterT ask - return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t - -instance MonadTrans (RequesterT t request response) where - lift = RequesterT . lift . lift - -instance MFunctor (RequesterT t request response) where - hoist f = RequesterT . hoist (hoist f) . unRequesterT - -instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where - type Performable (RequesterT t request response m) = Performable m - performEvent_ = lift . performEvent_ - performEvent = lift . performEvent - -instance MonadRef m => MonadRef (RequesterT t request response m) where - type Ref (RequesterT t request response m) = Ref m - newRef = lift . newRef - readRef = lift . readRef - writeRef r = lift . writeRef r - -instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where - newEventWithTrigger = lift . newEventWithTrigger - newFanEventWithTrigger f = lift $ newFanEventWithTrigger f - -instance MonadReader r m => MonadReader r (RequesterT t request response m) where - ask = lift ask - local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a - reader = lift . reader - -instance (Reflex t, Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (RequesterT t request response m) where - runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm' - traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental - {-# INLINABLE traverseDMapWithKeyWithAdjust #-} - traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental - traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove - -requesting' :: (MyTagTypeOffset x, Monad m) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x)) -requesting' = responseFromTag . castMyTagWrap <=< tagRequest - -{-# INLINABLE runWithReplaceRequesterTWith #-} -runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m - , MonadFix m - ) - => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b')) - -> RequesterT t request response m a - -> Event t (RequesterT t request response m b) - -> RequesterT t request response m (a, Event t b) -runWithReplaceRequesterTWith f a0 a' = do - rec na' <- numberOccurrencesFrom 1 a' - responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requests --TODO: Investigate whether we can really get rid of the prompt stuff here - let responses' = fanInt responses - ((result0, requests0), v') <- f (runRequesterT a0 (selectInt responses' 0)) $ fmapCheap (\(n, a) -> fmap ((,) n) $ runRequesterT a $ selectInt responses' n) na' - requests <- holdDyn (fmapCheap (IntMap.singleton 0) requests0) $ fmapCheap (\(n, (_, reqs)) -> fmapCheap (IntMap.singleton n) reqs) v' - return (result0, fmapCheap (fst . snd) v') - -{-# INLINE traverseIntMapWithKeyWithAdjustRequesterTWith #-} -traverseIntMapWithKeyWithAdjustRequesterTWith :: forall t request response m v v' p. - ( Reflex t - , MonadHold t m - , PatchTarget (p (Event t (IntMap (RequesterData request)))) ~ IntMap (Event t (IntMap (RequesterData request))) - , Patch (p (Event t (IntMap (RequesterData request)))) - , Functor p - , MonadFix m - ) - => ( (IntMap.Key -> (IntMap.Key, v) -> m (Event t (IntMap (RequesterData request)), v')) - -> IntMap (IntMap.Key, v) - -> Event t (p (IntMap.Key, v)) - -> RequesterT t request response m (IntMap (Event t (IntMap (RequesterData request)), v'), Event t (p (Event t (IntMap (RequesterData request)), v'))) - ) - -> (p (Event t (IntMap (RequesterData request))) -> IntMap (Event t (IntMap (RequesterData request)))) - -> (Incremental t (p (Event t (IntMap (RequesterData request)))) -> Event t (IntMap (IntMap (RequesterData request)))) - -> (IntMap.Key -> v -> RequesterT t request response m v') - -> IntMap v - -> Event t (p v) - -> RequesterT t request response m (IntMap v', Event t (p v')) -traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do - rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here - let responses :: EventSelectorInt t (IntMap (RequesterData response)) - responses = fanInt $ fmapCheap unpack response - unpack :: Entry response Multi3 -> IntMap (IntMap (RequesterData response)) - unpack = unEntry - pack :: IntMap (IntMap (RequesterData request)) -> Entry request Multi3 - pack = Entry - f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v') - f' k (n, v) = do - (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing mapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time? - return (fmapCheap (IntMap.singleton n) myRequests, result) - ndm' <- numberOccurrencesFrom 1 dm' - (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to Adjustable - let result0 = fmap snd children0 - result' = fforCheap children' $ fmap snd - requests0 :: IntMap (Event t (IntMap (RequesterData request))) - requests0 = fmap fst children0 - requests' :: Event t (p (Event t (IntMap (RequesterData request)))) - requests' = fforCheap children' $ fmap fst - promptRequests :: Event t (IntMap (IntMap (RequesterData request))) - promptRequests = coincidence $ fmapCheap (mergeInt . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence' - requests <- holdIncremental requests0 requests' - return (result0, result') - -{-# INLINE traverseDMapWithKeyWithAdjustRequesterTWith #-} -traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'. - ( GCompare k - , Reflex t - , MonadHold t m - , PatchTarget (p' (Some k) (Event t (IntMap (RequesterData request)))) ~ Map (Some k) (Event t (IntMap (RequesterData request))) - , Patch (p' (Some k) (Event t (IntMap (RequesterData request)))) - , MonadFix m - ) - => (forall k' v1 v2. GCompare k' - => (forall a. k' a -> v1 a -> m (v2 a)) - -> DMap k' v1 - -> Event t (p k' v1) - -> RequesterT t request response m (DMap k' v2, Event t (p k' v2)) - ) - -> (forall v1 v2. (forall a. v1 a -> v2 a) -> p k v1 -> p k v2) - -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2) - -> (forall v2. p' (Some k) v2 -> Map (Some k) v2) - -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a)) - -> (forall a. k a -> v a -> RequesterT t request response m (v' a)) - -> DMap k v - -> Event t (p k v) - -> RequesterT t request response m (DMap k v', Event t (p k v')) -traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do - rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here - let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response))) - responses = fanMap $ fmapCheap unpack response - unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response)) - unpack = _multi2Contents_values . unEntry - pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k) - pack m = Entry $ Multi2Contents { _multi2Contents_values = m, _multi2Contents_dict = Dict } - f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a) - f' k (Compose (n, v)) = do - (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k)) - return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result) - ndm' <- numberOccurrencesFrom 1 dm' - (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm' - let result0 = DMap.map (snd . getCompose) children0 - result' = fforCheap children' $ mapPatch $ snd . getCompose - requests0 :: Map (Some k) (Event t (IntMap (RequesterData request))) - requests0 = weakenDMapWith (fst . getCompose) children0 - requests' :: Event t (p' (Some k) (Event t (IntMap (RequesterData request)))) - requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose - promptRequests :: Event t (Map (Some k) (IntMap (RequesterData request))) - promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence' - requests <- holdIncremental requests0 requests' - return (result0, result') - -data Decoder rawResponse response = - forall a. Decoder (RequesterDataKey a) (rawResponse -> response a) - -matchResponsesWithRequests - :: forall t rawRequest rawResponse request response m. - ( MonadFix m - , MonadHold t m - , Reflex t - ) - => (forall a. request a -> (rawRequest, rawResponse -> response a)) - -- ^ Given a request (from 'Requester'), produces the wire format of the - -- request and a function used to process the associated response - -> Event t (RequesterData request) - -- ^ The outgoing requests - -> Event t (Int, rawResponse) - -- ^ The incoming responses, tagged by an identifying key - -> m ( Event t (Map Int rawRequest) - , Event t (RequesterData response) - ) - -- ^ A map of outgoing wire-format requests and an event of responses keyed - -- by the 'RequesterData' key of the associated outgoing request -matchResponsesWithRequests f send recv = matchResponseMapWithRequests f send $ uncurry Map.singleton <$> recv - --- | Matches incoming responses with previously-sent requests --- and uses the provided request "decoder" function to process --- incoming responses. -matchResponseMapWithRequests - :: forall t rawRequest rawResponse request response m. - ( MonadFix m - , MonadHold t m - , Reflex t - ) - => (forall a. request a -> (rawRequest, rawResponse -> response a)) - -- ^ Given a request (from 'Requester'), produces the wire format of the - -- request and a function used to process the associated response - -> Event t (RequesterData request) - -- ^ The outgoing requests - -> Event t (Map Int rawResponse) - -- ^ A map of incoming responses, tagged by an identifying key - -> m ( Event t (Map Int rawRequest) - , Event t (RequesterData response) - ) - -- ^ A map of outgoing wire-format requests and an event of responses keyed - -- by the 'RequesterData' key of the associated outgoing request -matchResponseMapWithRequests f send recv = do - rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing - waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <- - holdIncremental mempty $ leftmost - [ fmap (\(_, outstanding, _) -> outstanding) outgoing - , snd <$> incoming - ] - let outgoing = processOutgoing nextId send - incoming = processIncoming waitingFor recv - return (fmap (\(_, _, rawReqs) -> rawReqs) outgoing, fst <$> incoming) - where - -- Tags each outgoing request with an identifying integer key - -- and returns the next available key, a map of response decoders - -- for requests for which there are outstanding responses, and the - -- raw requests to be sent out. - processOutgoing - :: Behavior t Int - -- The next available key - -> Event t (RequesterData request) - -- The outgoing request - -> Event t ( Int - , PatchMap Int (Decoder rawResponse response) - , Map Int rawRequest ) - -- The new next-available-key, a map of requests expecting responses, and the tagged raw requests - processOutgoing nextId out = flip pushAlways out $ \dm -> do - oldNextId <- sample nextId - let (result, newNextId) = flip runState oldNextId $ forM (requesterDataToList dm) $ \(k :=> v) -> do - n <- get - put $ succ n - let (rawReq, rspF) = f v - return (n, rawReq, Decoder k rspF) - patchWaitingFor = PatchMap $ Map.fromList $ - (\(n, _, dec) -> (n, Just dec)) <$> result - toSend = Map.fromList $ (\(n, rawReq, _) -> (n, rawReq)) <$> result - return (newNextId, patchWaitingFor, toSend) - -- Looks up the each incoming raw response in a map of response - -- decoders and returns the decoded response and a patch that can - -- be used to clear the ID of the consumed response out of the queue - -- of expected responses. - processIncoming - :: Incremental t (PatchMap Int (Decoder rawResponse response)) - -- A map of outstanding expected responses - -> Event t (Map Int rawResponse) - -- A incoming response paired with its identifying key - -> Event t (RequesterData response, PatchMap Int v) - -- The decoded response and a patch that clears the outstanding responses queue - processIncoming waitingFor inc = flip push inc $ \rspMap -> do - wf <- sample $ currentIncremental waitingFor - let match rawRsp (Decoder k rspF) = - let rsp = rspF rawRsp - in singletonRequesterData k rsp - matches = Map.intersectionWith match rspMap wf - pure $ if Map.null matches then Nothing else Just - (Map.foldl' mergeRequesterData emptyRequesterData matches, PatchMap $ Nothing <$ matches) +import Reflex.Requester.Base.Internal diff --git a/src/Reflex/Requester/Base/Internal.hs b/src/Reflex/Requester/Base/Internal.hs new file mode 100644 index 00000000..4ec9056c --- /dev/null +++ b/src/Reflex/Requester/Base/Internal.hs @@ -0,0 +1,589 @@ +-- | This module provides 'RequesterT', the standard implementation of +-- 'Requester'. +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE ApplicativeDo #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE EmptyDataDecls #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE Rank2Types #-} +{-# LANGUAGE RecursiveDo #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} +#ifdef USE_REFLEX_OPTIMIZER +{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} +#endif +module Reflex.Requester.Base.Internal where + +import Reflex.Class +import Reflex.Adjustable.Class +import Reflex.Dynamic +import Reflex.Host.Class +import Reflex.PerformEvent.Class +import Reflex.PostBuild.Class +import Reflex.Requester.Class +import Reflex.TriggerEvent.Class + +import Control.Applicative (liftA2) +import Control.Monad.Exception +import Control.Monad.Identity +import Control.Monad.Morph +import Control.Monad.Primitive +import Control.Monad.Reader +import Control.Monad.Ref +import Control.Monad.State.Strict +import Data.Bits +import Data.Coerce +import Data.Constraint +import Data.Dependent.Map (DMap) +import qualified Data.Dependent.Map as DMap +import Data.Dependent.Sum (DSum (..)) +import Data.Functor.Compose +import Data.Functor.Misc +import Data.IntMap.Strict (IntMap) +import qualified Data.IntMap.Strict as IntMap +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Monoid ((<>)) +import Data.Proxy +import qualified Data.Semigroup as S +import Data.Some (Some(Some)) +import Data.Type.Equality +import Data.Unique.Tag + +import GHC.Exts (Any) +import Unsafe.Coerce + +--TODO: Make this module type-safe + +newtype TagMap (f :: * -> *) = TagMap (IntMap Any) + +newtype RequesterData f = RequesterData (TagMap (Entry f)) + +emptyRequesterData :: RequesterData f +emptyRequesterData = RequesterData $ TagMap IntMap.empty + +data RequesterDataKey a where + RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a + RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead + RequesterDataKey_Multi2 :: GCompare k => {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a + RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a + +singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f +singletonRequesterData rdk v = case rdk of + RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v + RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v + RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Multi2Contents + { _multi2Contents_values = Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v + , _multi2Contents_dict = Dict + } + RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v + +mergeRequesterData :: RequesterData f -> RequesterData f -> RequesterData f +mergeRequesterData (RequesterData a) (RequesterData b) = RequesterData $ mergeTagMap a b + +mergeTagMap :: forall f. TagMap (Entry f) -> TagMap (Entry f) -> TagMap (Entry f) +mergeTagMap (TagMap m) (TagMap n) = + TagMap $ IntMap.unionWithKey (g' combiner) m n + where + combiner :: forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a + combiner k (Entry a) (Entry b) = Entry $ case myKeyType k of + MyTagType_Single -> a + MyTagType_Multi -> IntMap.unionWith mergeRequesterData a b + MyTagType_Multi2 -> case _multi2Contents_dict a of + Dict -> Multi2Contents + { _multi2Contents_values = Map.unionWith (IntMap.unionWith mergeRequesterData) (_multi2Contents_values a) (_multi2Contents_values b) + , _multi2Contents_dict = Dict + } + MyTagType_Multi3 -> IntMap.unionWith (IntMap.unionWith mergeRequesterData) a b + g' :: (forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a) -> Int -> Any -> Any -> Any + g' f rawKey a b = + let k = MyTag rawKey :: MyTag a + fromAny :: Any -> Entry f a + fromAny = unsafeCoerce + toAny :: Entry f a -> Any + toAny = unsafeCoerce + in toAny $ f k (fromAny a) (fromAny b) + +requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f] +requesterDataToList (RequesterData m) = do + k :=> Entry e <- tagMapToList m + case myKeyType k of + MyTagType_Single -> return $ RequesterDataKey_Single k :=> e + MyTagType_Multi -> do + (k', e') <- IntMap.toList e + k'' :=> e'' <- requesterDataToList e' + return $ RequesterDataKey_Multi k k' k'' :=> e'' + MyTagType_Multi2 -> case _multi2Contents_dict e of + Dict -> do + (k', e') <- Map.toList $ _multi2Contents_values e + (k'', e'') <- IntMap.toList e' + k''' :=> e''' <- requesterDataToList e'' + return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e''' + MyTagType_Multi3 -> do + (k', e') <- IntMap.toList e + (k'', e'') <- IntMap.toList e' + k''' :=> e''' <- requesterDataToList e'' + return $ RequesterDataKey_Multi3 k k' k'' k''' :=> e''' + +singletonTagMap :: forall f a. MyTag a -> f a -> TagMap f +singletonTagMap (MyTag k) v = TagMap $ IntMap.singleton k $ (unsafeCoerce :: f a -> Any) v + +tagMapToList :: forall f. TagMap f -> [DSum MyTag f] +tagMapToList (TagMap m) = f <$> IntMap.toList m + where f :: (Int, Any) -> DSum MyTag f + f (k, v) = MyTag k :=> (unsafeCoerce :: Any -> f a) v + +traverseTagMapWithKey :: forall t f g. Applicative t => (forall a. MyTag a -> f a -> t (g a)) -> TagMap f -> t (TagMap g) +traverseTagMapWithKey f (TagMap m) = TagMap <$> IntMap.traverseWithKey g m + where + g :: Int -> Any -> t Any + g k v = (unsafeCoerce :: g a -> Any) <$> f (MyTag k) ((unsafeCoerce :: Any -> f a) v) + +-- | Runs in reverse to accommodate for the fact that we accumulate it in reverse +traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequesterData request -> m (RequesterData response) +traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWithKey go m --TODO: reverse this, since our tags are in reverse order + where go :: forall x. MyTag x -> Entry request x -> m (Entry response x) + go k (Entry request) = Entry <$> case myKeyType k of + MyTagType_Single -> f request + MyTagType_Multi -> traverse (traverseRequesterData f) request + MyTagType_Multi2 -> case request of + Multi2Contents { _multi2Contents_values = request', _multi2Contents_dict = Dict } -> do + v <- traverse (traverse (traverseRequesterData f)) request' + pure $ Multi2Contents + { _multi2Contents_values = v + , _multi2Contents_dict = Dict + } + MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request + +-- | 'traverseRequesterData' with its arguments flipped +forRequesterData :: forall request response m. Applicative m => RequesterData request -> (forall a. request a -> m (response a)) -> m (RequesterData response) +forRequesterData r f = traverseRequesterData f r + +data MyTagType :: * -> * where + MyTagType_Single :: MyTagType (Single a) + MyTagType_Multi :: MyTagType Multi + MyTagType_Multi2 :: MyTagType (Multi2 k) + MyTagType_Multi3 :: MyTagType Multi3 + +myKeyType :: MyTag x -> MyTagType x +myKeyType (MyTag k) = case k .&. 0x3 of + 0x0 -> unsafeCoerce MyTagType_Single + 0x1 -> unsafeCoerce MyTagType_Multi + 0x2 -> unsafeCoerce MyTagType_Multi2 + 0x3 -> unsafeCoerce MyTagType_Multi3 + t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t + +data Single a +data Multi +data Multi2 (k :: * -> *) +data Multi3 + +class MyTagTypeOffset x where + myTagTypeOffset :: proxy x -> Int + +instance MyTagTypeOffset (Single a) where + myTagTypeOffset _ = 0x0 + +instance MyTagTypeOffset Multi where + myTagTypeOffset _ = 0x1 + +instance MyTagTypeOffset (Multi2 k) where + myTagTypeOffset _ = 0x2 + +instance MyTagTypeOffset Multi3 where + myTagTypeOffset _ = 0x3 + +type family EntryContents request a where + EntryContents request (Single a) = request a + EntryContents request Multi = IntMap (RequesterData request) + EntryContents request (Multi2 k) = Multi2Contents k request + EntryContents request Multi3 = IntMap (IntMap (RequesterData request)) + +data Multi2Contents k request = Multi2Contents + { _multi2Contents_dict :: {-# UNPACK #-} !(Dict (GCompare k)) -- This is a Dict instead of an existential context because we only want to use it in certain circumstances + , _multi2Contents_values :: {-# UNPACK #-} !(Map (Some k) (IntMap (RequesterData request))) + } + +newtype Entry request x = Entry { unEntry :: EntryContents request x } + +{-# INLINE singleEntry #-} +singleEntry :: f a -> Entry f (Single a) +singleEntry = Entry + +{-# INLINE multiEntry #-} +multiEntry :: IntMap (RequesterData f) -> Entry f Multi +multiEntry = Entry + +{-# INLINE unMultiEntry #-} +unMultiEntry :: Entry f Multi -> IntMap (RequesterData f) +unMultiEntry = unEntry + +-- | We use a hack here to pretend we have x ~ request a; we don't want to use a GADT, because GADTs (even with zero-size existential contexts) can't be newtypes +-- WARNING: This type should never be exposed. In particular, this is extremely unsound if a MyTag from one run of runRequesterT is ever compared against a MyTag from another +newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum) + +newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum) + +{-# INLINE castMyTagWrap #-} +castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x) +castMyTagWrap = coerce + +instance GEq MyTag where + (MyTag a) `geq` (MyTag b) = + if a == b + then Just $ unsafeCoerce Refl + else Nothing + +instance GCompare MyTag where + (MyTag a) `gcompare` (MyTag b) = + case a `compare` b of + LT -> GLT + EQ -> unsafeCoerce GEQ + GT -> GGT + +instance GEq (MyTagWrap f) where + (MyTagWrap a) `geq` (MyTagWrap b) = + if a == b + then Just $ unsafeCoerce Refl + else Nothing + +instance GCompare (MyTagWrap f) where + (MyTagWrap a) `gcompare` (MyTagWrap b) = + case a `compare` b of + LT -> GLT + EQ -> unsafeCoerce GEQ + GT -> GGT + +data RequesterState t (request :: * -> *) = RequesterState + { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom + , _requesterState_requests :: ![(Int, Event t Any)] + } + +-- | A basic implementation of 'Requester'. +newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a } + deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException +-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2 +#if MIN_VERSION_base(4,9,1) + , MonadAsyncException +#endif + ) + +deriving instance MonadSample t m => MonadSample t (RequesterT t request response m) +deriving instance MonadHold t m => MonadHold t (RequesterT t request response m) +deriving instance PostBuild t m => PostBuild t (RequesterT t request response m) +deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m) + +instance PrimMonad m => PrimMonad (RequesterT t request response m) where + type PrimState (RequesterT t request response m) = PrimState m + primitive = lift . primitive + +-- TODO: Monoid and Semigroup can likely be derived once StateT has them. +instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where + mempty = pure mempty + mappend = liftA2 mappend + +instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where + (<>) = liftA2 (S.<>) + + +-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever +-- requests are made, and responses should be provided in the input 'Event'. +-- The 'Tag' keys will be used to return the responses to the same place the +-- requests were issued. +runRequesterT :: (Reflex t, Monad m) + => RequesterT t request response m a + -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse + -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild +runRequesterT (RequesterT a) responses = do + (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fanInt $ + coerceEvent responses + return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s) + +-- | Map a function over the request and response of a 'RequesterT' +withRequesterT + :: (Reflex t, MonadFix m) + => (forall x. req x -> req' x) -- ^ The function to map over the request + -> (forall x. rsp' x -> rsp x) -- ^ The function to map over the response + -> RequesterT t req rsp m a -- ^ The internal 'RequesterT' whose input and output will be transformed + -> RequesterT t req' rsp' m a -- ^ The resulting 'RequesterT' +withRequesterT freq frsp child = do + rec let rsp = fmap (runIdentity . traverseRequesterData (Identity . frsp)) rsp' + (a, req) <- lift $ runRequesterT child rsp + rsp' <- fmap (flip selectInt 0 . fanInt . fmapCheap unMultiEntry) $ requesting' $ + fmapCheap (multiEntry . IntMap.singleton 0) $ fmap (runIdentity . traverseRequesterData (Identity . freq)) req + return a + +instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where + type Request (RequesterT t request response m) = request + type Response (RequesterT t request response m) = response + requesting = fmap coerceEvent . responseFromTag . castMyTagWrap <=< tagRequest . (coerceEvent :: Event t (request a) -> Event t (Entry request (Single a))) + requesting_ = void . tagRequest . fmapCheap singleEntry + +{-# INLINE tagRequest #-} +tagRequest :: forall m x t request response. (Monad m, MyTagTypeOffset x) => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x)) +tagRequest req = do + old <- RequesterT get + let n = _requesterState_nextMyTag old .|. myTagTypeOffset (Proxy :: Proxy x) + t = MyTagWrap n + RequesterT $ put $ RequesterState + { _requesterState_nextMyTag = _requesterState_nextMyTag old - 0x4 + , _requesterState_requests = (n, (unsafeCoerce :: Event t (Entry request x) -> Event t Any) req) : _requesterState_requests old + } + return t + +{-# INLINE responseFromTag #-} +responseFromTag :: forall m t request response x. Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x)) +responseFromTag (MyTagWrap t) = do + responses :: EventSelectorInt t Any <- RequesterT ask + return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t + +instance MonadTrans (RequesterT t request response) where + lift = RequesterT . lift . lift + +instance MFunctor (RequesterT t request response) where + hoist f = RequesterT . hoist (hoist f) . unRequesterT + +instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where + type Performable (RequesterT t request response m) = Performable m + performEvent_ = lift . performEvent_ + performEvent = lift . performEvent + +instance MonadRef m => MonadRef (RequesterT t request response m) where + type Ref (RequesterT t request response m) = Ref m + newRef = lift . newRef + readRef = lift . readRef + writeRef r = lift . writeRef r + +instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where + newEventWithTrigger = lift . newEventWithTrigger + newFanEventWithTrigger f = lift $ newFanEventWithTrigger f + +instance MonadReader r m => MonadReader r (RequesterT t request response m) where + ask = lift ask + local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a + reader = lift . reader + +instance (Reflex t, Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (RequesterT t request response m) where + runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm' + traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental + {-# INLINABLE traverseDMapWithKeyWithAdjust #-} + traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental + traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove + +requesting' :: (MyTagTypeOffset x, Monad m) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x)) +requesting' = responseFromTag . castMyTagWrap <=< tagRequest + +{-# INLINABLE runWithReplaceRequesterTWith #-} +runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m + , MonadFix m + ) + => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b')) + -> RequesterT t request response m a + -> Event t (RequesterT t request response m b) + -> RequesterT t request response m (a, Event t b) +runWithReplaceRequesterTWith f a0 a' = do + rec na' <- numberOccurrencesFrom 1 a' + responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requests --TODO: Investigate whether we can really get rid of the prompt stuff here + let responses' = fanInt responses + ((result0, requests0), v') <- f (runRequesterT a0 (selectInt responses' 0)) $ fmapCheap (\(n, a) -> fmap ((,) n) $ runRequesterT a $ selectInt responses' n) na' + requests <- holdDyn (fmapCheap (IntMap.singleton 0) requests0) $ fmapCheap (\(n, (_, reqs)) -> fmapCheap (IntMap.singleton n) reqs) v' + return (result0, fmapCheap (fst . snd) v') + +{-# INLINE traverseIntMapWithKeyWithAdjustRequesterTWith #-} +traverseIntMapWithKeyWithAdjustRequesterTWith :: forall t request response m v v' p. + ( Reflex t + , MonadHold t m + , PatchTarget (p (Event t (IntMap (RequesterData request)))) ~ IntMap (Event t (IntMap (RequesterData request))) + , Patch (p (Event t (IntMap (RequesterData request)))) + , Functor p + , MonadFix m + ) + => ( (IntMap.Key -> (IntMap.Key, v) -> m (Event t (IntMap (RequesterData request)), v')) + -> IntMap (IntMap.Key, v) + -> Event t (p (IntMap.Key, v)) + -> RequesterT t request response m (IntMap (Event t (IntMap (RequesterData request)), v'), Event t (p (Event t (IntMap (RequesterData request)), v'))) + ) + -> (p (Event t (IntMap (RequesterData request))) -> IntMap (Event t (IntMap (RequesterData request)))) + -> (Incremental t (p (Event t (IntMap (RequesterData request)))) -> Event t (IntMap (IntMap (RequesterData request)))) + -> (IntMap.Key -> v -> RequesterT t request response m v') + -> IntMap v + -> Event t (p v) + -> RequesterT t request response m (IntMap v', Event t (p v')) +traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do + rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here + let responses :: EventSelectorInt t (IntMap (RequesterData response)) + responses = fanInt $ fmapCheap unpack response + unpack :: Entry response Multi3 -> IntMap (IntMap (RequesterData response)) + unpack = unEntry + pack :: IntMap (IntMap (RequesterData request)) -> Entry request Multi3 + pack = Entry + f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v') + f' k (n, v) = do + (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing mapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time? + return (fmapCheap (IntMap.singleton n) myRequests, result) + ndm' <- numberOccurrencesFrom 1 dm' + (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to Adjustable + let result0 = fmap snd children0 + result' = fforCheap children' $ fmap snd + requests0 :: IntMap (Event t (IntMap (RequesterData request))) + requests0 = fmap fst children0 + requests' :: Event t (p (Event t (IntMap (RequesterData request)))) + requests' = fforCheap children' $ fmap fst + promptRequests :: Event t (IntMap (IntMap (RequesterData request))) + promptRequests = coincidence $ fmapCheap (mergeInt . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence' + requests <- holdIncremental requests0 requests' + return (result0, result') + +{-# INLINE traverseDMapWithKeyWithAdjustRequesterTWith #-} +traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'. + ( GCompare k + , Reflex t + , MonadHold t m + , PatchTarget (p' (Some k) (Event t (IntMap (RequesterData request)))) ~ Map (Some k) (Event t (IntMap (RequesterData request))) + , Patch (p' (Some k) (Event t (IntMap (RequesterData request)))) + , MonadFix m + ) + => (forall k' v1 v2. GCompare k' + => (forall a. k' a -> v1 a -> m (v2 a)) + -> DMap k' v1 + -> Event t (p k' v1) + -> RequesterT t request response m (DMap k' v2, Event t (p k' v2)) + ) + -> (forall v1 v2. (forall a. v1 a -> v2 a) -> p k v1 -> p k v2) + -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2) + -> (forall v2. p' (Some k) v2 -> Map (Some k) v2) + -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a)) + -> (forall a. k a -> v a -> RequesterT t request response m (v' a)) + -> DMap k v + -> Event t (p k v) + -> RequesterT t request response m (DMap k v', Event t (p k v')) +traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do + rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here + let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response))) + responses = fanMap $ fmapCheap unpack response + unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response)) + unpack = _multi2Contents_values . unEntry + pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k) + pack m = Entry $ Multi2Contents { _multi2Contents_values = m, _multi2Contents_dict = Dict } + f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a) + f' k (Compose (n, v)) = do + (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k)) + return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result) + ndm' <- numberOccurrencesFrom 1 dm' + (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm' + let result0 = DMap.map (snd . getCompose) children0 + result' = fforCheap children' $ mapPatch $ snd . getCompose + requests0 :: Map (Some k) (Event t (IntMap (RequesterData request))) + requests0 = weakenDMapWith (fst . getCompose) children0 + requests' :: Event t (p' (Some k) (Event t (IntMap (RequesterData request)))) + requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose + promptRequests :: Event t (Map (Some k) (IntMap (RequesterData request))) + promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence' + requests <- holdIncremental requests0 requests' + return (result0, result') + +data Decoder rawResponse response = + forall a. Decoder (RequesterDataKey a) (rawResponse -> response a) + +matchResponsesWithRequests + :: forall t rawRequest rawResponse request response m. + ( MonadFix m + , MonadHold t m + , Reflex t + ) + => (forall a. request a -> (rawRequest, rawResponse -> response a)) + -- ^ Given a request (from 'Requester'), produces the wire format of the + -- request and a function used to process the associated response + -> Event t (RequesterData request) + -- ^ The outgoing requests + -> Event t (Int, rawResponse) + -- ^ The incoming responses, tagged by an identifying key + -> m ( Event t (Map Int rawRequest) + , Event t (RequesterData response) + ) + -- ^ A map of outgoing wire-format requests and an event of responses keyed + -- by the 'RequesterData' key of the associated outgoing request +matchResponsesWithRequests f send recv = matchResponseMapWithRequests f send $ uncurry Map.singleton <$> recv + +-- | Matches incoming responses with previously-sent requests +-- and uses the provided request "decoder" function to process +-- incoming responses. +matchResponseMapWithRequests + :: forall t rawRequest rawResponse request response m. + ( MonadFix m + , MonadHold t m + , Reflex t + ) + => (forall a. request a -> (rawRequest, rawResponse -> response a)) + -- ^ Given a request (from 'Requester'), produces the wire format of the + -- request and a function used to process the associated response + -> Event t (RequesterData request) + -- ^ The outgoing requests + -> Event t (Map Int rawResponse) + -- ^ A map of incoming responses, tagged by an identifying key + -> m ( Event t (Map Int rawRequest) + , Event t (RequesterData response) + ) + -- ^ A map of outgoing wire-format requests and an event of responses keyed + -- by the 'RequesterData' key of the associated outgoing request +matchResponseMapWithRequests f send recv = do + rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing + waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <- + holdIncremental mempty $ leftmost + [ fmap (\(_, outstanding, _) -> outstanding) outgoing + , snd <$> incoming + ] + let outgoing = processOutgoing nextId send + incoming = processIncoming waitingFor recv + return (fmap (\(_, _, rawReqs) -> rawReqs) outgoing, fst <$> incoming) + where + -- Tags each outgoing request with an identifying integer key + -- and returns the next available key, a map of response decoders + -- for requests for which there are outstanding responses, and the + -- raw requests to be sent out. + processOutgoing + :: Behavior t Int + -- The next available key + -> Event t (RequesterData request) + -- The outgoing request + -> Event t ( Int + , PatchMap Int (Decoder rawResponse response) + , Map Int rawRequest ) + -- The new next-available-key, a map of requests expecting responses, and the tagged raw requests + processOutgoing nextId out = flip pushAlways out $ \dm -> do + oldNextId <- sample nextId + let (result, newNextId) = flip runState oldNextId $ forM (requesterDataToList dm) $ \(k :=> v) -> do + n <- get + put $ succ n + let (rawReq, rspF) = f v + return (n, rawReq, Decoder k rspF) + patchWaitingFor = PatchMap $ Map.fromList $ + (\(n, _, dec) -> (n, Just dec)) <$> result + toSend = Map.fromList $ (\(n, rawReq, _) -> (n, rawReq)) <$> result + return (newNextId, patchWaitingFor, toSend) + -- Looks up the each incoming raw response in a map of response + -- decoders and returns the decoded response and a patch that can + -- be used to clear the ID of the consumed response out of the queue + -- of expected responses. + processIncoming + :: Incremental t (PatchMap Int (Decoder rawResponse response)) + -- A map of outstanding expected responses + -> Event t (Map Int rawResponse) + -- A incoming response paired with its identifying key + -> Event t (RequesterData response, PatchMap Int v) + -- The decoded response and a patch that clears the outstanding responses queue + processIncoming waitingFor inc = flip push inc $ \rspMap -> do + wf <- sample $ currentIncremental waitingFor + let match rawRsp (Decoder k rspF) = + let rsp = rspF rawRsp + in singletonRequesterData k rsp + matches = Map.intersectionWith match rspMap wf + pure $ if Map.null matches then Nothing else Just + (Map.foldl' mergeRequesterData emptyRequesterData matches, PatchMap $ Nothing <$ matches) From c85213407452b718242032ac02208aea2966e005 Mon Sep 17 00:00:00 2001 From: Cale Gibbard Date: Fri, 18 Nov 2022 13:43:59 -0500 Subject: [PATCH 45/69] bump constraints-extras --- reflex.cabal | 2 +- test/RequesterT.hs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index 958a6136..8cf07db9 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -76,7 +76,7 @@ library comonad >= 5.0.4 && < 5.1, commutative-semigroups >= 0.1 && <0.2, constraints >= 0.10 && <0.14, - constraints-extras >= 0.3 && < 0.4, + constraints-extras >= 0.3 && < 0.5, containers >= 0.6 && < 0.7, data-default >= 0.5 && < 0.8, dependent-map >= 0.3 && < 0.5, diff --git a/test/RequesterT.hs b/test/RequesterT.hs index 9bd19e0d..54d2119a 100644 --- a/test/RequesterT.hs +++ b/test/RequesterT.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} From 33b48697e74333f14690298b6da8491aca2f477c Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 9 Dec 2022 15:50:01 -0400 Subject: [PATCH 46/69] Support newer constraints-extras --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index c94738d6..b9b7570e 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -76,7 +76,7 @@ library comonad >= 5.0.4 && < 5.1, commutative-semigroups >= 0.1 && <0.2, constraints >= 0.10 && <0.14, - constraints-extras >= 0.3 && < 0.4, + constraints-extras >= 0.3 && < 0.5, containers >= 0.6 && < 0.7, data-default >= 0.5 && < 0.8, dependent-map >= 0.3 && < 0.5, From 66adca028f809b8b241709f4499adeea597e905c Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 9 Dec 2022 15:59:56 -0400 Subject: [PATCH 47/69] Update changelog --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index 08e54456..d62747d3 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,7 @@ ## 0.8.2.2 * Require witherable >= 0.4 and, hence, a newer monoidal-containers +* Support newer constraints-extras (0.4) ## 0.8.2.1 From 0166290bf680cc55fc30cd0cd701275f61de2f7e Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 9 Dec 2022 16:09:38 -0400 Subject: [PATCH 48/69] ci: update patch --- dep/reflex-platform/github.json | 4 ++-- dep/reflex-platform/thunk.nix | 5 ++++- release.nix | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dep/reflex-platform/github.json b/dep/reflex-platform/github.json index 9f4cbaa2..f23a0d26 100644 --- a/dep/reflex-platform/github.json +++ b/dep/reflex-platform/github.json @@ -3,6 +3,6 @@ "repo": "reflex-platform", "branch": "develop", "private": false, - "rev": "ac66356c8839d1dc16cc60887c2db5988a60e6c4", - "sha256": "0zk8pf72lid6cqq4mlr1mcwh6zd5lz9i83kw519aci6mfba1afvq" + "rev": "6c8830e059a6d2859cb1b65acefed3c2f1d216d3", + "sha256": "06kv45yq8qan0p22wzj5c9mx11ns1wddyqjr1xasjjkf6gaf0080" } diff --git a/dep/reflex-platform/thunk.nix b/dep/reflex-platform/thunk.nix index bbf2dc18..20f2d28c 100644 --- a/dep/reflex-platform/thunk.nix +++ b/dep/reflex-platform/thunk.nix @@ -2,7 +2,10 @@ let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: if !fetchSubmodules && !private then builtins.fetchTarball { url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; - } else (import {}).fetchFromGitHub { + } else (import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz"; + sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr"; +}) {}).fetchFromGitHub { inherit owner repo rev sha256 fetchSubmodules private; }; json = builtins.fromJSON (builtins.readFile ./github.json); diff --git a/release.nix b/release.nix index 3d1b11c4..62f051d8 100644 --- a/release.nix +++ b/release.nix @@ -35,8 +35,8 @@ let } {}; patch = self.callHackageDirect { pkg = "patch"; - ver = "0.0.7.0"; - sha256 = "0yr2hk3fpwjxi1z0n384k3aq9b3z00c02bbwqybcj3n20l4k17l6"; + ver = "0.0.8.0"; + sha256 = "1nnp7jn0vbx9zrnf57dxbknp6fbkqz7bca4i40aa6fabpwjw97kg"; } {}; }) # Use this package's source for reflex From 3d18f9a3260792dd78cfd1b13f6708102a2e40bd Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Wed, 21 Dec 2022 16:41:19 -0500 Subject: [PATCH 49/69] Update changelog --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index 29220c37..f66df946 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,7 @@ ## Unreleased * Expose all Requester internals in Reflex.Requester.Base.Internal +* [Add EventWriter instance for RequesterT #469](https://github.com/reflex-frp/reflex/pull/469) ## 0.8.2.2 From 8a186c48e48383d1fb35cea692e924fcd7956fba Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Wed, 21 Dec 2022 17:44:20 -0400 Subject: [PATCH 50/69] collection: holdUniqDyn on listWithKey child elements (#487) * collection: holdUniqDyn on listWithKey child elements * Add Eq contraints to other Reflex.Collection functions that now require them Co-authored-by: Cale Gibbard --- ChangeLog.md | 3 ++- reflex.cabal | 2 +- src/Reflex/Collection.hs | 16 ++++++++-------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index f66df946..3b87d486 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,8 @@ # Revision history for reflex -## Unreleased +## 0.9.0.0 +* Breaking Change: Filter updates to `listWithKey` child widgets so that changes to the input Map don't cause spurious updates to unaffected children. This imposes an `Eq` constraint on the child values. * Expose all Requester internals in Reflex.Requester.Base.Internal * [Add EventWriter instance for RequesterT #469](https://github.com/reflex-frp/reflex/pull/469) diff --git a/reflex.cabal b/reflex.cabal index 8885a37c..ac8e614d 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.8.2.2 +Version: 0.9.0.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. diff --git a/src/Reflex/Collection.hs b/src/Reflex/Collection.hs index 792e716a..b986dbe4 100644 --- a/src/Reflex/Collection.hs +++ b/src/Reflex/Collection.hs @@ -74,7 +74,7 @@ listHoldWithKey m0 m' f = do --where the Events carry diffs, not the whole value listWithKey :: forall t k v m a - . (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m) + . (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m, Eq v) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a)) @@ -106,7 +106,7 @@ listWithKey vals mkChild = do , tag (current vals) postBuild ] listHoldWithKey Map.empty changeVals $ \k v -> - mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k) + mkChild k =<< holdUniqDyn =<< holdDyn v (select childValChangedSelector $ Const2 k) -- | Display the given map of items (in key order) using the builder -- function provided, and update it with the given event. 'Nothing' @@ -147,7 +147,7 @@ listWithKeyShallowDiff initialVals valsChanged mkChild = do -- this scenario, but 'listViewWithKey' flattens this to -- @/Event t (Map k a)/@ via 'switch'. listViewWithKey - :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) + :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m, Eq v) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a)) @@ -155,7 +155,7 @@ listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild listViewWithKey' - :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) + :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m, Eq v) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a)) @@ -165,7 +165,7 @@ listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild -- selected at any time. selectViewListWithKey :: forall t m k v a - . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m) + . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m, Eq v) => Dynamic t k -- ^ Current selection key -> Dynamic t (Map k v) @@ -189,7 +189,7 @@ selectViewListWithKey selection vals mkChild = do -- item widget's output 'Event'. selectViewListWithKey_ :: forall t m k v a - . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m) + . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m, Eq v) => Dynamic t k -- ^ Current selection key -> Dynamic t (Map k v) @@ -207,7 +207,7 @@ selectViewListWithKey_ selection vals mkChild = -- key/value map. Unlike the 'withKey' variants, the child widgets -- are insensitive to which key they're associated with. list - :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) + :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m, Eq v) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a)) @@ -215,7 +215,7 @@ list dm mkChild = listWithKey dm (\_ dv -> mkChild dv) -- | Create a dynamically-changing set of widgets from a Dynamic list. simpleList - :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) + :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m, Eq v) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a]) From 82c2b5ef5f9ab390545756132771f91b4aafbfd1 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Wed, 21 Dec 2022 22:32:53 -0400 Subject: [PATCH 51/69] Support ghc 9.4.3 (#486) * ci: enable ghc 9.4.3 * cabal: loosen bounds * ci: remove extra cabal update * Add shell.nix; Allow compiler selection when entering shell * ci: update github cache action * cabal: loosen version bounds; Improve shell.nix * cabal: loosen time bounds * ci: fix reflex-platform build --- .github/workflows/haskell.yml | 10 ++++------ .gitignore | 1 - nixpkgs/default.nix | 2 ++ nixpkgs/github.json | 8 ++++++++ nixpkgs/thunk.nix | 12 ++++++++++++ overlay.nix | 15 +++++++++++++++ reflex.cabal | 18 +++++++++--------- release.nix | 29 +++++------------------------ shell.nix | 34 ++++++++++++++++++++++++++++++++++ src.nix | 8 ++++++++ 10 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 nixpkgs/default.nix create mode 100644 nixpkgs/github.json create mode 100644 nixpkgs/thunk.nix create mode 100644 overlay.nix create mode 100644 shell.nix create mode 100644 src.nix diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index 0b16d958..ff623e44 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -6,18 +6,18 @@ jobs: build: strategy: matrix: - ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2', '9.0.1', '9.2.2'] + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.7', '9.0.1', '9.2.5', '9.4.3'] os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} name: GHC ${{ matrix.ghc }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - uses: haskell/actions/setup@v1 + - uses: haskell/actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} - name: Cache - uses: actions/cache@v1 + uses: actions/cache@v3 env: cache-name: cache-cabal with: @@ -30,9 +30,7 @@ jobs: ${{ runner.os }} - name: Install dependencies - run: | - cabal update - cabal build --only-dependencies --enable-tests --enable-benchmarks + run: cabal build --only-dependencies --enable-tests --enable-benchmarks - name: Build run: cabal build --enable-tests --enable-benchmarks all - name: Run tests diff --git a/.gitignore b/.gitignore index 7823a5f7..0cab7d45 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ tags hsenv.log \#*# .#* -/shell.nix /ghci-tmp *.dump-* *.verbose-core2core diff --git a/nixpkgs/default.nix b/nixpkgs/default.nix new file mode 100644 index 00000000..2b4d4ab1 --- /dev/null +++ b/nixpkgs/default.nix @@ -0,0 +1,2 @@ +# DO NOT HAND-EDIT THIS FILE +import (import ./thunk.nix) \ No newline at end of file diff --git a/nixpkgs/github.json b/nixpkgs/github.json new file mode 100644 index 00000000..4c7dd2f2 --- /dev/null +++ b/nixpkgs/github.json @@ -0,0 +1,8 @@ +{ + "owner": "NixOS", + "repo": "nixpkgs", + "branch": "nixpkgs-unstable", + "private": false, + "rev": "e37ef84b478fa8da0ced96522adfd956fde9047a", + "sha256": "03qak39mn2142gp6zglrzrkdbig6h4r3da1psmvf3q2dwcw3zsfv" +} diff --git a/nixpkgs/thunk.nix b/nixpkgs/thunk.nix new file mode 100644 index 00000000..20f2d28c --- /dev/null +++ b/nixpkgs/thunk.nix @@ -0,0 +1,12 @@ +# DO NOT HAND-EDIT THIS FILE +let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: + if !fetchSubmodules && !private then builtins.fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; + } else (import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz"; + sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr"; +}) {}).fetchFromGitHub { + inherit owner repo rev sha256 fetchSubmodules private; + }; + json = builtins.fromJSON (builtins.readFile ./github.json); +in fetch json \ No newline at end of file diff --git a/overlay.nix b/overlay.nix new file mode 100644 index 00000000..70ddfdb5 --- /dev/null +++ b/overlay.nix @@ -0,0 +1,15 @@ +{ haskellLib, self, super }: +{ + # jailbreak here because callHackageDirect doesn't give us a way to get the latest revision of a package + # 0.1.0.0-r3 would work just fine + commutative-semigroups = haskellLib.doJailbreak (self.callHackageDirect { + pkg = "commutative-semigroups"; + ver = "0.1.0.0"; + sha256 = "0xmv20n3iqjc64xi3c91bwqrg8x79sgipmflmk21zz4rj9jdkv8i"; + } {}); + patch = self.callHackageDirect { + pkg = "patch"; + ver = "0.0.8.1"; + sha256 = "0q5rxnyilhbnfph48fnxbclggsbbhs0pkn0kfiadm0hmfr440cgk"; + } {}; +} diff --git a/reflex.cabal b/reflex.cabal index ac8e614d..fc228569 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -28,8 +28,8 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2 || ==9.0.1 || ==9.2.2, - GHCJS ==8.6 + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.7 || ==9.0.1 || ==9.2.5 || ==9.4.3, + GHCJS ==8.6 || ==8.10 flag use-reflex-optimizer description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental. @@ -71,7 +71,7 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.11 && < 4.17, + base >= 4.11 && < 4.18, bifunctors >= 5.2 && < 5.6, comonad >= 5.0.4 && < 5.1, commutative-semigroups >= 0.1 && <0.2, @@ -81,7 +81,7 @@ library data-default >= 0.5 && < 0.8, dependent-map >= 0.3 && < 0.5, exception-transformers == 0.4.*, - lens >= 4.7 && < 5.2, + lens >= 4.7 && < 5.3, mmorph >= 1.0 && < 1.2, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, @@ -95,7 +95,7 @@ library semigroupoids >= 4.0 && < 6, stm >= 2.4 && < 2.6, syb >= 0.5 && < 0.8, - time >= 1.4 && < 1.12, + time >= 1.4 && < 1.13, transformers >= 0.5.6.0 && < 0.6, unbounded-delays >= 0.1.0.0 && < 0.2, witherable >= 0.4 && < 0.5 @@ -188,7 +188,7 @@ library dependent-sum >= 0.6 && < 0.8, haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.19 + template-haskell >= 2.9 && < 2.20 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell @@ -260,10 +260,10 @@ test-suite hlint , directory , filepath , filemanip - if impl(ghc >= 8.8) - build-depends: hlint >= 3 && < 4 + if impl(ghc < 9.2) + build-depends: hlint (< 2.1 || >= 2.2.2) && < 3.5 else - build-depends: hlint (< 2.1 || >= 2.2.2) && < 4 + build-depends: hlint >= 3.5 && < 3.6 if impl(ghcjs) buildable: False diff --git a/release.nix b/release.nix index 62f051d8..2cf9dd8f 100644 --- a/release.nix +++ b/release.nix @@ -3,11 +3,11 @@ }: let - native-reflex-platform = reflex-platform-fun {}; + native-reflex-platform = reflex-platform-fun { __useNewerCompiler = true; }; inherit (native-reflex-platform.nixpkgs) lib; perPlatform = lib.genAttrs supportedSystems (system: let - reflex-platform = reflex-platform-fun { inherit system; }; + reflex-platform = reflex-platform-fun { inherit system; __useNewerCompiler = true; }; compilers = [ "ghc" "ghcjs" @@ -25,32 +25,13 @@ let variationPkgs = lib.genAttrs variations (variation: let reflex-platform = reflex-platform-fun { inherit system; + __useNewerCompiler = true; __useTemplateHaskell = variation == "reflex"; # TODO hack haskellOverlays = [ - (self: super: { - commutative-semigroups = self.callHackageDirect { - pkg = "commutative-semigroups"; - ver = "0.1.0.0"; - sha256 = "0xmv20n3iqjc64xi3c91bwqrg8x79sgipmflmk21zz4rj9jdkv8i"; - } {}; - patch = self.callHackageDirect { - pkg = "patch"; - ver = "0.0.8.0"; - sha256 = "1nnp7jn0vbx9zrnf57dxbknp6fbkqz7bca4i40aa6fabpwjw97kg"; - } {}; - }) + (self: super: import ./overlay.nix { inherit self super; haskellLib = native-reflex-platform.nixpkgs.haskell.lib; }) # Use this package's source for reflex (self: super: { - _dep = super._dep // { - reflex = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ - "release.nix" - ".git" - "dist" - "cabal.haskell-ci" - "cabal.project" - ".travis.yml" - ])) ./.; - }; + _dep = super._dep // { reflex = import ./src.nix; }; }) ]; }; diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..365f7c0a --- /dev/null +++ b/shell.nix @@ -0,0 +1,34 @@ +# Enter a shell for this project, with some choice of compiler. By default, we +# select the version of ghc provided by reflex-platform, but you can choose a +# later version from nixpkgs as well by doing: +# $ nix-shell --argstr compiler "ghc943" +{ compiler ? "reflex-platform" # or "ghc943", "ghc924" +}: +let + rp = import ./dep/reflex-platform { __useNewerCompiler = true; }; + pkgs = rp.nixpkgs; + haskellLib = pkgs.haskell.lib; + system = builtins.currentSystem; + nixpkgsGhc = ((import ./nixpkgs {}).haskell.packages.${compiler}).override { + overrides = self: super: import ./overlay.nix { inherit self super haskellLib; } // { + hlint = self.callHackageDirect { + pkg = "hlint"; + ver = "3.5"; + sha256 = "1np43k54918v54saqqgnd82ccd6225njwxpg2031asi70jam80x9"; + } {}; + }; + }; + reflexEnv = if compiler == "reflex-platform" + then (import ./release.nix {}).${system}.ghc.reflex.env + else (nixpkgsGhc.callCabal2nix "reflex" (import ./src.nix) {}).env; +in + pkgs.mkShell { + name = "shell"; + buildInputs = [ + pkgs.cabal-install + pkgs.ghcid + ]; + inputsFrom = [ + reflexEnv + ]; + } diff --git a/src.nix b/src.nix new file mode 100644 index 00000000..60039b2e --- /dev/null +++ b/src.nix @@ -0,0 +1,8 @@ +builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ + "release.nix" + ".git" + "dist" + "cabal.haskell-ci" + "cabal.project" + ".travis.yml" +])) ./. From 68d66e87f58075c0cfabe095072622d08c24ab01 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Wed, 24 May 2023 15:27:51 -0400 Subject: [PATCH 52/69] Update for GHC 9.6 (#493) * Update for GHC 9.6 * ci: add ghc 9.6 * cabal: loosen bifunctors version bounds * ci: loosen bounds for ghc84 * ci: build exception-transformers without tests on nix * Bump version and update changelog * bench: add import to fix ghc96 build --------- Co-authored-by: Doug Beardsley --- .github/workflows/haskell.yml | 9 ++-- ChangeLog.md | 4 ++ bench/Main.hs | 1 + nixpkgs/github.json | 4 +- reflex.cabal | 24 ++++++----- release.nix | 60 ++++++++++++++++++++++++++- src/Reflex/BehaviorWriter/Base.hs | 2 + src/Reflex/Class.hs | 2 + src/Reflex/Collection.hs | 2 + src/Reflex/Dynamic.hs | 2 +- src/Reflex/DynamicWriter/Base.hs | 2 + src/Reflex/EventWriter/Base.hs | 1 + src/Reflex/PerformEvent/Base.hs | 1 + src/Reflex/PerformEvent/Class.hs | 2 + src/Reflex/PostBuild/Base.hs | 1 + src/Reflex/Requester/Base/Internal.hs | 2 + src/Reflex/Requester/Class.hs | 1 + src/Reflex/Spider/Internal.hs | 1 + src/Reflex/TriggerEvent/Base.hs | 1 + test/Reflex/Plan/Reflex.hs | 1 + test/Reflex/Test/CrossImpl.hs | 2 + 21 files changed, 106 insertions(+), 19 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index ff623e44..dc3c98e9 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -6,16 +6,17 @@ jobs: build: strategy: matrix: - ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.7', '9.0.1', '9.2.5', '9.4.3'] + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.7', '9.0.2', '9.2.5', '9.4.5', '9.6.1'] os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} name: GHC ${{ matrix.ghc }} on ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: haskell/actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} + cabal-version: '3.10.1.0' - name: Cache uses: actions/cache@v3 env: @@ -35,5 +36,7 @@ jobs: run: cabal build --enable-tests --enable-benchmarks all - name: Run tests run: cabal test --enable-tests all - - name: Build Docs + - if: matrix.ghc != '8.4.4' + # docs aren't built on ghc 8.4.4 because some dependency docs don't build on older GHCs + name: Build Docs run: cabal haddock diff --git a/ChangeLog.md b/ChangeLog.md index 3b87d486..ee4dec33 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## 0.9.0.1 + +* Add support for ghc-9.6 + ## 0.9.0.0 * Breaking Change: Filter updates to `listWithKey` child widgets so that changes to the input Map don't cause spurious updates to unaffected children. This imposes an `Eq` constraint on the child values. diff --git a/bench/Main.hs b/bench/Main.hs index bed63496..2e12e9c1 100644 --- a/bench/Main.hs +++ b/bench/Main.hs @@ -12,6 +12,7 @@ module Main where import Control.Concurrent.STM import Control.DeepSeq import Control.Exception (evaluate) +import Control.Monad import Control.Monad.Identity import Control.Monad.IO.Class import Criterion.Main diff --git a/nixpkgs/github.json b/nixpkgs/github.json index 4c7dd2f2..89cfe00a 100644 --- a/nixpkgs/github.json +++ b/nixpkgs/github.json @@ -3,6 +3,6 @@ "repo": "nixpkgs", "branch": "nixpkgs-unstable", "private": false, - "rev": "e37ef84b478fa8da0ced96522adfd956fde9047a", - "sha256": "03qak39mn2142gp6zglrzrkdbig6h4r3da1psmvf3q2dwcw3zsfv" + "rev": "c7eb65213bd7d95eafb8c5e2e181f04da103d054", + "sha256": "1glf6j13hbwi459qrc8kkkhfw27a08vdg17sr3zwhadg4bkxz5ia" } diff --git a/reflex.cabal b/reflex.cabal index fc228569..f867a37f 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.9.0.0 +Version: 0.9.0.1 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. @@ -28,7 +28,7 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.7 || ==9.0.1 || ==9.2.5 || ==9.4.3, + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.7 || ==9.0.1 || ==9.2.5 || ==9.4.5 || ==9.6.1, GHCJS ==8.6 || ==8.10 flag use-reflex-optimizer @@ -71,8 +71,8 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.11 && < 4.18, - bifunctors >= 5.2 && < 5.6, + base >= 4.11 && < 4.19, + bifunctors >= 5.2 && < 5.7, comonad >= 5.0.4 && < 5.1, commutative-semigroups >= 0.1 && <0.2, constraints >= 0.10 && <0.14, @@ -80,11 +80,11 @@ library containers >= 0.6 && < 0.7, data-default >= 0.5 && < 0.8, dependent-map >= 0.3 && < 0.5, - exception-transformers == 0.4.*, + exception-transformers >= 0.4 && < 0.5, lens >= 4.7 && < 5.3, - mmorph >= 1.0 && < 1.2, + mmorph >= 1.0 && < 1.3, monad-control >= 1.0.1 && < 1.1, - mtl >= 2.1 && < 2.3, + mtl >= 2.1 && < 2.4, patch >= 0.0.7 && < 0.1, prim-uniq >= 0.1.0.1 && < 0.3, primitive >= 0.5 && < 0.8, @@ -92,17 +92,17 @@ library random >= 1.1 && < 1.3, ref-tf >= 0.4 && < 0.6, reflection == 2.1.*, - semigroupoids >= 4.0 && < 6, + semigroupoids >= 4.0 && < 7, stm >= 2.4 && < 2.6, syb >= 0.5 && < 0.8, time >= 1.4 && < 1.13, - transformers >= 0.5.6.0 && < 0.6, + transformers >= 0.5 && < 0.7, unbounded-delays >= 0.1.0.0 && < 0.2, witherable >= 0.4 && < 0.5 if flag(split-these) build-depends: these >= 1 && <1.3, - semialign >=1 && <1.3, + semialign >=1 && <1.4, monoidal-containers >= 0.6.2.0 && < 0.7 else build-depends: these >= 0.4 && <0.9, @@ -188,7 +188,7 @@ library dependent-sum >= 0.6 && < 0.8, haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.20 + template-haskell >= 2.9 && < 2.21 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell @@ -260,6 +260,8 @@ test-suite hlint , directory , filepath , filemanip + if impl(ghc >= 9.6) + buildable: False if impl(ghc < 9.2) build-depends: hlint (< 2.1 || >= 2.2.2) && < 3.5 else diff --git a/release.nix b/release.nix index 2cf9dd8f..dac89c9f 100644 --- a/release.nix +++ b/release.nix @@ -5,7 +5,6 @@ let native-reflex-platform = reflex-platform-fun { __useNewerCompiler = true; }; inherit (native-reflex-platform.nixpkgs) lib; - perPlatform = lib.genAttrs supportedSystems (system: let reflex-platform = reflex-platform-fun { inherit system; __useNewerCompiler = true; }; compilers = [ @@ -21,6 +20,63 @@ let "-dontUseTemplateHaskell" "" ]; + pkgs = import ./nixpkgs { inherit system; }; + sharedOverrides = self: super: { + exception-transformers = pkgs.haskell.lib.dontCheck super.exception-transformers; + }; + nixpkgsGhcs = + let + nixGhc902 = pkgs.haskell.packages.ghc902.override { overrides = sharedOverrides; }; + nixGhc945 = pkgs.haskell.packages.ghc945.override { overrides = sharedOverrides; }; + nixGhc961 = pkgs.haskell.packages.ghc961.override { + overrides = self: super: sharedOverrides self super // { + these-lens = self.callHackageDirect { + pkg = "these-lens"; + ver = "1.0.1.3"; + sha256 = "0n1vkr57jz5yvy4jm15v5cs42rp342ni0gisib7aqyhibpicqs5c"; + } {}; + these = self.callHackageDirect { + pkg = "these"; + ver = "1.2"; + sha256 = "1iaaq1fsvg8c3l0czcicshkmbbr00hnwkdamjbkljsa1qvlilaf0"; + } {}; + lens = self.callHackageDirect { + pkg = "lens"; + ver = "5.2.2"; + sha256 = "0c4a421sxfjm1cj3nvgwkr4glll23mqnsvs2iv5qh85931h2f3cy"; + } {}; + + assoc = self.callHackageDirect { + pkg = "assoc"; + ver = "1.1"; + sha256 = "1krvcafrbj98z5hv55gq4zb1in5yd71nmz9zdiqgnywjzbrvpf75"; + } {}; + + strict = self.callHackageDirect { + pkg = "strict"; + ver = "0.5"; + sha256 = "02iyvrr7nd7fnivz78lzdchy8zw1cghqj1qx2yzbbb9869h1mny7"; + } {}; + + hlint = self.callHackageDirect { + pkg = "hlint"; + ver = "3.5"; + sha256 = "1np43k54918v54saqqgnd82ccd6225njwxpg2031asi70jam80x9"; + } {}; + + patch = self.callHackageDirect { + pkg = "patch"; + ver = "0.0.8.2"; + sha256 = "160zqqhjg48fr3a33gffd82qm3728c8hwf8sn37pbpv82fw71rzg"; + } {}; + }; + }; + in + { + ghc902 = nixGhc902.callCabal2nix "reflex" (import ./src.nix) {}; + ghc945 = nixGhc945.callCabal2nix "reflex" (import ./src.nix) {}; + ghc961 = nixGhc961.callCabal2nix "reflex" (import ./src.nix) {}; + }; compilerPkgs = lib.genAttrs compilers (ghc: let variationPkgs = lib.genAttrs variations (variation: let reflex-platform = reflex-platform-fun { @@ -40,7 +96,7 @@ let cache = reflex-platform.pinBuildInputs "reflex-${system}-${ghc}" (builtins.attrValues variationPkgs); }); - in compilerPkgs // { + in compilerPkgs // nixpkgsGhcs // { cache = reflex-platform.pinBuildInputs "reflex-${system}" (map (a: a.cache) (builtins.attrValues compilerPkgs)); }); diff --git a/src/Reflex/BehaviorWriter/Base.hs b/src/Reflex/BehaviorWriter/Base.hs index 0ffff17f..c83d8f53 100644 --- a/src/Reflex/BehaviorWriter/Base.hs +++ b/src/Reflex/BehaviorWriter/Base.hs @@ -21,7 +21,9 @@ module Reflex.BehaviorWriter.Base , withBehaviorWriterT ) where +import Control.Monad import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.IO.Class import Control.Monad.Morph diff --git a/src/Reflex/Class.hs b/src/Reflex/Class.hs index fbdbfce5..128ac493 100644 --- a/src/Reflex/Class.hs +++ b/src/Reflex/Class.hs @@ -185,6 +185,8 @@ import Data.Zip (Zip (..), Unzip (..)) #endif import Control.Applicative +import Control.Monad +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.Reader import Control.Monad.State.Strict diff --git a/src/Reflex/Collection.hs b/src/Reflex/Collection.hs index b986dbe4..770ea4b6 100644 --- a/src/Reflex/Collection.hs +++ b/src/Reflex/Collection.hs @@ -34,6 +34,8 @@ import Data.Zip (Zip (..)) #endif #endif +import Control.Monad +import Control.Monad.Fix import Control.Monad.Identity import Data.Align import Data.Functor.Misc diff --git a/src/Reflex/Dynamic.hs b/src/Reflex/Dynamic.hs index 649c7a87..4df27298 100644 --- a/src/Reflex/Dynamic.hs +++ b/src/Reflex/Dynamic.hs @@ -99,7 +99,7 @@ import Data.Monoid ((<>)) import Data.These import Data.Type.Equality ((:~:) (..)) -import Debug.Trace +import Debug.Trace hiding (traceEventWith) -- | Map a sampling function over a 'Dynamic'. mapDynM :: forall t m a b. (Reflex t, MonadHold t m) => (forall m'. MonadSample t m' => a -> m' b) -> Dynamic t a -> m (Dynamic t b) diff --git a/src/Reflex/DynamicWriter/Base.hs b/src/Reflex/DynamicWriter/Base.hs index af7d1359..1e89150e 100644 --- a/src/Reflex/DynamicWriter/Base.hs +++ b/src/Reflex/DynamicWriter/Base.hs @@ -19,7 +19,9 @@ module Reflex.DynamicWriter.Base , withDynamicWriterT ) where +import Control.Monad import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.IO.Class import Control.Monad.Morph diff --git a/src/Reflex/EventWriter/Base.hs b/src/Reflex/EventWriter/Base.hs index 82266a0a..2d28d94e 100644 --- a/src/Reflex/EventWriter/Base.hs +++ b/src/Reflex/EventWriter/Base.hs @@ -34,6 +34,7 @@ import Reflex.Requester.Class import Reflex.TriggerEvent.Class import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.Morph import Control.Monad.Primitive diff --git a/src/Reflex/PerformEvent/Base.hs b/src/Reflex/PerformEvent/Base.hs index 2263c23a..fb9eefea 100644 --- a/src/Reflex/PerformEvent/Base.hs +++ b/src/Reflex/PerformEvent/Base.hs @@ -33,6 +33,7 @@ import Reflex.Requester.Class import Control.Lens import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.Primitive import Control.Monad.Reader diff --git a/src/Reflex/PerformEvent/Class.hs b/src/Reflex/PerformEvent/Class.hs index cd240a13..1b72fdde 100644 --- a/src/Reflex/PerformEvent/Class.hs +++ b/src/Reflex/PerformEvent/Class.hs @@ -17,6 +17,8 @@ module Reflex.PerformEvent.Class , performEventAsync ) where +import Control.Monad +import Control.Monad.Fix import Control.Monad.Reader import Control.Monad.Trans.Maybe (MaybeT (..)) diff --git a/src/Reflex/PostBuild/Base.hs b/src/Reflex/PostBuild/Base.hs index 5dba681b..9c4db4d7 100644 --- a/src/Reflex/PostBuild/Base.hs +++ b/src/Reflex/PostBuild/Base.hs @@ -30,6 +30,7 @@ import Reflex.TriggerEvent.Class import Control.Applicative (liftA2) import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.Primitive import Control.Monad.Reader diff --git a/src/Reflex/Requester/Base/Internal.hs b/src/Reflex/Requester/Base/Internal.hs index ea2580ee..1bf303b8 100644 --- a/src/Reflex/Requester/Base/Internal.hs +++ b/src/Reflex/Requester/Base/Internal.hs @@ -32,7 +32,9 @@ import Reflex.Requester.Class import Reflex.TriggerEvent.Class import Control.Applicative (liftA2) +import Control.Monad import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.Morph import Control.Monad.Primitive diff --git a/src/Reflex/Requester/Class.hs b/src/Reflex/Requester/Class.hs index 679e6abd..f9e50757 100644 --- a/src/Reflex/Requester/Class.hs +++ b/src/Reflex/Requester/Class.hs @@ -18,6 +18,7 @@ module Reflex.Requester.Class , requestingIdentity ) where +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.Reader import qualified Control.Monad.State.Lazy as Lazy diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs index ff4b4835..0206b79c 100644 --- a/src/Reflex/Spider/Internal.hs +++ b/src/Reflex/Spider/Internal.hs @@ -36,6 +36,7 @@ import Control.Concurrent import Control.Exception import Control.Monad hiding (forM, forM_, mapM, mapM_) import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_) import Control.Monad.Primitive import Control.Monad.Reader.Class diff --git a/src/Reflex/TriggerEvent/Base.hs b/src/Reflex/TriggerEvent/Base.hs index eb0edfd7..35969a2b 100644 --- a/src/Reflex/TriggerEvent/Base.hs +++ b/src/Reflex/TriggerEvent/Base.hs @@ -16,6 +16,7 @@ module Reflex.TriggerEvent.Base import Control.Applicative (liftA2) import Control.Concurrent import Control.Monad.Exception +import Control.Monad.Fix import Control.Monad.Primitive import Control.Monad.Reader import Control.Monad.Ref diff --git a/test/Reflex/Plan/Reflex.hs b/test/Reflex/Plan/Reflex.hs index c7987f65..9e42f03b 100644 --- a/test/Reflex/Plan/Reflex.hs +++ b/test/Reflex/Plan/Reflex.hs @@ -34,6 +34,7 @@ import Reflex.TestPlan import Control.Applicative import Control.Monad +import Control.Monad.Fix import Control.Monad.Identity import Control.Monad.State.Strict diff --git a/test/Reflex/Test/CrossImpl.hs b/test/Reflex/Test/CrossImpl.hs index 4d9abed9..780ecea8 100644 --- a/test/Reflex/Test/CrossImpl.hs +++ b/test/Reflex/Test/CrossImpl.hs @@ -24,6 +24,8 @@ import qualified Reflex.Spider.Internal as S import qualified Reflex.Profiled as Prof import Control.Arrow (second, (&&&)) +import Control.Monad +import Control.Monad.Fix import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_) import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_) import Data.Dependent.Sum (DSum (..)) From 2fb31a258b06d5f79204ccec89be0dbeee64177e Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Wed, 24 May 2023 15:28:40 -0400 Subject: [PATCH 53/69] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b6ba088..04e26e22 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [Reflex](https://reflex-frp.org/) -[![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) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE) +[![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) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE) 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. From bda4f8d1a245a9e4d1ebcd81c3be9445460e6251 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 2 Jun 2023 17:01:19 -0400 Subject: [PATCH 54/69] headless: add a few instances --- ChangeLog.md | 4 ++++ reflex.cabal | 2 +- src/Reflex/Host/Headless.hs | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index ee4dec33..144ffb8b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## 0.9.1.0 + +* Headless Host: Add some MonadSample, MonadHold, and MonadFix instances + ## 0.9.0.1 * Add support for ghc-9.6 diff --git a/reflex.cabal b/reflex.cabal index f867a37f..2de9fdb8 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.9.0.1 +Version: 0.9.1.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. diff --git a/src/Reflex/Host/Headless.hs b/src/Reflex/Host/Headless.hs index 0b4b9256..21b2d6cf 100644 --- a/src/Reflex/Host/Headless.hs +++ b/src/Reflex/Host/Headless.hs @@ -39,6 +39,10 @@ type MonadHeadlessApp t m = , Reflex t , ReflexHost t , TriggerEvent t m + , MonadSample t (Performable m) + , MonadSample t m + , MonadFix (Performable m) + , MonadHold t (Performable m) ) -- | Run a headless FRP network. Inside the action, you will most probably use From d77ad8cfdd1869688475e3140304e3899dc7b605 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 7 Jul 2023 15:59:53 -0400 Subject: [PATCH 55/69] Add MonadMask, MonadCatch, MonadThrow instances --- ChangeLog.md | 4 ++++ reflex.cabal | 1 + src/Reflex/BehaviorWriter/Base.hs | 4 ++++ src/Reflex/DynamicWriter/Base.hs | 4 ++++ src/Reflex/EventWriter/Base.hs | 4 ++++ src/Reflex/Host/Headless.hs | 16 ++++++++++------ src/Reflex/PerformEvent/Base.hs | 4 ++++ src/Reflex/PostBuild/Base.hs | 16 +++++++++++++++- src/Reflex/Query/Base.hs | 3 ++- src/Reflex/Requester/Base/Internal.hs | 4 ++++ src/Reflex/Spider/Internal.hs | 6 ++++-- src/Reflex/TriggerEvent/Base.hs | 3 ++- 12 files changed, 58 insertions(+), 11 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 144ffb8b..386c3b58 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## Unreleased + +* Add MonadMask, MonadCatch, MonadThrow instances + ## 0.9.1.0 * Headless Host: Add some MonadSample, MonadHold, and MonadFix instances diff --git a/reflex.cabal b/reflex.cabal index 2de9fdb8..7c86bc06 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -80,6 +80,7 @@ library containers >= 0.6 && < 0.7, data-default >= 0.5 && < 0.8, dependent-map >= 0.3 && < 0.5, + exceptions >= 0.10 && < 0.11, exception-transformers >= 0.4 && < 0.5, lens >= 4.7 && < 5.3, mmorph >= 1.0 && < 1.3, diff --git a/src/Reflex/BehaviorWriter/Base.hs b/src/Reflex/BehaviorWriter/Base.hs index c83d8f53..520d8602 100644 --- a/src/Reflex/BehaviorWriter/Base.hs +++ b/src/Reflex/BehaviorWriter/Base.hs @@ -22,6 +22,7 @@ module Reflex.BehaviorWriter.Base ) where import Control.Monad +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Identity @@ -62,6 +63,9 @@ newtype BehaviorWriterT t w m a = BehaviorWriterT { unBehaviorWriterT :: StateT , MonadFix , MonadAsyncException , MonadException + , MonadCatch + , MonadThrow + , MonadMask ) -- | Run a 'BehaviorWriterT' action. The behavior writer output will be provided diff --git a/src/Reflex/DynamicWriter/Base.hs b/src/Reflex/DynamicWriter/Base.hs index 1e89150e..912aea3d 100644 --- a/src/Reflex/DynamicWriter/Base.hs +++ b/src/Reflex/DynamicWriter/Base.hs @@ -20,6 +20,7 @@ module Reflex.DynamicWriter.Base ) where import Control.Monad +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Identity @@ -101,6 +102,9 @@ newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dy , MonadFix , MonadAsyncException , MonadException + , MonadCatch + , MonadThrow + , MonadMask ) deriving instance MonadHold t m => MonadHold t (DynamicWriterT t w m) diff --git a/src/Reflex/EventWriter/Base.hs b/src/Reflex/EventWriter/Base.hs index 2d28d94e..0612d66a 100644 --- a/src/Reflex/EventWriter/Base.hs +++ b/src/Reflex/EventWriter/Base.hs @@ -33,6 +33,7 @@ import Reflex.Query.Class import Reflex.Requester.Class import Reflex.TriggerEvent.Class +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Identity @@ -109,6 +110,9 @@ newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (EventWri , MonadIO , MonadException , MonadAsyncException + , MonadMask + , MonadCatch + , MonadThrow ) -- | Run a 'EventWriterT' action. diff --git a/src/Reflex/Host/Headless.hs b/src/Reflex/Host/Headless.hs index 21b2d6cf..bfe6a9a4 100644 --- a/src/Reflex/Host/Headless.hs +++ b/src/Reflex/Host/Headless.hs @@ -8,6 +8,7 @@ module Reflex.Host.Headless where import Control.Concurrent.Chan (newChan, readChan) import Control.Monad (unless) +import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow) import Control.Monad.Fix (MonadFix, fix) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Primitive (PrimMonad) @@ -23,26 +24,29 @@ import Reflex import Reflex.Host.Class type MonadHeadlessApp t m = - ( Adjustable t m + ( Reflex t + , Adjustable t m + , MonadCatch m + , MonadFix (Performable m) , MonadFix m + , MonadHold t (Performable m) , MonadHold t m , MonadIO (HostFrame t) , MonadIO (Performable m) , MonadIO m + , MonadMask m , MonadRef (HostFrame t) + , MonadSample t (Performable m) + , MonadSample t m + , MonadThrow m , NotReady t m , PerformEvent t m , PostBuild t m , PrimMonad (HostFrame t) , Ref (HostFrame t) ~ IORef , Ref m ~ IORef - , Reflex t , ReflexHost t , TriggerEvent t m - , MonadSample t (Performable m) - , MonadSample t m - , MonadFix (Performable m) - , MonadHold t (Performable m) ) -- | Run a headless FRP network. Inside the action, you will most probably use diff --git a/src/Reflex/PerformEvent/Base.hs b/src/Reflex/PerformEvent/Base.hs index fb9eefea..152460f9 100644 --- a/src/Reflex/PerformEvent/Base.hs +++ b/src/Reflex/PerformEvent/Base.hs @@ -32,6 +32,7 @@ import Reflex.Requester.Base import Reflex.Requester.Class import Control.Lens +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Identity @@ -64,6 +65,9 @@ deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEvent deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m) deriving instance (ReflexHost t, Monoid a) => Monoid (PerformEventT t m a) deriving instance (ReflexHost t, S.Semigroup a) => S.Semigroup (PerformEventT t m a) +deriving instance (ReflexHost t, MonadCatch (HostFrame t)) => MonadCatch (PerformEventT t m) +deriving instance (ReflexHost t, MonadThrow (HostFrame t)) => MonadThrow (PerformEventT t m) +deriving instance (ReflexHost t, MonadMask (HostFrame t)) => MonadMask (PerformEventT t m) instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m) where type PrimState (PerformEventT t m) = PrimState (HostFrame t) diff --git a/src/Reflex/PostBuild/Base.hs b/src/Reflex/PostBuild/Base.hs index 9c4db4d7..b866c2e3 100644 --- a/src/Reflex/PostBuild/Base.hs +++ b/src/Reflex/PostBuild/Base.hs @@ -29,6 +29,7 @@ import Reflex.PostBuild.Class import Reflex.TriggerEvent.Class import Control.Applicative (liftA2) +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Identity @@ -44,7 +45,20 @@ import qualified Data.IntMap.Strict as IntMap import qualified Data.Semigroup as S -- | Provides a basic implementation of 'PostBuild'. -newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException) +newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } + deriving + ( Functor + , Applicative + , Monad + , MonadFix + , MonadIO + , MonadTrans + , MonadException + , MonadAsyncException + , MonadMask + , MonadThrow + , MonadCatch + ) -- | Run a 'PostBuildT' action. An 'Event' should be provided that fires -- immediately after the action is finished running; no other 'Event's should diff --git a/src/Reflex/Query/Base.hs b/src/Reflex/Query/Base.hs index 59eaa0db..d4135e47 100644 --- a/src/Reflex/Query/Base.hs +++ b/src/Reflex/Query/Base.hs @@ -20,6 +20,7 @@ module Reflex.Query.Base ) where import Control.Applicative (liftA2) +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Morph @@ -60,7 +61,7 @@ import Reflex.Requester.Class import Reflex.TriggerEvent.Class newtype QueryT t q m a = QueryT { unQueryT :: StateT [Behavior t q] (EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m)) a } - deriving (Functor, Applicative, Monad, MonadException, MonadFix, MonadIO, MonadAtomicRef) + deriving (Functor, Applicative, Monad, MonadException, MonadFix, MonadIO, MonadAtomicRef, MonadCatch, MonadThrow, MonadMask) deriving instance MonadHold t m => MonadHold t (QueryT t q m) deriving instance MonadSample t m => MonadSample t (QueryT t q m) diff --git a/src/Reflex/Requester/Base/Internal.hs b/src/Reflex/Requester/Base/Internal.hs index 1bf303b8..12d7a8f9 100644 --- a/src/Reflex/Requester/Base/Internal.hs +++ b/src/Reflex/Requester/Base/Internal.hs @@ -33,6 +33,7 @@ import Reflex.TriggerEvent.Class import Control.Applicative (liftA2) import Control.Monad +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Identity @@ -277,6 +278,9 @@ newtype RequesterT t request (response :: Type -> Type) m a = RequesterT { unReq #if MIN_VERSION_base(4,9,1) , MonadAsyncException #endif + , MonadCatch + , MonadThrow + , MonadMask ) deriving instance MonadSample t m => MonadSample t (RequesterT t request response m) diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs index 0206b79c..bd833ed2 100644 --- a/src/Reflex/Spider/Internal.hs +++ b/src/Reflex/Spider/Internal.hs @@ -35,6 +35,7 @@ import Control.Applicative (liftA2) import Control.Concurrent import Control.Exception import Control.Monad hiding (forM, forM_, mapM, mapM_) +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_) @@ -1058,7 +1059,8 @@ data SomeMergeUpdate x = SomeMergeUpdate newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () } -- EventM can do everything BehaviorM can, plus create holds -newtype EventM x a = EventM { unEventM :: IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException) +newtype EventM x a = EventM { unEventM :: IO a } + deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException, MonadCatch, MonadThrow, MonadMask) newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x } @@ -2864,7 +2866,7 @@ runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a runSpiderHostForTimeline (SpiderHost a) _ = a newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } - deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException) + deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException, MonadMask, MonadThrow, MonadCatch) instance Monad (SpiderHostFrame x) where {-# INLINABLE (>>=) #-} diff --git a/src/Reflex/TriggerEvent/Base.hs b/src/Reflex/TriggerEvent/Base.hs index 35969a2b..2e409447 100644 --- a/src/Reflex/TriggerEvent/Base.hs +++ b/src/Reflex/TriggerEvent/Base.hs @@ -15,6 +15,7 @@ module Reflex.TriggerEvent.Base import Control.Applicative (liftA2) import Control.Concurrent +import Control.Monad.Catch (MonadMask, MonadThrow, MonadCatch) import Control.Monad.Exception import Control.Monad.Fix import Control.Monad.Primitive @@ -41,7 +42,7 @@ newtype EventTriggerRef t a = EventTriggerRef { unEventTriggerRef :: IORef (Mayb -- | A basic implementation of 'TriggerEvent'. newtype TriggerEventT t m a = TriggerEventT { unTriggerEventT :: ReaderT (Chan [DSum (EventTriggerRef t) TriggerInvocation]) m a } - deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException) + deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException, MonadCatch, MonadThrow, MonadMask) -- | Run a 'TriggerEventT' action. The argument should be a 'Chan' into which -- 'TriggerInvocation's can be passed; it is expected that some other thread From 535978b825a3055e22d04b043657bb18786e59ba Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 7 Jul 2023 16:09:59 -0400 Subject: [PATCH 56/69] Version 0.9.2.0 --- ChangeLog.md | 2 +- reflex.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 386c3b58..57bf687e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,6 @@ # Revision history for reflex -## Unreleased +## 0.9.2.0 * Add MonadMask, MonadCatch, MonadThrow instances diff --git a/reflex.cabal b/reflex.cabal index 7c86bc06..5e817e62 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.9.1.0 +Version: 0.9.2.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 2d3583bf6f946ceca63978c826e3066921903409 Mon Sep 17 00:00:00 2001 From: Ryan Trinkle Date: Tue, 26 Dec 2023 15:21:44 -0400 Subject: [PATCH 57/69] Allow runHeadlessApp to return a non-unit value --- ChangeLog.md | 4 +++ src/Reflex/Host/Headless.hs | 71 +++++++++++++++++++++---------------- test/hlint.hs | 1 + 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 57bf687e..fc98f9c5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## 0.9.3.0 + +* Headless Host: Generalize to allow returning arbitrary types + ## 0.9.2.0 * Add MonadMask, MonadCatch, MonadThrow instances diff --git a/src/Reflex/Host/Headless.hs b/src/Reflex/Host/Headless.hs index bfe6a9a4..8da83329 100644 --- a/src/Reflex/Host/Headless.hs +++ b/src/Reflex/Host/Headless.hs @@ -1,23 +1,22 @@ -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ConstraintKinds #-} module Reflex.Host.Headless where import Control.Concurrent.Chan (newChan, readChan) -import Control.Monad (unless) import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow) import Control.Monad.Fix (MonadFix, fix) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Primitive (PrimMonad) import Control.Monad.Ref (MonadRef, Ref, readRef) import Data.Dependent.Sum (DSum (..), (==>)) -import Data.Foldable (for_) +import Data.Foldable (for_, asum) import Data.Functor.Identity (Identity(..)) import Data.IORef (IORef, readIORef) -import Data.Maybe (catMaybes, fromMaybe) +import Data.Maybe (catMaybes) import Data.Traversable (for) import Reflex @@ -54,10 +53,11 @@ type MonadHeadlessApp t m = -- classes to interface the FRP network with the outside world. Useful for -- testing. Each headless network runs on its own spider timeline. runHeadlessApp - :: (forall t m. MonadHeadlessApp t m => m (Event t ())) + :: forall a + . (forall t m. MonadHeadlessApp t m => m (Event t a)) -- ^ The action to be run in the headless FRP network. The FRP network is -- closed at the first occurrence of the resulting 'Event'. - -> IO () + -> IO a runHeadlessApp guest = -- We are using the 'Spider' implementation of reflex. Running the host -- allows us to take actions on the FRP timeline. @@ -71,7 +71,7 @@ runHeadlessApp guest = -- Run the "guest" application, providing the appropriate context. We'll -- pure the result of the action, and a 'FireCommand' that will be used to -- trigger events. - (result, fc@(FireCommand fire)) <- do + (result :: Event t a, fc@(FireCommand fire)) <- do hostPerformEventT $ -- Allows the guest app to run -- 'performEvent', so that actions -- (e.g., IO actions) can be run when @@ -97,35 +97,46 @@ runHeadlessApp guest = shutdown <- subscribeEvent result -- When there is a subscriber to the post-build event, fire the event. - soa <- for mPostBuildTrigger $ \postBuildTrigger -> - fire [postBuildTrigger :=> Identity ()] $ isFiring shutdown + initialShutdownEventFirings :: Maybe [Maybe a] <- for mPostBuildTrigger $ \postBuildTrigger -> + fire [postBuildTrigger :=> Identity ()] $ sequence =<< readEvent shutdown + let shutdownImmediately = case initialShutdownEventFirings of + -- We didn't even fire postBuild because it wasn't subscribed + Nothing -> Nothing + -- Take the first Just, if there is one. Ideally, we should cut off + -- the event loop as soon as the firing happens, but Performable + -- doesn't currently give us an easy way to do that + Just firings -> asum firings - -- The main application loop. We wait for new events and fire those that - -- have subscribers. If we detect a shutdown request, the application - -- terminates. - unless (or (fromMaybe [] soa)) $ fix $ \loop -> do - -- Read the next event (blocking). - ers <- liftIO $ readChan events - stop <- do - -- Fire events that have subscribers. - fireEventTriggerRefs fc ers $ - -- Check if the shutdown 'Event' is firing. - isFiring shutdown - if or stop - then pure () - else loop + case shutdownImmediately of + Just exitResult -> pure exitResult + -- The main application loop. We wait for new events and fire those that + -- have subscribers. If we detect a shutdown request, the application + -- terminates. + Nothing -> fix $ \loop -> do + -- Read the next event (blocking). + ers <- liftIO $ readChan events + shutdownEventFirings :: [Maybe a] <- do + -- Fire events that have subscribers. + fireEventTriggerRefs fc ers $ + -- Check if the shutdown 'Event' is firing. + sequence =<< readEvent shutdown + let -- If the shutdown event fires multiple times, take the first one. + -- Ideally, we should cut off the event loop as soon as this fires, + -- but Performable doesn't currently give us an easy way to do that. + shutdownNow = asum shutdownEventFirings + case shutdownNow of + Just exitResult -> pure exitResult + Nothing -> loop where - isFiring ev = readEvent ev >>= \case - Nothing -> pure False - Just _ -> pure True -- Use the given 'FireCommand' to fire events that have subscribers -- and call the callback for the 'TriggerInvocation' of each. fireEventTriggerRefs - :: MonadIO m + :: forall b m t + . MonadIO m => FireCommand t m -> [DSum (EventTriggerRef t) TriggerInvocation] - -> ReadPhase m a - -> m [a] + -> ReadPhase m b + -> m [b] fireEventTriggerRefs (FireCommand fire) ers rcb = do mes <- liftIO $ for ers $ \(EventTriggerRef er :=> TriggerInvocation a _) -> do diff --git a/test/hlint.hs b/test/hlint.hs index 1d0e319d..ba309924 100644 --- a/test/hlint.hs +++ b/test/hlint.hs @@ -21,6 +21,7 @@ main = do , "--ignore=Use ." , "--ignore=Use unless" , "--ignore=Reduce duplication" + , "--ignore=Replace case with maybe" , "--cpp-define=USE_TEMPLATE_HASKELL" , "--cpp-define=DEBUG" , "--ignore=Use tuple-section" From 92bc51cd4f3ec292f1255d3c8d22b99cc8f9a758 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 12 Jan 2024 10:09:02 -0400 Subject: [PATCH 58/69] Remove unnecessary signature Older GHCs seem to want this `t` to be SpiderTimeline specifically --- src/Reflex/Host/Headless.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Reflex/Host/Headless.hs b/src/Reflex/Host/Headless.hs index 8da83329..13f66489 100644 --- a/src/Reflex/Host/Headless.hs +++ b/src/Reflex/Host/Headless.hs @@ -71,7 +71,7 @@ runHeadlessApp guest = -- Run the "guest" application, providing the appropriate context. We'll -- pure the result of the action, and a 'FireCommand' that will be used to -- trigger events. - (result :: Event t a, fc@(FireCommand fire)) <- do + (result, fc@(FireCommand fire)) <- do hostPerformEventT $ -- Allows the guest app to run -- 'performEvent', so that actions -- (e.g., IO actions) can be run when From e698e134ebfd73f2bcccf80670646e56b6b42041 Mon Sep 17 00:00:00 2001 From: Ali Abrar Date: Fri, 12 Jan 2024 09:54:40 -0500 Subject: [PATCH 59/69] Update reflex.cabal --- reflex.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex.cabal b/reflex.cabal index 5e817e62..1cc96e18 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.9.2.0 +Version: 0.9.3.0 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects. From 62c590202abf979a2768d9a3f9d036749d0f1aef Mon Sep 17 00:00:00 2001 From: endgame Date: Mon, 10 Jun 2024 18:18:54 +1000 Subject: [PATCH 60/69] Reflex.Network: improve haddocks * Replace reference to deprecated function with its replacement * Normalise comment formatting * Suggest how to flatten the result of `networkHold` --- src/Reflex/Network.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Reflex/Network.hs b/src/Reflex/Network.hs index 03348aee..8f914a82 100644 --- a/src/Reflex/Network.hs +++ b/src/Reflex/Network.hs @@ -22,7 +22,7 @@ import Reflex.PostBuild.Class -- | A 'Dynamic' "network": Takes a 'Dynamic' of network-creating actions and replaces the network whenever the 'Dynamic' updates. -- The returned Event of network results fires at post-build time and when the 'Dynamic' updates. -- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events, where the outer 'Event' fires --- when switching networks. Such an 'Event' would typically be flattened (via 'switchPromptly'). +-- when switching networks. Such an 'Event' would typically be flattened (via 'switchHoldPromptly'). networkView :: (NotReady t m, Adjustable t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a) networkView child = do postBuild <- getPostBuild @@ -30,8 +30,8 @@ networkView child = do snd <$> runWithReplace notReady newChild -- | Given an initial "network" and an 'Event' of network-creating actions, create a network that is recreated whenever the Event fires. --- The returned Dynamic of network results occurs when the Event does. --- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened. +-- The returned Dynamic of network results occurs when the Event does. +-- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened (via 'switchPromptlyDyn'). networkHold :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a) networkHold child0 newChild = do (result0, newResult) <- runWithReplace child0 newChild From 93ab198cf2e0d690ff99c6ad3e7ab4cf22931a75 Mon Sep 17 00:00:00 2001 From: Alexandre Esteves Date: Wed, 7 Aug 2024 14:07:17 +0100 Subject: [PATCH 61/69] Move unconditional dep outside of conditional --- reflex.cabal | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/reflex.cabal b/reflex.cabal index 1cc96e18..184e9a14 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -79,6 +79,7 @@ library constraints-extras >= 0.3 && < 0.5, containers >= 0.6 && < 0.7, data-default >= 0.5 && < 0.8, + dependent-sum >= 0.6 && < 0.8, dependent-map >= 0.3 && < 0.5, exceptions >= 0.10 && < 0.11, exception-transformers >= 0.4 && < 0.5, @@ -186,16 +187,12 @@ library if flag(use-template-haskell) cpp-options: -DUSE_TEMPLATE_HASKELL build-depends: - dependent-sum >= 0.6 && < 0.8, haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, template-haskell >= 2.9 && < 2.21 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell - else - build-depends: - dependent-sum >= 0.6 && < 0.8 if flag(fast-weak) && impl(ghcjs) cpp-options: -DGHCJS_FAST_WEAK From dec453385073d2a0a21045ea3db0dab8704025f1 Mon Sep 17 00:00:00 2001 From: ymeister <47071325+ymeister@users.noreply.github.com> Date: Sun, 22 Sep 2024 12:57:58 +0000 Subject: [PATCH 62/69] Add Matrix room link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 04e26e22..8177f220 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Reflex is a fully-deterministic, higher-order Functional Reactive Programming in * [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. * [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. * [Get started with Reflex](https://github.com/reflex-frp/reflex-platform) +* [#reflex-frp:matrix.org](https://matrix.to/#/#reflex-frp:matrix.org): Official Matrix room * [/r/reflexfrp](https://www.reddit.com/r/reflexfrp) * [irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4) From 90aef432c96cb1ef3828e4c52edaf9bda7fda114 Mon Sep 17 00:00:00 2001 From: Alexandre Esteves Date: Fri, 12 Apr 2024 00:09:47 +0100 Subject: [PATCH 63/69] Build with ghc 9.8 --- .github/workflows/haskell.yml | 2 +- ChangeLog.md | 4 ++++ reflex.cabal | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index dc3c98e9..518b69c9 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -6,7 +6,7 @@ jobs: build: strategy: matrix: - ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.7', '9.0.2', '9.2.5', '9.4.5', '9.6.1'] + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.7', '9.0.2', '9.2.5', '9.4.5', '9.6.1', '9.8.2'] os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} diff --git a/ChangeLog.md b/ChangeLog.md index fc98f9c5..41cee5a0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Revision history for reflex +## Unreleased + +* Add support for GHC 9.8 + ## 0.9.3.0 * Headless Host: Generalize to allow returning arbitrary types diff --git a/reflex.cabal b/reflex.cabal index 184e9a14..9cbe55bb 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -28,7 +28,7 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.7 || ==9.0.1 || ==9.2.5 || ==9.4.5 || ==9.6.1, + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.7 || ==9.0.1 || ==9.2.5 || ==9.4.5 || ==9.6.1 || ==9.8.2, GHCJS ==8.6 || ==8.10 flag use-reflex-optimizer @@ -71,7 +71,7 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.11 && < 4.19, + base >= 4.11 && < 4.20, bifunctors >= 5.2 && < 5.7, comonad >= 5.0.4 && < 5.1, commutative-semigroups >= 0.1 && <0.2, @@ -89,7 +89,7 @@ library mtl >= 2.1 && < 2.4, patch >= 0.0.7 && < 0.1, prim-uniq >= 0.1.0.1 && < 0.3, - primitive >= 0.5 && < 0.8, + primitive >= 0.5 && < 0.9, profunctors >= 5.3 && < 5.7, random >= 1.1 && < 1.3, ref-tf >= 0.4 && < 0.6, @@ -189,7 +189,7 @@ library build-depends: haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.21 + template-haskell >= 2.9 && < 2.22 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell From 1205bc76e76f7c7600dfb4d2945474b655974ff5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 26 Sep 2024 16:59:54 -0400 Subject: [PATCH 64/69] Bump patch in `release.nix` --- release.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.nix b/release.nix index dac89c9f..7cb4c507 100644 --- a/release.nix +++ b/release.nix @@ -66,8 +66,8 @@ let patch = self.callHackageDirect { pkg = "patch"; - ver = "0.0.8.2"; - sha256 = "160zqqhjg48fr3a33gffd82qm3728c8hwf8sn37pbpv82fw71rzg"; + ver = "0.0.8.3"; + sha256 = "054slcrlsdcs6azwph6v3vgsgk939ax7ax9xw76whywkrim20n1w"; } {}; }; }; From a2b637fcfaf63ab7cbd37f3cb3af9e9999292167 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 26 Sep 2024 17:25:16 -0400 Subject: [PATCH 65/69] Update cabal --- .github/workflows/haskell.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index 518b69c9..c20fe722 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -16,7 +16,7 @@ jobs: - uses: haskell/actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} - cabal-version: '3.10.1.0' + cabal-version: '3.10.3.0' - name: Cache uses: actions/cache@v3 env: From edadd04e265bc9ef9d4f558d95392af5307ee822 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 27 Sep 2024 11:16:31 -0400 Subject: [PATCH 66/69] Need to cabal update after cache unpack Thanks @fgaz! See also https://github.com/haskell-actions/setup?tab=readme-ov-file#model-cabal-workflow-with-caching --- .github/workflows/haskell.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index c20fe722..353bcd96 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -12,11 +12,14 @@ jobs: name: GHC ${{ matrix.ghc }} on ${{ matrix.os }} steps: + - uses: actions/checkout@v3 + - uses: haskell/actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} cabal-version: '3.10.3.0' + - name: Cache uses: actions/cache@v3 env: @@ -31,11 +34,16 @@ jobs: ${{ runner.os }} - name: Install dependencies - run: cabal build --only-dependencies --enable-tests --enable-benchmarks + run: | + cabal update + cabal build --only-dependencies --enable-tests --enable-benchmarks + - name: Build run: cabal build --enable-tests --enable-benchmarks all + - name: Run tests run: cabal test --enable-tests all + - if: matrix.ghc != '8.4.4' # docs aren't built on ghc 8.4.4 because some dependency docs don't build on older GHCs name: Build Docs From 4f78e3ba6a5f7f8e2abfa0b899dea0bfe2313361 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 27 Sep 2024 14:47:30 -0400 Subject: [PATCH 67/69] Minimal support for 9.10 PR #502 contains some other orthogonal cleanups. --- .github/workflows/haskell.yml | 2 +- ChangeLog.md | 2 +- reflex.cabal | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index 353bcd96..5a048b2a 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -6,7 +6,7 @@ jobs: build: strategy: matrix: - ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.7', '9.0.2', '9.2.5', '9.4.5', '9.6.1', '9.8.2'] + ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.7', '9.0.2', '9.2.5', '9.4.5', '9.6.1', '9.8.2', '9.10.1'] os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} diff --git a/ChangeLog.md b/ChangeLog.md index 41cee5a0..04db6ca4 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,7 +2,7 @@ ## Unreleased -* Add support for GHC 9.8 +* Add support for GHC 9.8 and 9.10 ## 0.9.3.0 diff --git a/reflex.cabal b/reflex.cabal index 9cbe55bb..554df59a 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -28,7 +28,7 @@ extra-source-files: ChangeLog.md tested-with: - GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.7 || ==9.0.1 || ==9.2.5 || ==9.4.5 || ==9.6.1 || ==9.8.2, + GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.7 || ==9.0.1 || ==9.2.5 || ==9.4.5 || ==9.6.1 || ==9.8.2 || ==9.10.1, GHCJS ==8.6 || ==8.10 flag use-reflex-optimizer @@ -71,25 +71,25 @@ library hs-source-dirs: src build-depends: MemoTrie == 0.6.*, - base >= 4.11 && < 4.20, + base >= 4.11 && < 4.21, bifunctors >= 5.2 && < 5.7, comonad >= 5.0.4 && < 5.1, - commutative-semigroups >= 0.1 && <0.2, - constraints >= 0.10 && <0.14, + commutative-semigroups >= 0.1 && <0.3, + constraints >= 0.10 && <0.15, constraints-extras >= 0.3 && < 0.5, - containers >= 0.6 && < 0.7, + containers >= 0.6 && < 0.8, data-default >= 0.5 && < 0.8, - dependent-sum >= 0.6 && < 0.8, dependent-map >= 0.3 && < 0.5, + dependent-sum >= 0.6 && < 0.8, exceptions >= 0.10 && < 0.11, exception-transformers >= 0.4 && < 0.5, - lens >= 4.7 && < 5.3, + lens >= 4.7 && < 5.4, mmorph >= 1.0 && < 1.3, monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.4, patch >= 0.0.7 && < 0.1, prim-uniq >= 0.1.0.1 && < 0.3, - primitive >= 0.5 && < 0.9, + primitive >= 0.5 && < 0.10, profunctors >= 5.3 && < 5.7, random >= 1.1 && < 1.3, ref-tf >= 0.4 && < 0.6, @@ -189,7 +189,7 @@ library build-depends: haskell-src-exts >= 1.16 && < 1.24, haskell-src-meta >= 0.6 && < 0.9, - template-haskell >= 2.9 && < 2.22 + template-haskell >= 2.9 && < 2.23 exposed-modules: Reflex.Dynamic.TH other-extensions: TemplateHaskell From 8642d588952a16c2ff6145fb89aa4be4599b4743 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 1 Oct 2024 11:23:31 -0400 Subject: [PATCH 68/69] Fix a loopy superclass solution This is deprecated in at least 9.8, and removed in 9.10. Co-Authored-By: ymeister <47071325+ymeister@users.noreply.github.com> --- src/Reflex/PerformEvent/Base.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Reflex/PerformEvent/Base.hs b/src/Reflex/PerformEvent/Base.hs index 152460f9..9fae30ae 100644 --- a/src/Reflex/PerformEvent/Base.hs +++ b/src/Reflex/PerformEvent/Base.hs @@ -73,7 +73,7 @@ instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m type PrimState (PerformEventT t m) = PrimState (HostFrame t) primitive = PerformEventT . lift . primitive -instance (ReflexHost t, Ref m ~ Ref IO) => PerformEvent t (PerformEventT t m) where +instance (Monad (HostFrame t), ReflexHost t, Ref m ~ Ref IO) => PerformEvent t (PerformEventT t m) where type Performable (PerformEventT t m) = HostFrame t {-# INLINABLE performEvent_ #-} performEvent_ = PerformEventT . requesting_ From e21f856bcbb1a04a7144d82a81771669a0e4e072 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 3 Oct 2024 18:31:50 -0400 Subject: [PATCH 69/69] Release 0.9.3.1 --- ChangeLog.md | 2 +- reflex.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 04db6ca4..0974a225 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,6 @@ # Revision history for reflex -## Unreleased +## 0.9.3.1 * Add support for GHC 9.8 and 9.10 diff --git a/reflex.cabal b/reflex.cabal index 554df59a..712cc639 100644 --- a/reflex.cabal +++ b/reflex.cabal @@ -1,5 +1,5 @@ Name: reflex -Version: 0.9.3.0 +Version: 0.9.3.1 Synopsis: Higher-order Functional Reactive Programming Description: Interactive programs without callbacks or side-effects.