packages feed

rhine 1.4.0.1 → 1.5

raw patch · 5 files changed

+103/−35 lines, 5 filesdep +foldable1-classes-compatdep ~QuickCheckdep ~automatondep ~base

Dependencies added: foldable1-classes-compat

Dependency ranges changed: QuickCheck, automaton, base, tasty-quickcheck

Files

ChangeLog.md view
@@ -1,5 +1,10 @@ # Revision history for rhine +## 1.5++* Added `forever` utility for recursion in `ClSFExcept`+* Support GHC 9.10+ ## 1.4  * Add `Profunctor` instance for `ResamplingBuffer`
rhine.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: rhine-version: 1.4.0.1+version: 1.5 synopsis: Functional Reactive Programming with type-level clocks description:   Rhine is a library for synchronous and asynchronous Functional Reactive Programming (FRP).@@ -35,6 +35,7 @@   ghc ==9.4.7   ghc ==9.6.4   ghc ==9.8.2+  ghc ==9.10.1  source-repository head   type: git@@ -43,12 +44,12 @@ source-repository this   type: git   location: https://github.com/turion/rhine.git-  tag: v1.4+  tag: v1.5  common opts   build-depends:-    automaton ^>=1.4,-    base >=4.14 && <4.20,+    automaton ^>=1.5,+    base >=4.16 && <4.21,     monad-schedule ^>=0.2,     mtl >=2.2 && <2.4,     selective ^>=0.7,@@ -82,7 +83,7 @@  common test-deps   build-depends:-    QuickCheck ^>=2.14,+    QuickCheck >=2.14 && <2.16,     tasty >=1.4 && <1.6,     tasty-hunit ^>=0.10,     tasty-quickcheck ^>=0.10,@@ -138,6 +139,7 @@   other-modules:     FRP.Rhine.ClSF.Except.Util     FRP.Rhine.ClSF.Random.Util+    FRP.Rhine.Schedule.Internal    -- LANGUAGE extensions used by modules in this package.   -- other-extensions:@@ -146,6 +148,7 @@     MonadRandom >=0.5,     containers >=0.5,     deepseq >=1.4,+    foldable1-classes-compat ^>=0.1,     free >=5.1,     mmorph ^>=1.2,     profunctors ^>=5.6,
src/FRP/Rhine/ClSF/Except.hs view
@@ -16,6 +16,7 @@   exceptS,   runAutomatonExcept,   currentInput,+  forever, ) where 
src/FRP/Rhine/Schedule.hs view
@@ -20,22 +20,16 @@ import Control.Arrow import Data.List.NonEmpty as N --- transformers-import Control.Monad.Trans.Reader- -- monad-schedule import Control.Monad.Schedule.Class  -- automaton import Data.Automaton-import Data.Automaton.Final (getFinal, toFinal)-import Data.Stream-import Data.Stream.Final qualified as StreamFinal import Data.Stream.Optimized (OptimizedStreamT (..), toStreamT)-import Data.Stream.Result  -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Schedule.Internal  -- * Scheduling @@ -48,35 +42,15 @@ scheduleList automatons0 =   Automaton $     Stateful $-      StreamT-        { state = (getFinal . toFinal <$> automatons0, [])-        , step = \(automatons, running) -> ReaderT $ \a -> do-            let bsAndConts = flip (runReaderT . StreamFinal.getFinal) a <$> automatons-            (done, running') <- schedule (N.head bsAndConts :| N.tail bsAndConts ++ running)-            return $ Result (resultState <$> done, running') $ output <$> done-        }+      scheduleStreams' $+        toStreamT . getAutomaton <$> automatons0  {- | Run two automata concurrently.  Whenever one automaton returns a value, it is returned.--This is similar to 'scheduleList', but more efficient. -} schedulePair :: (Monad m, MonadSchedule m) => Automaton m a b -> Automaton m a b -> Automaton m a b-schedulePair (Automaton automatonL) (Automaton automatonR) = Automaton $! Stateful $! scheduleStreams (toStreamT automatonL) (toStreamT automatonR)-  where-    scheduleStreams :: (Monad m, MonadSchedule m) => StreamT m b -> StreamT m b -> StreamT m b-    scheduleStreams (StreamT stateL0 stepL) (StreamT stateR0 stepR) =-      StreamT-        { state = (stepL stateL0, stepR stateR0)-        , step-        }-      where-        step (runningL, runningR) = do-          result <- race runningL runningR-          case result of-            Left (Result stateL' b, runningR') -> return $ Result (stepL stateL', runningR') b-            Right (runningL', Result stateR' b) -> return $ Result (runningL', stepR stateR') b+schedulePair automatonL automatonR = concatS $ fmap toList $ scheduleList $ automatonL :| [automatonR]  -- | Run two running clocks concurrently. runningSchedule ::
+ src/FRP/Rhine/Schedule/Internal.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE ExistentialQuantification #-}++module FRP.Rhine.Schedule.Internal where++-- base+import Control.Arrow+import Data.Function ((&))+import Data.Functor ((<&>))+import Data.Functor.Compose (Compose (..))+import Data.Kind (Type)+import Data.List.NonEmpty as N++-- foldable1-classes-compat+import Data.Foldable1 (Foldable1 (foldrMap1))++-- sop-core+import Data.SOP (HCollapse (hcollapse), HSequence (htraverse'), I (..), K (K), NP (..), NS (..), SListI, apInjs_NP, hliftA, hzipWith, unI)++-- monad-schedule+import Control.Monad.Schedule.Class++-- automaton+import Data.Stream hiding (concatS)+import Data.Stream.Result++-- | One step of a stream, with the state type argument going last, so it is usable with sop-core.+newtype Step m b state = Step {getStep :: ResultStateT state m b}++-- | The result of a stream, with the type arguments swapped, so it's usable with sop-core+newtype RunningResult b state = RunningResult {getRunningResult :: Result state b}++-- | Transform an n-ary product of at least one type into a nonempty list of all its content.+apInjs_NPNonEmpty :: (SListI xs) => NP f (x ': xs) -> NonEmpty (NS f (x ': xs))+apInjs_NPNonEmpty (fx :* fxs) = Z fx :| (S <$> apInjs_NP fxs)++-- | A nonempty list of 'StreamT's, unzipped into their states and their steps.+data Streams m b = forall state (states :: [Type]).+  (SListI states) =>+  Streams+  { states :: NP I (state ': states)+  , steps :: NP (Step m b) (state ': states)+  }++-- | Run 'Streams' concurrently by scheduling them in 'MonadSchedule'.+scheduleStreams :: (MonadSchedule m, Functor m, Applicative m) => Streams m b -> StreamT m (NonEmpty b)+scheduleStreams Streams {states, steps} =+  StreamT+    { state = (apInjs_NPNonEmpty states, []) -- All the initial states and no currently running continuations+    , step =+        -- Some streams have not started yet, or just finished their step. Others are still running.+        \(restingStates, runningStreams) ->+          -- Start all currently not running streams by zipping each with its step+          fmap (htraverse' getCompose . hzipWith (\Step {getStep} -> Compose . fmap RunningResult . getResultStateT getStep . unI) steps) restingStates+            -- Append all already running states to the freshly started ones+            & flip appendList runningStreams+            -- Schedule all running streams concurrently+            & schedule+            -- Separate into finished streams and still running streams+            & fmap+              ( \(finished, running) ->+                  let finishedStates = finished <&> (hliftA (getRunningResult >>> resultState >>> I))+                      outputs =+                        finished+                          <&> (hliftA (getRunningResult >>> output >>> K) >>> hcollapse)+                   in Result (finishedStates, running) outputs+              )+    }++-- | Run a nonempty list of streams concurrently.+scheduleStreams' :: (MonadSchedule m, Applicative m) => NonEmpty (StreamT m b) -> StreamT m (NonEmpty b)+scheduleStreams' = scheduleStreams . foldrMap1 buildStreams consStreams+  where+    buildStreams :: StreamT m b -> Streams m b+    buildStreams StreamT {state, step} =+      Streams+        { states = I state :* Nil+        , steps = Step (ResultStateT step) :* Nil+        }++    consStreams :: StreamT m b -> Streams m b -> Streams m b+    consStreams StreamT {state, step} Streams {states, steps} =+      Streams+        { states = I state :* states+        , steps = Step (ResultStateT step) :* steps+        }