packages feed

reactive-banana 0.4.3.2 → 0.5.0.0

raw patch · 17 files changed

+1913/−1288 lines, 17 filesdep +fclabelsdep +hashabledep +unordered-containersPVP ok

version bump matches the API change (PVP)

Dependencies added: fclabels, hashable, unordered-containers

API changes (from Hackage documentation)

- Reactive.Banana: type Behavior = Behavior PushIO
- Reactive.Banana: type Discrete = Discrete PushIO
- Reactive.Banana: type Event = Event PushIO
- Reactive.Banana.Implementation: actuate :: EventNetwork -> IO ()
- Reactive.Banana.Implementation: compile :: NetworkDescription () -> IO EventNetwork
- Reactive.Banana.Implementation: data EventNetwork
- Reactive.Banana.Implementation: data NetworkDescription a
- Reactive.Banana.Implementation: data PushIO
- Reactive.Banana.Implementation: fromAddHandler :: AddHandler a -> NetworkDescription (Event PushIO a)
- Reactive.Banana.Implementation: fromPoll :: IO a -> NetworkDescription (Behavior PushIO a)
- Reactive.Banana.Implementation: instance Applicative NetworkDescription
- Reactive.Banana.Implementation: instance Functor NetworkDescription
- Reactive.Banana.Implementation: instance Monad NetworkDescription
- Reactive.Banana.Implementation: instance MonadFix NetworkDescription
- Reactive.Banana.Implementation: instance MonadIO NetworkDescription
- Reactive.Banana.Implementation: instance Typeable EventNetwork
- Reactive.Banana.Implementation: interpret :: (Event PushIO a -> Event PushIO b) -> [a] -> IO [[b]]
- Reactive.Banana.Implementation: interpretAsHandler :: (Event PushIO a -> Event PushIO b) -> AddHandler a -> AddHandler b
- Reactive.Banana.Implementation: liftIO :: MonadIO m => forall a. IO a -> m a
- Reactive.Banana.Implementation: liftIOLater :: IO () -> NetworkDescription ()
- Reactive.Banana.Implementation: newAddHandler :: IO (AddHandler a, a -> IO ())
- Reactive.Banana.Implementation: pause :: EventNetwork -> IO ()
- Reactive.Banana.Implementation: reactimate :: Event PushIO (IO ()) -> NetworkDescription ()
- Reactive.Banana.Implementation: type AddHandler a = (a -> IO ()) -> IO (IO ())
- Reactive.Banana.Incremental: accumD :: FRP f => a -> Event f (a -> a) -> Discrete f a
- Reactive.Banana.Incremental: applyD :: FRP f => Discrete f (a -> b) -> Event f a -> Event f b
- Reactive.Banana.Incremental: changes :: Discrete f a -> Event f a
- Reactive.Banana.Incremental: data Discrete f a
- Reactive.Banana.Incremental: initial :: Discrete f a -> a
- Reactive.Banana.Incremental: instance FRP f => Applicative (Discrete f)
- Reactive.Banana.Incremental: instance FRP f => Apply (Discrete f) (Event f)
- Reactive.Banana.Incremental: instance FRP f => Functor (Discrete f)
- Reactive.Banana.Incremental: stepperD :: FRP f => a -> Event f a -> Discrete f a
- Reactive.Banana.Incremental: value :: Discrete f a -> Behavior f a
- Reactive.Banana.Model: (<@) :: Apply f g => f a -> g b -> g a
- Reactive.Banana.Model: (<@>) :: Apply f g => f (a -> b) -> g a -> g b
- Reactive.Banana.Model: accumB :: FRP f => a -> Event f (a -> a) -> Behavior f a
- Reactive.Banana.Model: accumE :: FRP f => a -> Event f (a -> a) -> Event f a
- Reactive.Banana.Model: apply :: FRP f => Behavior f (a -> b) -> Event f a -> Event f b
- Reactive.Banana.Model: class (Functor f, Functor g) => Apply f g where f <@ g = (const <$> f) <@> g
- Reactive.Banana.Model: class (Functor (Event f), Functor (Behavior f), Applicative (Behavior f)) => FRP f where filterE p = filterApply (pure p) filterApply bp = fmap snd . filterE fst . apply ((\ p a -> (p a, a)) <$> bp) accumB acc = stepper acc . accumE acc stepper acc = accumB acc . fmap const
- Reactive.Banana.Model: data Model
- Reactive.Banana.Model: filterApply :: FRP f => Behavior f (a -> Bool) -> Event f a -> Event f a
- Reactive.Banana.Model: filterE :: FRP f => (a -> Bool) -> Event f a -> Event f a
- Reactive.Banana.Model: filterJust :: FRP f => Event f (Maybe a) -> Event f a
- Reactive.Banana.Model: instance Applicative (Behavior Model)
- Reactive.Banana.Model: instance FRP Model
- Reactive.Banana.Model: instance FRP f => Apply (Behavior f) (Event f)
- Reactive.Banana.Model: instance FRP f => Monoid (Event f a)
- Reactive.Banana.Model: instance Functor (Behavior Model)
- Reactive.Banana.Model: instance Functor (Event Model)
- Reactive.Banana.Model: interpret :: (Event Model a -> Event Model b) -> [a] -> [[b]]
- Reactive.Banana.Model: interpretTime :: (Event Model a -> Event Model b) -> [(Time, a)] -> [(Time, b)]
- Reactive.Banana.Model: mapAccum :: FRP f => acc -> Event f (acc -> (x, acc)) -> (Event f x, Behavior f acc)
- Reactive.Banana.Model: never :: FRP f => Event f a
- Reactive.Banana.Model: stepper :: FRP f => a -> Event f a -> Behavior f a
- Reactive.Banana.Model: type Time = Double
- Reactive.Banana.Model: union :: FRP f => Event f a -> Event f a -> Event f a
- Reactive.Banana.Model: whenE :: FRP f => Behavior f Bool -> Event f a -> Event f a
- Reactive.Banana.Tests: accumBvsE :: FRP f => Event f Dummy -> Event f Int
- Reactive.Banana.Tests: add1 :: (Functor f, Num b) => f b -> f b
- Reactive.Banana.Tests: counter :: (Num b, FRP f) => Event f a -> Event f b
- Reactive.Banana.Tests: decrease :: FRP f => Event f Dummy -> Event f Int
- Reactive.Banana.Tests: double :: FRP f => Event f a -> Event f a
- Reactive.Banana.Tests: filtering :: (Num a, Ord a, FRP f) => Event f a -> Event f a
- Reactive.Banana.Tests: matchesModel :: (Typeable a, Show b, Eq b) => (forall f. FRP f => Event f a -> Event f b) -> [a] -> IO Bool
- Reactive.Banana.Tests: sharing :: (Num a, Ord a, FRP f) => Event f a -> Event f a
- Reactive.Banana.Tests: test :: (Event PushIO Int -> Event PushIO b) -> IO [[b]]
- Reactive.Banana.Tests: testSuite :: IO ()
- Reactive.Banana.Tests: type Dummy = Int
+ Reactive.Banana.Combinators: (<@) :: Apply f g => f a -> g b -> g a
+ Reactive.Banana.Combinators: (<@>) :: Apply f g => f (a -> b) -> g a -> g b
+ Reactive.Banana.Combinators: B :: Behavior Expr a -> Behavior t a
+ Reactive.Banana.Combinators: E :: Event Expr [a] -> Event t a
+ Reactive.Banana.Combinators: accumB :: a -> Event t (a -> a) -> Behavior t a
+ Reactive.Banana.Combinators: accumE :: a -> Event t (a -> a) -> Event t a
+ Reactive.Banana.Combinators: apply :: Behavior t (a -> b) -> Event t a -> Event t b
+ Reactive.Banana.Combinators: calm :: Event t a -> Event t a
+ Reactive.Banana.Combinators: class (Functor f, Functor g) => Apply f g where f <@ g = (const <$> f) <@> g
+ Reactive.Banana.Combinators: collect :: Event t a -> Event t [a]
+ Reactive.Banana.Combinators: filterApply :: Behavior t (a -> Bool) -> Event t a -> Event t a
+ Reactive.Banana.Combinators: filterE :: (a -> Bool) -> Event t a -> Event t a
+ Reactive.Banana.Combinators: filterJust :: Event t (Maybe a) -> Event t a
+ Reactive.Banana.Combinators: instance Applicative (Behavior t)
+ Reactive.Banana.Combinators: instance Apply (Behavior t) (Event t)
+ Reactive.Banana.Combinators: instance Functor (Behavior t)
+ Reactive.Banana.Combinators: instance Functor (Event t)
+ Reactive.Banana.Combinators: instance Monoid (Event t (a -> a))
+ Reactive.Banana.Combinators: interpretModel :: (forall t. Event t a -> Event t b) -> [[a]] -> IO [[b]]
+ Reactive.Banana.Combinators: interpretPushGraph :: (forall t. Event t a -> Event t b) -> [[a]] -> IO [[b]]
+ Reactive.Banana.Combinators: mapAccum :: acc -> Event t (acc -> (x, acc)) -> (Event t x, Behavior t acc)
+ Reactive.Banana.Combinators: never :: Event t a
+ Reactive.Banana.Combinators: newtype Behavior t a
+ Reactive.Banana.Combinators: newtype Event t a
+ Reactive.Banana.Combinators: spill :: Event t [a] -> Event t a
+ Reactive.Banana.Combinators: split :: Event t (Either a b) -> (Event t a, Event t b)
+ Reactive.Banana.Combinators: stepper :: a -> Event t a -> Behavior t a
+ Reactive.Banana.Combinators: unB :: Behavior t a -> Behavior Expr a
+ Reactive.Banana.Combinators: unE :: Event t a -> Event Expr [a]
+ Reactive.Banana.Combinators: union :: Event t a -> Event t a -> Event t a
+ Reactive.Banana.Combinators: unionWith :: (a -> a -> a) -> Event t a -> Event t a -> Event t a
+ Reactive.Banana.Combinators: whenE :: Behavior t Bool -> Event t a -> Event t a
+ Reactive.Banana.Experimental.Calm: (<@) :: Apply f g => f a -> g b -> g a
+ Reactive.Banana.Experimental.Calm: (<@>) :: Apply f g => f (a -> b) -> g a -> g b
+ Reactive.Banana.Experimental.Calm: accumB :: a -> Event t (a -> a) -> Behavior t a
+ Reactive.Banana.Experimental.Calm: accumE :: a -> Event t (a -> a) -> Event t a
+ Reactive.Banana.Experimental.Calm: apply :: Behavior t (a -> b) -> Event t a -> Event t b
+ Reactive.Banana.Experimental.Calm: class (Functor f, Functor g) => Apply f g where f <@ g = (const <$> f) <@> g
+ Reactive.Banana.Experimental.Calm: collect :: Event t a -> Event t [a]
+ Reactive.Banana.Experimental.Calm: data Event t a
+ Reactive.Banana.Experimental.Calm: filterE :: (a -> Bool) -> Event t a -> Event t a
+ Reactive.Banana.Experimental.Calm: filterJust :: Event t (Maybe a) -> Event t a
+ Reactive.Banana.Experimental.Calm: fromCalm :: Event t a -> Event t a
+ Reactive.Banana.Experimental.Calm: instance Apply (Behavior t) (Event t)
+ Reactive.Banana.Experimental.Calm: instance Functor (Event t)
+ Reactive.Banana.Experimental.Calm: interpretModel :: (forall t. Event t a -> Event t b) -> [a] -> IO [Maybe b]
+ Reactive.Banana.Experimental.Calm: interpretPushGraph :: (forall t. Event t a -> Event t b) -> [a] -> IO [Maybe b]
+ Reactive.Banana.Experimental.Calm: mapAccum :: acc -> Event t (acc -> (x, acc)) -> (Event t x, Behavior t acc)
+ Reactive.Banana.Experimental.Calm: never :: Event t a
+ Reactive.Banana.Experimental.Calm: stepper :: a -> Event t a -> Behavior t a
+ Reactive.Banana.Experimental.Calm: type Behavior t = Behavior t
+ Reactive.Banana.Experimental.Calm: unionWith :: (a -> a -> a) -> Event t a -> Event t a -> Event t a
+ Reactive.Banana.Frameworks: actuate :: EventNetwork -> IO ()
+ Reactive.Banana.Frameworks: changes :: Behavior t a -> NetworkDescription t (Event t a)
+ Reactive.Banana.Frameworks: compile :: (forall t. NetworkDescription t ()) -> IO EventNetwork
+ Reactive.Banana.Frameworks: data EventNetwork
+ Reactive.Banana.Frameworks: data NetworkDescription t a
+ Reactive.Banana.Frameworks: fromAddHandler :: AddHandler a -> NetworkDescription t (Event t a)
+ Reactive.Banana.Frameworks: fromChanges :: a -> AddHandler a -> NetworkDescription t (Behavior t a)
+ Reactive.Banana.Frameworks: fromPoll :: IO a -> NetworkDescription t (Behavior t a)
+ Reactive.Banana.Frameworks: initial :: Behavior t a -> NetworkDescription t a
+ Reactive.Banana.Frameworks: instance Applicative (NetworkDescription t)
+ Reactive.Banana.Frameworks: instance Functor (NetworkDescription t)
+ Reactive.Banana.Frameworks: instance Monad (NetworkDescription t)
+ Reactive.Banana.Frameworks: instance MonadFix (NetworkDescription t)
+ Reactive.Banana.Frameworks: instance MonadIO (NetworkDescription t)
+ Reactive.Banana.Frameworks: instance Typeable EventNetwork
+ Reactive.Banana.Frameworks: interpret :: (forall t. Event t a -> Event t b) -> [a] -> IO [[b]]
+ Reactive.Banana.Frameworks: interpretAsHandler :: (forall t. Event t a -> Event t b) -> AddHandler a -> AddHandler b
+ Reactive.Banana.Frameworks: liftIO :: MonadIO m => forall a. IO a -> m a
+ Reactive.Banana.Frameworks: liftIOLater :: IO () -> NetworkDescription t ()
+ Reactive.Banana.Frameworks: newAddHandler :: IO (AddHandler a, a -> IO ())
+ Reactive.Banana.Frameworks: newEvent :: NetworkDescription t (Event t a, a -> IO ())
+ Reactive.Banana.Frameworks: pause :: EventNetwork -> IO ()
+ Reactive.Banana.Frameworks: reactimate :: Event t (IO ()) -> NetworkDescription t ()
+ Reactive.Banana.Frameworks: type AddHandler a = (a -> IO ()) -> IO (IO ())
+ Reactive.Banana.Internal.Model: interpretModel :: (Event Expr a -> Event Expr b) -> Event a -> IO (Event b)

Files

doc/examples/ActuatePause.hs view
@@ -11,7 +11,7 @@ import Debug.Trace import Data.IORef -import Reactive.Banana as R+import Reactive.Banana   main :: IO ()
doc/examples/SlotMachine.hs view
@@ -3,6 +3,8 @@          Example: Slot machine ------------------------------------------------------------------------------}+{-# LANGUAGE ScopedTypeVariables #-} -- allows "forall t. NetworkDescription t"+ import Control.Monad (when) import Data.Maybe (isJust, fromJust) import Data.List (nub)@@ -18,7 +20,7 @@ main = do     displayHelpMessage     sources <- makeSources-    network <- setupNetwork sources+    network <- compile $ setupNetwork sources     actuate network     eventLoop sources @@ -82,9 +84,8 @@   -- Set up the program logic in terms of events and behaviors.-setupNetwork :: (EventSource (), EventSource ()) -> IO EventNetwork-setupNetwork (escoin,esplay) = compile $ do-+setupNetwork :: forall t. (EventSource (), EventSource ()) -> NetworkDescription t ()+setupNetwork (escoin,esplay) = do     -- initial random number generator     initialStdGen <- liftIO $ newStdGen @@ -99,8 +100,8 @@         -- The  ecoin      event adds a coin to the credits         -- The  edoesplay  event removes money         -- The  ewin       event adds credits because the player has won-        bcredits :: Behavior Money-        ecredits :: Event Money+        bcredits :: Behavior t Money+        ecredits :: Event t Money         (ecredits, bcredits) = mapAccum 0 . fmap (\f x -> (f x,f x)) $             ((addCredit <$ ecoin)             `union` (removeCredit <$ edoesplay)@@ -113,20 +114,20 @@         addWin Triple = (+20)                  -- Event: does the player have enough money to play the game?-        emayplay :: Event Bool+        emayplay :: Event t Bool         emayplay = apply ((\credits _ -> credits > 0) <$> bcredits) eplay                  -- Event: player has enough coins and plays-        edoesplay :: Event ()+        edoesplay :: Event t ()         edoesplay = () <$ filterE id  emayplay         -- Event: event that fires when the player doesn't have enough money-        edenied   :: Event ()+        edenied   :: Event t ()         edenied   = () <$ filterE not emayplay                           -- State: random number generator-        bstdgen :: Behavior StdGen-        eroll   :: Event Reels+        bstdgen :: Behavior t StdGen+        eroll   :: Event t Reels         -- accumulate the random number generator while rolling the reels         (eroll, bstdgen) = mapAccum initialStdGen (roll <$> edoesplay)         @@ -140,7 +141,7 @@             (z3,gen3) = random gen2                  -- Event: it's a win!-        ewin :: Event Win+        ewin :: Event t Win         ewin = fmap fromJust $ filterE isJust $ fmap checkWin eroll         checkWin (z1,z2,z3)             | length (nub [z1,z2,z3]) == 1 = Just Triple
reactive-banana.cabal view
@@ -1,32 +1,18 @@ Name:                reactive-banana-Version:             0.4.3.2-Synopsis:            Small but solid library for-                     functional reactive programming (FRP).+Version:             0.5.0.0+Synopsis:            Practical library for functional reactive programming (FRP). Description:         -    A small but solid library for functional reactive programming (FRP).-    .-    The current focus of this library is to implement-    a subset of the semantic model for functional reactive programming-    pioneered by Conal Elliott.-    .-    Moreover, this library can hooked into /any/-    existing event-based framework.-    It is especially useful in conjunction with existing-    GUI frameworks like @wxHaskell@ or @gtk2hs@.+    Reactive-banana is a practical library for Functional Reactive Programming (FRP).     .-    This also means that your code can be a mix of FRP and imperative parts.-    Bored of programming imperative GUIs? Write some parts with FRP!-    Don't know how to express something with FRP?-    Switch back to imperative style!+    FRP offers an elegant and concise way to express interactive programs such as graphical user interfaces, animations, computer music or robot controllers. Thus, the reactive-banana library promises to avoid the spagethetti code commonly used in traditional GUI technologies.     .-    In the spectrum of possible FRP implementations,-    this one features simple semantics but modest expressivity.-    Predicting space & time usage should be easy.+    See the project homepage <http://haskell.org/haskellwiki/Reactive-banana>+    for a more detailed introduction and features.     .     Stability forecast:-    Known inefficiencies that will be addressed.     No semantic bugs expected.-    Significant API changes are likely in future versions.+    Significant API changes are likely in future versions,+    though the main interface is beginning to stabilize.  Homepage:            http://haskell.org/haskellwiki/Reactive-banana License:             BSD3@@ -43,7 +29,8 @@  Library     hs-source-dirs:     src-    extensions:         TypeFamilies, FlexibleContexts,+    extensions:+                        TypeFamilies, FlexibleContexts,                         FlexibleInstances, EmptyDataDecls,                         GADTs, BangPatterns, TupleSections,                         Rank2Types, NoMonomorphismRestriction,@@ -52,13 +39,21 @@         base >= 4.2 && < 5, containers >= 0.3 && < 0.5,         transformers == 0.2.*,         QuickCheck >= 1.2 && < 2.5,-        vault == 0.1.*-    exposed-modules:    Reactive.Banana,-                        Reactive.Banana.Incremental,-                        Reactive.Banana.Model,-                        Reactive.Banana.Implementation,+        vault == 0.1.*, unordered-containers == 0.2.*, hashable == 1.1.*,+        fclabels == 1.1.*+    exposed-modules:+                        Reactive.Banana,+                        Reactive.Banana.Combinators,+                        Reactive.Banana.Frameworks,+                        Reactive.Banana.Experimental.Calm,+                        Reactive.Banana.Internal.Model+    other-modules:+                        Reactive.Banana.Internal.AST,+                        Reactive.Banana.Internal.InputOutput,+                        Reactive.Banana.Internal.PushGraph,+                        Reactive.Banana.Internal.TotalOrder,                         Reactive.Banana.Tests-    other-modules:      Reactive.Banana.PushIO+  Source-repository head     type:               git
src/Reactive/Banana.hs view
@@ -5,19 +5,9 @@ ------------------------------------------------------------------------------}  module Reactive.Banana (-    module Reactive.Banana.Incremental,-    module Reactive.Banana.Model,-    module Reactive.Banana.Implementation,--    Event, Behavior, Discrete,+    module Reactive.Banana.Combinators,+    module Reactive.Banana.Frameworks,     ) where -import Reactive.Banana.Incremental hiding (Discrete)-import qualified Reactive.Banana.Incremental as Polymorph-import Reactive.Banana.Model hiding (interpret, Event, Behavior)-import qualified Reactive.Banana.Model as Polymorph-import Reactive.Banana.Implementation--type Event    = Polymorph.Event PushIO-type Behavior = Polymorph.Behavior PushIO-type Discrete = Polymorph.Discrete PushIO+import Reactive.Banana.Combinators+import Reactive.Banana.Frameworks
+ src/Reactive/Banana/Combinators.hs view
@@ -0,0 +1,318 @@+{-----------------------------------------------------------------------------+    Reactive Banana+------------------------------------------------------------------------------}+{-# LANGUAGE Rank2Types, MultiParamTypeClasses, TupleSections, FlexibleInstances #-}++module Reactive.Banana.Combinators (+    -- * Synopsis+    -- | Combinators for building event graphs.+    +    -- * Introduction+    -- $intro1+    Event(..), Behavior(..),+    -- $intro2+    interpretModel, interpretPushGraph,+    +    -- * Core Combinators+    module Control.Applicative,+    module Data.Monoid,+    never, union, filterE, collect, spill, accumE,+    apply, stepper,+    -- $classes+    +    -- * Derived Combinators+    -- ** Filtering+    filterJust, filterApply, whenE, split,+    -- ** Accumulation+    -- $Accumulation.+    accumB, mapAccum,+    -- ** Simultaneous event occurrences+    calm, unionWith,+    -- ** Apply class+    Apply(..),+    ) where++import Control.Applicative+import Control.Monad++import Data.Maybe (isJust)+import Data.Monoid (Monoid(..))++import Reactive.Banana.Internal.InputOutput+import qualified Reactive.Banana.Internal.AST as AST+import qualified Reactive.Banana.Internal.Model as Model+import Reactive.Banana.Internal.PushGraph++{-----------------------------------------------------------------------------+    Introduction+------------------------------------------------------------------------------}+{-$intro1++At its core, Functional Reactive Programming (FRP) is about two+data types 'Event' and 'Behavior' and the various ways to combine them.++-}++{-| @Event t a@ represents a stream of events as they occur in time.+Semantically, you can think of @Event t a@ as an infinite list of values+that are tagged with their corresponding time of occurence,++> type Event t a = [(Time,a)]+-}+newtype Event t a = E { unE :: AST.Event AST.Expr [a] }+    -- ^ (Constructor exported for internal use only.)++{-| @Behavior t a@ represents a value that varies in time. Think of it as++> type Behavior t a = Time -> a+-}+newtype Behavior t a = B { unB :: AST.Behavior AST.Expr a }+    -- ^ (Constructor exported for internal use only.)++{-$intro2++As you can see, both types seem to have a superfluous parameter @t@.+The library uses it to rule out certain gross inefficiencies,+in particular in connection with dynamic event switching.+For basic stuff, you can completely ignore it,+except of course for the fact that it will annoy you in your type signatures.++While the type synonyms mentioned above are the way you should think about+'Behavior' and 'Event', they are a bit vague for formal manipulation.+To remedy this, the library provides a very simple but authoritative+model implementation. See 'Reactive.Banana.Model' for more.++-}++{-----------------------------------------------------------------------------+    Interpetation+------------------------------------------------------------------------------}+-- | Interpret with model implementation.+-- Useful for testing.+interpretModel :: (forall t. Event t a -> Event t b) -> [[a]] -> IO [[b]]+interpretModel f xs =+    map toList <$> Model.interpretModel (unE . f . E) (map Just xs)++-- | Interpret with push-based implementation.+-- Useful for testing.+interpretPushGraph :: (forall t. Event t a -> Event t b) -> [[a]] -> IO [[b]]+interpretPushGraph f xs = do+    i <- newInputChannel+    automaton <- compileToAutomaton (unE . f . E $ AST.inputE i)+    map toList <$> unfoldAutomaton automaton i xs++toList :: Maybe [a] -> [a]+toList Nothing   = []+toList (Just xs) = xs++{-----------------------------------------------------------------------------+    Basic combinators++    Implemented in terms of Reactive.Banana.Internal.AST+------------------------------------------------------------------------------}+singleton :: a -> [a]+singleton x = [x]++-- | Event that never occurs.+-- Think of it as @never = []@.+never    :: Event t a+never = E $ singleton <$> AST.never++-- | Merge two event streams of the same type.+-- In case of simultaneous occurrences, the left argument comes first.+-- Think of it as+--+-- > union ((timex,x):xs) ((timey,y):ys)+-- >    | timex <= timey = (timex,x) : union xs ((timey,y):ys)+-- >    | timex >  timey = (timey,y) : union ((timex,x):xs) ys+union    :: Event t a -> Event t a -> Event t a+union e1 e2 = E $ AST.unionWith (++) (unE e1) (unE e2)++-- | Allow all events that fulfill the predicate, discard the rest.+-- Think of it as+-- +-- > filterE p es = [(time,a) | (time,a) <- es, p a]+filterE   :: (a -> Bool) -> Event t a -> Event t a+filterE p = E . AST.filterE (not . null) . (filter p <$>) . unE++-- | Collect simultaneous event occurences.+-- The result will never contain an empty list.+-- Example:+--+-- > collect [(time1, e1), (time1, e2)] = [(time1, [e1,e2])]+collect   :: Event t a -> Event t [a]+collect e = E $ singleton <$> unE e++-- | Emit simultaneous event occurrences.+-- Up to strictness, we have+--+-- > spill . collect = id+spill :: Event t [a] -> Event t a+spill e = E $ concat <$> unE e++-- | Construct a time-varying function from an initial value and +-- a stream of new values. Think of it as+--+-- > stepper x0 ex = \time -> last (x0 : [x | (timex,x) <- ex, timex < time])+-- +-- Note that the smaller-than-sign in the comparision @timex < time@ means +-- that the value of the behavior changes \"slightly after\"+-- the event occurrences. This allows for recursive definitions.+-- +-- Also note that in the case of simultaneous occurrences,+-- only the last one is kept.+stepper :: a -> Event t a -> Behavior t a+stepper x e = B $ AST.stepperB x (last <$> unE e)++-- | The 'accumE' function accumulates a stream of events.+-- Example:+--+-- > accumE "x" [(time1,(++"y")),(time2,(++"z"))]+-- >    = [(time1,"xy"),(time2,"xyz")]+--+-- Note that the output events are simultaneous with the input events,+-- there is no \"delay\" like in the case of 'accumB'.+accumE   :: a -> Event t (a -> a) -> Event t a+accumE acc = E . mapAccumE acc . fmap concatenate . unE+    where+    concatenate :: [a -> a] -> a -> ([a],a)+    concatenate fs acc = (tail values, last values)+        where values = scanl' (flip ($)) acc fs++    mapAccumE :: s -> AST.Event AST.Expr (s -> (a,s)) -> AST.Event AST.Expr a+    mapAccumE acc = fmap fst . AST.accumE (undefined,acc) . fmap (. snd)++-- strict version of scanl+scanl' :: (a -> b -> a) -> a -> [b] -> [a]+scanl' f x ys = x : case ys of+    []   -> []+    y:ys -> let z = f x y in z `seq` scanl' f z ys++-- | Apply a time-varying function to a stream of events.+-- Think of it as+-- +-- > apply bf ex = [(time, bf time x) | (time, x) <- ex]+apply    :: Behavior t (a -> b) -> Event t a -> Event t b+apply bf ex = E $ AST.applyE (map <$> unB bf) (unE ex)++{-$classes++/Further combinators that Haddock can't document properly./++> instance Monoid (Event t (a -> a))++This monoid instance is /not/ the straightforward instance+that you would obtain from 'never' and 'union'.+Instead of just merging event streams, we use 'unionWith' to compose+the functions. This is very useful in the context of 'accumE' and 'accumB'+where simultaneous event occurrences are best avoided.++> instance Applicative (Behavior t)++'Behavior' is an applicative functor. In particular, we have the following functions.++> pure :: a -> Behavior t a++The constant time-varying value. Think of it as @pure x = \\time -> x@.++> (<*>) :: Behavior t (a -> b) -> Behavior t a -> Behavior t b++Combine behaviors in applicative style.+Think of it as @bf \<*\> bx = \\time -> bf time $ bx time@.++-}++instance Monoid (Event t (a -> a)) where+    mempty  = never+    mappend = unionWith (flip (.))++instance Functor (Event t) where+    fmap f e = E $ (map f) <$> (unE e)++instance Applicative (Behavior t) where+    pure x = B $ AST.pureB x+    bf <*> bx = B $ AST.applyB (unB bf) (unB bx)++instance Functor (Behavior t) where+    fmap = liftA++{-----------------------------------------------------------------------------+    Derived Combinators+------------------------------------------------------------------------------}+-- | Keep only the 'Just' values.+-- Variant of 'filterE'.+filterJust :: Event t (Maybe a) -> Event t a+filterJust = fmap (maybe err id) . filterE isJust+    where err = error "Reactive.Banana.Model.filterJust: Internal error. :("++-- | Allow all events that fulfill the time-varying predicate, discard the rest.+-- Generalization of 'filterE'.+filterApply :: Behavior t (a -> Bool) -> Event t a -> Event t a+filterApply bp = fmap snd . filterE fst . apply ((\p a-> (p a,a)) <$> bp)++-- | Allow events only when the behavior is 'True'.+-- Variant of 'filterApply'.+whenE :: Behavior t Bool -> Event t a -> Event t a+whenE bf = filterApply (const <$> bf)++-- | Split event occurrences according to a tag.+split :: Event t (Either a b) -> (Event t a, Event t b)+split e = (filterJust $ fromLeft <$> e, filterJust $ fromRight <$> e)+    where+    fromLeft  (Left  a) = Just a+    fromLeft  (Right b) = Nothing+    fromRight (Left  a) = Nothing+    fromright (Right b) = Just b+++-- | Combine simultaneous event occurrences into a single occurrence.+--+-- > unionWith f e1 e2 = fmap (foldr1 f) <$> collect (e1 `union` e2)+unionWith :: (a -> a -> a) -> Event t a -> Event t a -> Event t a+unionWith f e1 e2 = E $ AST.unionWith g (unE e1) (unE e2)+    where g xs ys = singleton $ foldr1 f (xs ++ ys)++-- | Keep only the last occurrence when simultaneous occurrences happen.+calm :: Event t a -> Event t a+calm = fmap last . collect++++-- $Accumulation.+-- Note: all accumulation functions are strict in the accumulated value!+-- acc -> (x,acc) is the order used by 'unfoldr' and 'State'.++-- | The 'accumB' function is similar to a /strict/ left fold, 'foldl''.+-- It starts with an initial value and combines it with incoming events.+-- For example, think+--+-- > accumB "x" [(time1,(++"y")),(time2,(++"z"))]+-- >    = stepper "x" [(time1,"xy"),(time2,"xyz")]+-- +-- Note that the value of the behavior changes \"slightly after\"+-- the events occur. This allows for recursive definitions.+accumB   :: a -> Event t (a -> a) -> Behavior t a+-- accumB x (Event e) = behavior $ AccumB x e+accumB  acc = stepper acc . accumE acc++-- | Efficient combination of 'accumE' and 'accumB'.+mapAccum :: acc -> Event t (acc -> (x,acc)) -> (Event t x, Behavior t acc)+mapAccum acc ef = (fst <$> e, stepper acc (snd <$> e))+    where e = accumE (undefined,acc) ((. snd) <$> ef)+++infixl 4 <@>, <@++-- | Class for overloading the 'apply' function.+class (Functor f, Functor g) => Apply f g where+    -- | Infix operation for the 'apply' function, similar to '<*>'+    (<@>) :: f (a -> b) -> g a -> g b+    -- | Convenience function, similar to '<*'+    (<@)  :: f a -> g b -> g a+    +    f <@ g = (const <$> f) <@> g ++instance Apply (Behavior t) (Event t) where+    (<@>) = apply++
+ src/Reactive/Banana/Experimental/Calm.hs view
@@ -0,0 +1,131 @@+{-----------------------------------------------------------------------------+    Reactive Banana+------------------------------------------------------------------------------}+{-# LANGUAGE Rank2Types, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}++module Reactive.Banana.Experimental.Calm (+    -- * Synopsis+    -- | Experimental module: API change very likely.+    --+    -- 'Event' type that disallows simultaneous event occurrences.+    --+    -- The combinators behave essentially as their counterparts+    -- in "Reactive.Banana.Combinators".+    +    -- * Main types+    Event, Behavior, collect, fromCalm,+    interpretModel, interpretPushGraph,+    +    -- * Core Combinators+    module Control.Applicative,+    never, unionWith, filterE, accumE,+    apply, stepper,+    -- $classes+    +    -- * Derived Combinators+    -- ** Filtering+    filterJust,+    -- ** Accumulation+    -- $Accumulation.+    accumB, mapAccum,+    -- ** Apply class+    Reactive.Banana.Combinators.Apply(..),+    ) where++import Control.Applicative+import Control.Monad++import Data.Maybe (listToMaybe)++import qualified Reactive.Banana.Combinators as Prim+import qualified Reactive.Banana.Combinators++{-----------------------------------------------------------------------------+    Main types+------------------------------------------------------------------------------}+newtype Event t a = E { unE :: Prim.Event t a }++type Behavior t = Reactive.Banana.Combinators.Behavior t++-- | Convert event with possible simultaneous occurrences+-- into an 'Event' with a single occurrence.+collect :: Reactive.Banana.Combinators.Event t a -> Event t [a]+collect = E . Prim.collect++-- | Convert event with single occurrences into+-- event with possible simultaneous occurrences+fromCalm :: Event t a -> Reactive.Banana.Combinators.Event t a+fromCalm = unE++singleton x = [x]++-- | Interpret with model implementation.+-- Useful for testing.+interpretModel :: (forall t. Event t a -> Event t b) -> [a] -> IO [Maybe b]+interpretModel f xs =+    map listToMaybe <$> Prim.interpretModel (unE . f . E) (map singleton xs)++-- | Interpret with push-based implementation.+-- Useful for testing.+interpretPushGraph :: (forall t. Event t a -> Event t b) -> [a] -> IO [Maybe b]+interpretPushGraph f xs =+    map listToMaybe <$> Prim.interpretPushGraph (unE . f . E) (map singleton xs)++{-----------------------------------------------------------------------------+    Core Combinators+------------------------------------------------------------------------------}+-- | Event that never occurs.+-- Think of it as @never = []@.+never    :: Event t a+never = E $ Prim.never++-- | Merge two event streams of the same type.+-- Combine simultaneous values if necessary.+unionWith    :: (a -> a -> a) -> Event t a -> Event t a -> Event t a+unionWith f e1 e2 = E $ Prim.unionWith f (unE e1) (unE e2)++-- | Allow all events that fulfill the predicate, discard the rest.+filterE   :: (a -> Bool) -> Event t a -> Event t a+filterE p = E . Prim.filterE p . unE++-- | Construct a time-varying function from an initial value and +-- a stream of new values.+stepper :: a -> Event t a -> Behavior t a+stepper x e = Prim.stepper x (unE e)++-- | The 'accumE' function accumulates a stream of events.+accumE   :: a -> Event t (a -> a) -> Event t a+accumE acc = E . Prim.accumE acc . unE++-- | Apply a time-varying function to a stream of events.+apply    :: Behavior t (a -> b) -> Event t a -> Event t b+apply b = E . Prim.apply b . unE++instance Functor (Event t) where+    fmap f = E . fmap f . unE++{-----------------------------------------------------------------------------+    Derived Combinators+------------------------------------------------------------------------------}+-- | Keep only the 'Just' values.+-- Variant of 'filterE'.+filterJust :: Event t (Maybe a) -> Event t a+filterJust = E . Prim.filterJust . unE++-- | The 'accumB' function is similar to a /strict/ left fold, 'foldl''.+-- It starts with an initial value and combines it with incoming events.+accumB :: a -> Event t (a -> a) -> Behavior t a+accumB acc = Prim.accumB acc . unE++-- $Accumulation.+-- Note: all accumulation functions are strict in the accumulated value!+-- acc -> (x,acc) is the order used by 'unfoldr' and 'State'.++-- | Efficient combination of 'accumE' and 'accumB'.+mapAccum :: acc -> Event t (acc -> (x,acc)) -> (Event t x, Behavior t acc)+mapAccum acc ef = let (e,b) = Prim.mapAccum acc (unE ef) in (E e, b)++instance Reactive.Banana.Combinators.Apply (Behavior t) (Event t) where+    (<@>) = apply++
+ src/Reactive/Banana/Frameworks.hs view
@@ -0,0 +1,403 @@+{-----------------------------------------------------------------------------+    Reactive Banana+------------------------------------------------------------------------------}+{-# LANGUAGE Rank2Types, DeriveDataTypeable #-}++module Reactive.Banana.Frameworks (+    -- * Synopsis+    -- | Build event networks using existing event-based frameworks and run them.+    +    -- * Simple use+    interpret, interpretAsHandler,++    -- * Building event networks with input/output+    -- $build+    NetworkDescription, compile,+    AddHandler, fromAddHandler, fromChanges, fromPoll,+    reactimate, initial, changes,+    liftIO, liftIOLater,+    +    -- * Running event networks+    EventNetwork, actuate, pause,+    +    -- * Utilities+    -- $utilities+    newAddHandler, newEvent,+    ) where++import Control.Applicative+import Control.Arrow (second)+import Control.Concurrent+import Control.Monad+import Control.Monad.Fix       (MonadFix(..))+import Control.Monad.IO.Class  (MonadIO(..))+import Control.Monad.Trans.RWS++import Data.Dynamic (Typeable)+import Data.IORef+import Data.List (nub)+import Data.Monoid++import qualified Data.HashMap.Strict as Map++import Reactive.Banana.Internal.InputOutput+import Reactive.Banana.Combinators+import qualified Reactive.Banana.Internal.AST as AST+import qualified Reactive.Banana.Internal.PushGraph as Implementation++type Map = Map.HashMap++{-----------------------------------------------------------------------------+    AST specific functions+------------------------------------------------------------------------------}+inputE :: InputChannel [a] -> Event t a+inputE = E . AST.inputE++{-----------------------------------------------------------------------------+    NetworkDescription, setting up event networks+------------------------------------------------------------------------------}+{-$build++    After having read all about 'Event's and 'Behavior's,+    you want to hook them up to an existing event-based framework,+    like @wxHaskell@ or @Gtk2Hs@.+    How do you do that?++    This "Reactive.Banana.Implementation" module allows you to obtain /input/ events+    from external sources+    and it allows you perform /output/ in reaction to events.+    +    In constrast, the functions from "Reactive.Banana.Model" allow you +    to express the output events in terms of the input events.+    This expression is called an /event graph/.+    +    An /event network/ is an event graph together with inputs and outputs.+    To build an event network,+    describe the inputs, outputs and event graph in the 'NetworkDescription' monad +    and use the 'compile' function to obtain an event network from that.++    To /activate/ an event network, use the 'actuate' function.+    The network will register its input event handlers and start producing output.++    A typical setup looks like this:+    +> main = do+>   -- initialize your GUI framework+>   window <- newWindow+>   ...+>+>   -- describe the event network+>   let networkDescription :: forall t. NetworkDescription t ()+>       networkDescription = do+>           -- input: obtain  Event  from functions that register event handlers+>           emouse    <- fromAddHandler $ registerMouseEvent window+>           ekeyboard <- fromAddHandler $ registerKeyEvent window+>           -- input: obtain  Behavior  from changes+>           btext     <- fromChanges    "" $ registerTextChange editBox+>           -- input: obtain  Behavior  from mutable data by polling+>           bdie      <- fromPoll       $ randomRIO (1,6)+>+>           -- express event graph+>           let+>               behavior1 = accumB ...+>               ...+>               event15 = union event13 event14+>   +>           -- output: animate some event occurences+>           reactimate $ fmap print event15+>           reactimate $ fmap drawCircle eventCircle+>+>       -- compile network description into a network+>       network <- compile networkDescription+>       -- register handlers and start producing outputs+>       actuate network++    In short, you use 'fromAddHandler' to obtain /input/ events.+    The library uses this to register event handlers+    with your event-based framework.+    +    To animate /output/ events, use the 'reactimate' function.++-}++type AddHandler'     = AddHandler InputValue+type Preparations t  =+    ( [Event t (IO ())]     -- reactimate outputs+    , [AddHandler']         -- fromAddHandler events +    , [IO InputValue]       -- fromPoll events+    , [IO ()]               -- liftIOLater+    )++-- | Monad for describing event networks.+-- +-- The 'NetworkDescription' monad is an instance of 'MonadIO',+-- so 'IO' is allowed inside.+-- +-- Note: The phantom type @t@ prevents you from smuggling+-- values of types 'Event' or 'Behavior'+-- outside the 'NetworkDescription' monad.+newtype NetworkDescription t a+    = Prepare { unPrepare :: RWST () (Preparations t) () IO a }++instance Monad (NetworkDescription t) where+    return  = Prepare . return+    m >>= k = Prepare $ unPrepare m >>= unPrepare . k+instance MonadIO (NetworkDescription t) where+    liftIO  = Prepare . liftIO+instance Functor (NetworkDescription t) where+    fmap f  = Prepare . fmap f . unPrepare+instance Applicative (NetworkDescription t) where+    pure    = Prepare . pure+    f <*> a = Prepare $ unPrepare f <*> unPrepare a+instance MonadFix (NetworkDescription t) where+    mfix f  = Prepare $ mfix (unPrepare . f)++{- | Output.+    Execute the 'IO' action whenever the event occurs.+    +    +    Note: If two events occur very close to each other,+    there is no guarantee that the @reactimate@s for one +    event will have finished before the ones for the next event start executing.+    This does /not/ affect the values of events and behaviors,+    it only means that the @reactimate@ for different events may interleave.+    Fortuantely, this is a very rare occurrence, and only happens if+    +    * you call an event handler from inside 'reactimate',+    +    * or you use concurrency.+    +    In these cases, the @reactimate@s follow the control flow+    of your event-based framework.++-}+reactimate :: Event t (IO ()) -> NetworkDescription t ()+reactimate e = Prepare $ tell ([e],[],[],[])++-- | A value of type @AddHandler a@ is just a facility for registering+-- callback functions, also known as event handlers.+-- +-- The type is a bit mysterious, it works like this:+-- +-- > do unregisterMyHandler <- addHandler myHandler+--+-- The argument is an event handler that will be registered.+-- The return value is an action that unregisters this very event handler again.+type AddHandler a = (a -> IO ()) -> IO (IO ())++-- | Input,+-- obtain an 'Event' from an 'AddHandler'.+--+-- When the event network is actuated,+-- this will register a callback function such that+-- an event will occur whenever the callback function is called.+fromAddHandler :: AddHandler a -> NetworkDescription t (Event t a)+fromAddHandler addHandler = Prepare $ do+    i <- liftIO $ newInputChannel+    let addHandler' k = addHandler $ k . toValue i . (\x -> [x])+    tell ([],[addHandler'],[],[])+    return $ inputE i++-- | Input,+-- obtain a 'Behavior' by frequently polling mutable data, like the current time.+--+-- The resulting 'Behavior' will be updated on whenever the event+-- network processes an input event.+--+-- This function is occasionally useful, but+-- the recommended way to obtain 'Behaviors' is by using 'fromChanges'.+-- +-- Ideally, the argument IO action just polls a mutable variable,+-- it should not perform expensive computations.+-- Neither should its side effects affect the event network significantly.+fromPoll :: IO a -> NetworkDescription t (Behavior t a)+fromPoll poll = Prepare $ do+    i <- liftIO $ newInputChannel+    let poll' = toValue i . (:[]) <$> poll+    tell ([],[],[poll'],[])+    initial <- liftIO $ poll+    return $ stepper initial (inputE i)++-- | Input,+-- obtain a 'Behavior' from an 'AddHandler' that notifies changes.+-- +-- This is essentially just an application of the 'stepper' combinator.+fromChanges :: a -> AddHandler a -> NetworkDescription t (Behavior t a)+fromChanges initial changes = stepper initial <$> fromAddHandler changes++-- | Output,+-- observe when a 'Behavior' changes.+-- +-- Strictly speaking, a 'Behavior' denotes a value that varies *continuously* in time,+-- so there is no well-defined event which indicates when the behavior changes.+-- +-- Still, for reasons of efficiency, the library provides a way to observe+-- changes when the behavior is a step function, for instance as +-- created by 'stepper'. There are no formal guarantees,+-- but the idea is that+--+-- > changes (stepper x e) = return (calm e)+changes :: Behavior t a -> NetworkDescription t (Event t a)+changes ~(B (AST.Pair _ (AST.Stepper x e))) = return $ E $ fmap (:[]) e++-- | Output,+-- observe the initial value contained in a 'Behavior'.+--+-- Similar to 'updates', this function is not well-defined,+-- but exists for reasons of efficiency.+initial :: Behavior t a -> NetworkDescription t a+initial ~(B (AST.Pair _ (AST.Stepper x e))) = return x++-- | Lift an 'IO' action into the 'NetworkDescription' monad,+-- but defer its execution until compilation time.+-- This can be useful for recursive definitions using 'MonadFix'.+liftIOLater :: IO () -> NetworkDescription t ()+liftIOLater m = Prepare $ tell ([],[],[],[m])++-- | Compile a 'NetworkDescription' into an 'EventNetwork'+-- that you can 'actuate', 'pause' and so on.+compile :: (forall t. NetworkDescription t ()) -> IO EventNetwork+compile (Prepare m) = do+    -- execute the NetworkDescription monad+    (_,_,(outputs,inputs,polls,liftIOs)) <- runRWST m () ()+    sequence_ liftIOs+    +    let -- union of all  reactimates+        E graph = foldr union never outputs+    +    automaton <- Implementation.compileToAutomaton graph+    +    -- allocate new variable for the automaton+    rautomaton <- newEmptyMVar+    putMVar rautomaton automaton+    +    let -- run the graph on a single input value+        run :: InputValue -> IO ()+        run input = do+            -- takeMVar  makes sure that event graph updates are sequential.+            automaton  <- takeMVar rautomaton+            -- poll mutable data+            pollValues <- sequence polls+            -- percolate inputs through event graph+            (reactimates,automaton') <- runStep automaton $ input:pollValues+            putMVar rautomaton automaton'+            -- Run corresponding IO actions afterwards.+            -- Under certain circumstances, they can *interleave*.+            case reactimates of+                Just actions -> sequence_ actions+                Nothing      -> return ()+    +        -- register event handlers+        register :: IO (IO ())+        register = fmap sequence_ . sequence . map ($ run) $ inputs++    makeEventNetwork register+++{-----------------------------------------------------------------------------+    Running event networks+------------------------------------------------------------------------------}+-- | Data type that represents a compiled event network.+-- It may be paused or already running.+data EventNetwork = EventNetwork {+    -- | Actuate an event network.+    -- The inputs will register their event handlers, so that+    -- the networks starts to produce outputs in response to input events.+    actuate :: IO (),+    +    -- | Pause an event network.+    -- Immediately stop producing output and+    -- unregister all event handlers for inputs.+    -- Hence, the network stops responding to input events,+    -- but it's state will be preserved.+    --+    -- You can resume the network with 'actuate'.+    --+    -- Note: You can stop a network even while it is processing events,+    -- i.e. you can use 'pause' as an argument to 'reactimate'.+    -- The network will /not/ stop immediately though, only after+    -- the current event has been processed completely.+    pause :: IO ()+    } deriving (Typeable)++-- Make an event network from a function that registers all event handlers+makeEventNetwork :: IO (IO ()) -> IO EventNetwork+makeEventNetwork register = do+    let nop = return ()+    unregister <- newIORef nop+    let+        actuate = register >>= writeIORef unregister+        pause   = readIORef unregister >>= id >> writeIORef unregister nop+    return $ EventNetwork actuate pause+++{-----------------------------------------------------------------------------+    Simple use+------------------------------------------------------------------------------}+-- | Simple way to run an event graph. Very useful for testing.+-- Uses the efficient push-driven implementation.+interpret :: (forall t. Event t a -> Event t b) -> [a] -> IO [[b]]+interpret f xs = do+    output                    <- newIORef []+    (addHandler, runHandlers) <- newAddHandler+    network                   <- compile $ do+        e <- fromAddHandler addHandler+        reactimate $ fmap (\b -> modifyIORef output (++[b])) (f e)++    actuate network+    bs <- forM xs $ \x -> do+        runHandlers x+        bs <- readIORef output+        writeIORef output []+        return bs+    return bs++-- | Simple way to write a single event handler with functional reactive programming.+interpretAsHandler+    :: (forall t. Event t a -> Event t b)+    -> AddHandler a -> AddHandler b+interpretAsHandler f addHandlerA = \handlerB -> do+    network <- compile $ do+        e <- fromAddHandler addHandlerA+        reactimate $ handlerB <$> f e+    actuate network+    return (pause network)+++{-----------------------------------------------------------------------------+    Utilities+------------------------------------------------------------------------------}+{-$utilities++    This section collects a few convenience functions+    for unusual use cases. For instance:+    +    * The event-based framework you want to hook into is poorly designed+    +    * You have to write your own event loop and roll a little event framework++-}++-- | Build a facility to register and unregister event handlers.+newAddHandler :: IO (AddHandler a, a -> IO ())+newAddHandler = do+    handlers <- newIORef Map.empty+    let addHandler k = do+            key <- newUnique+            modifyIORef handlers $ Map.insert key k+            return $ modifyIORef handlers $ Map.delete key+        runHandlers x =+            mapM_ ($ x) . map snd . Map.toList =<< readIORef handlers+    return (addHandler, runHandlers)+++-- | Build an 'Event' together with an 'IO' action that can +-- fire occurrences of this event. Variant of 'newAddHandler'.+-- +-- This function is mainly useful for passing callback functions+-- inside a 'reactimate'.+newEvent :: NetworkDescription t (Event t a, a -> IO ())+newEvent = do+    (addHandler, fire) <- liftIO $ newAddHandler+    e <- fromAddHandler addHandler+    return (e,fire)
− src/Reactive/Banana/Implementation.hs
@@ -1,371 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{------------------------------------------------------------------------------    Reactive Banana-    -    Linking the push-based implementation to an event-based framework-------------------------------------------------------------------------------}-module Reactive.Banana.Implementation (-    -- * Synopsis-    -- | Build event networks using existing event-based frameworks-    --   and run them.-    -    -- * Simple use-    PushIO, interpret, interpretAsHandler,--    -- * Building event networks with input/output-    -- $build-    NetworkDescription, compile,-    AddHandler, fromAddHandler, fromPoll, reactimate, liftIO, liftIOLater,-    -    -- * Running event networks-    EventNetwork, actuate, pause,-    -    -- * Utilities-    -- $utilities-    newAddHandler,-    -    module Data.Dynamic, -- remove this when bumping to 0.5-    ) where--import Control.Applicative-import Control.Arrow (second)-import Control.Concurrent-import Control.Monad-import Control.Monad.Fix       (MonadFix(..))-import Control.Monad.IO.Class  (MonadIO(..))-import Control.Monad.Trans.RWS--import Data.Dynamic-import Data.IORef-import Data.List (nub)-import Data.Monoid-import qualified Data.Map as Map-import Data.Unique--import Reactive.Banana.PushIO hiding (compile)-import qualified Reactive.Banana.PushIO as Implementation-import qualified Reactive.Banana.Model as Model--{------------------------------------------------------------------------------    PushIO specific functions-------------------------------------------------------------------------------}-type Flavor  = Implementation.PushIO--poll :: IO a -> Model.Behavior Flavor a-poll = behavior . Poll--input :: Channel -> Key a -> Model.Event Flavor a-input c = event . Input c--compileHandlers :: Model.Event Flavor (IO ()) -> IO [(Channel, Universe -> IO ())]-compileHandlers graph = do-    -- compile event graph-    let graph' = Implementation.unEvent graph-    (paths1,cache) <- Implementation.compile (invalidRef, Reactimate graph')--    -- prepare threading the cache as state-    rcache <- newEmptyMVar-    putMVar rcache cache-    let -- run a single path-        run m = do-            -- takeMVar  makes sure that event graph updates are sequential.-            cache <- takeMVar rcache-            (reactimates,cache') <- runRun m cache-            putMVar rcache cache'-            -- However, the corresponding IO actions are run afterwards,-            -- and under certain circumstances, they can *interleave*.-            reactimates-         -        appendReactimates-            :: [Universe -> Run (IO ())]-            -> (Universe -> Run (IO ()))-        appendReactimates ps x = do-            reactimates <- sequence $ map ($ x) ps-            return $ sequence_ reactimates-            -        paths2 = map (second $ (run .) . appendReactimates) $ groupByChannel paths1-    -    return paths2----- FIXME: make this faster-groupByChannel :: [(Channel, a)] -> [(Channel, [a])]-groupByChannel xs = [(i, [x | (j,x) <- xs, i == j]) | i <- channels]-    where channels = nub . map fst $ xs--{------------------------------------------------------------------------------    NetworkDescription, setting up event networks-------------------------------------------------------------------------------}-{-$build--    After having read all about 'Event's and 'Behavior's,-    you want to hook them up to an existing event-based framework,-    like @wxHaskell@ or @Gtk2Hs@.-    How do you do that?--    This "Reactive.Banana.Implementation" module allows you to obtain /input/ events-    from external sources-    and it allows you perform /output/ in reaction to events.-    -    In constrast, the functions from "Reactive.Banana.Model" allow you -    to express the output events in terms of the input events.-    This expression is called an /event graph/.-    -    An /event network/ is an event graph together with inputs and outputs.-    To build an event network,-    describe the inputs, outputs and event graph in the 'NetworkDescription' monad -    and use the 'compile' function to obtain an event network from that.--    To /activate/ an event network, use the 'actuate' function.-    The network will register its input event handlers and start producing output.--    A typical setup looks like this:-    -> main = do->   -- initialize your GUI framework->   window <- newWindow->   ...->->   -- build the event network->   network <- compile $ do->       -- input: obtain  Event  from functions that register event handlers->       emouse    <- fromAddHandler $ registerMouseEvent window->       ekeyboard <- fromAddHandler $ registerKeyEvent window->       -- input: obtain  Behavior  from mutable data by polling->       btext     <- fromPoll       $ getTextValue editBox->       bdie      <- fromPoll       $ randomRIO (1,6)->->       -- express event graph->       let->           behavior1 = accumB ...->           ...->           event15 = union event13 event14->   ->       -- output: animate some event occurences->       reactimate $ fmap print event15->       reactimate $ fmap drawCircle eventCircle->->   -- register handlers and start producing outputs->   actuate network--    In short, you use 'fromAddHandler' to obtain /input/ events.-    The library uses this to register event handlers-    with your event-based framework.-    -    To animate /output/ events, use the 'reactimate' function.---}--type AddHandler'  = (Channel, AddHandler Universe)-type Preparations = ([Model.Event Flavor (IO ())], [AddHandler'], [IO ()])---- | Monad for describing event networks.--- --- The 'NetworkDescription' monad is an instance of 'MonadIO',--- so 'IO' is allowed inside.--- --- Note: It is forbidden to smuggle values of types 'Event' or 'Behavior'--- outside the 'NetworkDescription' monad. This shouldn't be possible by default,--- but you might get clever and use 'IORef' to circumvent this.--- Don't do that, it won't work and also has a 99,98% chance of --- destroying the earth by summoning time-traveling zygohistomorphisms.-newtype NetworkDescription a = Prepare { unPrepare :: RWST () Preparations Channel IO a }--instance Monad (NetworkDescription) where-    return  = Prepare . return-    m >>= k = Prepare $ unPrepare m >>= unPrepare . k-instance MonadIO (NetworkDescription) where-    liftIO  = Prepare . liftIO-instance Functor (NetworkDescription) where-    fmap f  = Prepare . fmap f . unPrepare-instance Applicative (NetworkDescription) where-    pure    = Prepare . pure-    f <*> a = Prepare $ unPrepare f <*> unPrepare a-instance MonadFix (NetworkDescription) where-    mfix f  = Prepare $ mfix (unPrepare . f)--{- | Output.-    Execute the 'IO' action whenever the event occurs.-    -    -    Note: If two events occur very close to each other,-    there is no guarantee that the @reactimate@s for one -    event will have finished before the ones for the next event start executing.-    This does /not/ affect the values of events and behaviors,-    it only means that the @reactimate@ for different events may interleave.-    Fortuantely, this is a very rare occurrence, and only happens if-    -    * you call an event handler from inside 'reactimate',-    -    * or you use concurrency.-    -    In these cases, the @reactimate@s follow the control flow-    of your event-based framework.---}-reactimate :: Model.Event PushIO (IO ()) -> NetworkDescription ()-reactimate e = Prepare $ tell ([e], [], [])---- | A value of type @AddHandler a@ is just a facility for registering--- callback functions, also known as event handlers.--- --- The type is a bit mysterious, it works like this:--- --- > do unregisterMyHandler <- addHandler myHandler------ The argument is an event handler that will be registered.--- The return value is an action that unregisters this very event handler again.-type AddHandler a = (a -> IO ()) -> IO (IO ())---- | Input,--- obtain an 'Event' from an 'AddHandler'.------ When the event network is actuated,--- this will register a callback function such that--- an event will occur whenever the callback function is called.-fromAddHandler :: AddHandler a -> NetworkDescription (Model.Event PushIO a)-fromAddHandler addHandler = Prepare $ do-        channel <- newChannel-        key     <- liftIO $ newUniverseKey-        let addHandler' k = addHandler $ k . toUniverse key-        tell ([], [(channel, addHandler')], [])-        return $ input channel key-    where-    newChannel = do c <- get; put $! c+1; return c---- | Input,--- obtain a 'Behavior' by polling mutable data, like mutable variables or GUI widgets.--- --- Ideally, the argument IO action just polls a mutable variable,--- it should not perform expensive computations.--- Neither should its side effects affect the event network significantly.--- --- Internally, the event network will take a snapshot of each mutable--- datum before processing an input event, so that the obtained behavior--- is well-defined. This snapshot is guaranteed to happen before--- any 'reactimate' is performed. The network may omit taking a snapshot altogether--- if the behavior is not needed.-fromPoll :: IO a -> NetworkDescription (Model.Behavior PushIO a)-fromPoll m = return $ poll m---- | Lift an 'IO' action into the 'NetworkDescription' monad,--- but defer its execution until compilation time.--- This can be useful for recursive definitions using 'MonadFix'.-liftIOLater :: IO () -> NetworkDescription ()-liftIOLater m = Prepare $ tell ([],[], [m])---- | Compile a 'NetworkDescription' into an 'EventNetwork'--- that you can 'actuate', 'pause' and so on.-compile :: NetworkDescription () -> IO EventNetwork-compile (Prepare m) = do-    (_,_,(outputs,inputs,liftIOs)) <- runRWST m () 0-    sequence_ liftIOs-    -    let -- union of all  reactimates-        graph = mconcat outputs :: Model.Event Flavor (IO ())-    paths <- compileHandlers graph-    -    let -- register event handlers-        register = fmap sequence_ . sequence . map snd . applyChannels inputs-                 $ paths-    makeEventNetwork register---- FIXME: make this faster-applyChannels :: [(Channel, a -> b)] -> [(Channel, a)] -> [(Channel, b)]-applyChannels fs xs =-    [(i, f x) | (i,f) <- fs, (j,x) <- xs, i == j]--{------------------------------------------------------------------------------    Running event networks-------------------------------------------------------------------------------}--- | Data type that represents a compiled event network.--- It may be paused or already running.-data EventNetwork = EventNetwork {-    -- | Actuate an event network.-    -- The inputs will register their event handlers, so that-    -- the networks starts to produce outputs in response to input events.-    actuate :: IO (),-    -    -- | Pause an event network.-    -- Immediately stop producing output and-    -- unregister all event handlers for inputs.-    -- Hence, the network stops responding to input events,-    -- but it's state will be preserved.-    ---    -- You can resume the network with 'actuate'.-    ---    -- Note: You can stop a network even while it is processing events,-    -- i.e. you can use 'pause' as an argument to 'reactimate'.-    -- The network will /not/ stop immediately though, only after-    -- the current event has been processed completely.-    pause :: IO ()-    } deriving (Typeable)---- Make an event network from a function that registers all event handlers-makeEventNetwork :: IO (IO ()) -> IO EventNetwork-makeEventNetwork register = do-    let nop = return ()-    unregister <- newIORef nop-    let-        actuate = register >>= writeIORef unregister-        pause   = readIORef unregister >>= id >> writeIORef unregister nop-    return $ EventNetwork actuate pause---{------------------------------------------------------------------------------    Simple use-------------------------------------------------------------------------------}--- | Simple way to run an event graph. Very useful for testing.-interpret :: (Model.Event PushIO a -> Model.Event PushIO b) -> [a] -> IO [[b]]-interpret f xs = do-    output                    <- newIORef []-    (addHandler, runHandlers) <- newAddHandler-    network                   <- compile $ do-        e <- fromAddHandler addHandler-        reactimate $ fmap (\b -> modifyIORef output (++[b])) (f e)--    actuate network-    bs <- forM xs $ \x -> do-        runHandlers x-        bs <- readIORef output-        writeIORef output []-        return bs-    return bs---- | Simple way to write a single event handler with functional reactive programming.-interpretAsHandler-    :: (Model.Event PushIO a -> Model.Event PushIO b)-    -> AddHandler a -> AddHandler b-interpretAsHandler f addHandlerA = \handlerB -> do-    network <- compile $ do-        e <- fromAddHandler addHandlerA-        reactimate $ handlerB <$> f e-    actuate network-    return (pause network)--{------------------------------------------------------------------------------    Utilities-------------------------------------------------------------------------------}-{-$utilities--    This section collects a few convenience functions-    for unusual use cases. For instance:-    -    * The event-based framework you want to hook into is poorly designed-    -    * You have to write your own event loop and roll a little event framework---}---- | Build a facility to register and unregister event handlers.-newAddHandler :: IO (AddHandler a, a -> IO ())-newAddHandler = do-    handlers <- newIORef Map.empty-    let addHandler k = do-            key <- newUnique-            modifyIORef handlers $ Map.insert key k-            return $ modifyIORef handlers $ Map.delete key-        runHandlers x =-            mapM_ ($ x) . map snd . Map.toList =<< readIORef handlers-    return (addHandler, runHandlers)-
− src/Reactive/Banana/Incremental.hs
@@ -1,118 +0,0 @@-{------------------------------------------------------------------------------    Reactive Banana-    -    Derived data type, a hybrid between  Event  and  Behavior-------------------------------------------------------------------------------}-{-# LANGUAGE MultiParamTypeClasses #-}-module Reactive.Banana.Incremental (-    -- * Why a third type Discrete?-    -- $discrete-    -    -- * Discrete time-varying values-    Discrete, initial, changes, value, stepperD,-    accumD, applyD,-    ) where--import Control.Applicative-import Reactive.Banana.Model--{------------------------------------------------------------------------------    Data Type-------------------------------------------------------------------------------}-{-$discrete--In an ideal world, users of functional reactive programming would-only need to use the notions of 'Behavior' and 'Event',-the first corresponding to value that vary in time-and the second corresponding to a stream of event ocurrences.--However, there is the problem of /incremental updates/.-Ideally, users would describe, say, the value of a GUI text field-as a 'Behavior' and the reactive-banana implementation would figure-out how to map this onto the screen without needless redrawing.-In other words, the screen should only be updated when the behavior changes.--While this would be easy to implement in simple cases,-it may not always suit the user;-there are many different ways of implementing-/incremental computations/.-But I don't know a unified theory for them, so-I have decided that the reactive-banana will give-/explicit control over updates to the user/-in the form of specialized data types like 'Discrete',-and shall not attempt to bake experimental optimizations into the 'Behavior' type.--To sum it up:--* You get explicit control over updates (the 'changes' function),--* but you need to learn a third data type 'Discrete',-which almost duplicates the 'Behavior' type.--* Even though the type 'Behavior' is more fundamental,-you will probably use 'Discrete' more often.--That said, 'Discrete' is not a new primitive type,-but built from exising types and combinators;-you are encouraged to look at the source code.--If you are an FRP implementor, I encourage you to find a better solution.-But if you are a user, you may want to accept the trade-off for now.---}---- | Like 'Behavior', the type 'Discrete' denotes a value that varies in time.--- However, unlike 'Behavior',--- it also provides a stream of events that indicate when the value has changed.--- In other words, we can now observe updates.-data Discrete f a = D {-        -- | Initial value.-        initial :: a,-        -- | Event that records when the value changes.-        -- Simultaneous events may be pruned for efficiency reasons.-        changes :: Event f a,-        -- | Behavior corresponding to the value. It is always true that-        -- -        -- > value x = stepper (initial x) (changes x)-        value   :: Behavior f a-        }---- | Construct a discrete time-varying value from an initial value and --- a stream of new values.-stepperD :: FRP f => a -> Event f a -> Discrete f a-stepperD x e = D { initial = x, changes = calm e, value = stepper x e}-    where-    -- in case of simultaneous occurence: keep only the last event?-    calm = id---- | Accumulate a stream of events into a discrete time-varying value.-accumD :: FRP f => a -> Event f (a -> a) -> Discrete f a-accumD x = stepperD x . accumE x---- | Apply a discrete time-varying value to a stream of events.--- --- > applyD = apply . value-applyD :: FRP f => Discrete f (a -> b) -> Event f a -> Event f b-applyD = apply . value---- | Overloading 'applyD'-instance FRP f => Apply (Discrete f) (Event f) where-    (<@>) = applyD---- | Functor instance-instance FRP f => Functor (Discrete f) where-    fmap f r = stepperD (f $ initial r) $ fmap f (changes r)---- | Applicative instance-instance FRP f => Applicative (Discrete f) where-    pure x    = D { initial = x, changes = never, value = pure x }-    df <*> dx = stepperD b e-        where-        b = initial df $ initial dx-        e = uncurry ($) <$> pairs-        pairs = accumE (initial df, initial dx) $-            (left <$> changes df) `union` (right <$> changes dx)-        -        left  f (_,x) = (f,x)-        right x (f,_) = (f,x)-
+ src/Reactive/Banana/Internal/AST.hs view
@@ -0,0 +1,222 @@+{-----------------------------------------------------------------------------+    Reactive-Banana+------------------------------------------------------------------------------}+{-# LANGUAGE GADTs, TypeFamilies, TupleSections, EmptyDataDecls,+    TypeSynonymInstances, FlexibleInstances #-}++module Reactive.Banana.Internal.AST where+-- Abstract syntax tree and assorted data types.++import Control.Applicative+import qualified Data.Vault as Vault+import System.IO.Unsafe++import Data.Hashable++import Reactive.Banana.Internal.InputOutput++{-----------------------------------------------------------------------------+    Abstract syntax tree+------------------------------------------------------------------------------}+-- Type families allow us to support multiple tags in the AST+type family   Event    t :: * -> *+type family   Behavior t :: * -> *++-- | Constructors for events.+data EventD t :: * -> * where+    Never     :: EventD t a+    UnionWith :: (a -> a -> a) -> Event t a -> Event t a -> EventD t a+    FilterE   :: (a -> Bool) -> Event t a -> EventD t a+    ApplyE    :: Behavior t (a -> b) -> Event t a -> EventD t b+    AccumE    :: a -> Event t (a -> a) -> EventD t a+    +    InputE    :: InputChannel a   -> EventD t a   -- represent external inputs+    InputPure :: InputChannel (EventModel a)+              -> EventD t a                       -- input for model implementation++-- | Constructors for behaviors.+data BehaviorD t :: * -> * where+    Stepper :: a -> Event t a -> BehaviorD t a++    InputB :: InputChannel a -> BehaviorD t a -- represent external inputs ++{-----------------------------------------------------------------------------+    Observable sharing+    +    Each constructor is paired with a @Node@ value.+    The @Node@ serves as a unique identifier and stores various keys+    into various vaults.+------------------------------------------------------------------------------}+data Pair f g a = Pair !(f a) (g a)++fstPair :: Pair f g a -> f a+fstPair (Pair x y) = x++-- | Type index indicating expressions with observable sharing+data Expr+type instance Event    Expr = Pair Node (EventD    Expr)+type instance Behavior Expr = Pair Node (BehaviorD Expr)++-- smart constructor that handles observable sharing+shareE :: EventD Expr a -> Event Expr a+shareE e = pair+    where+    {-# NOINLINE pair #-}+    -- mention argument to prevent let-floating+    pair = unsafePerformIO (fmap (flip Pair e) newNode)++shareB :: BehaviorD Expr a -> Behavior Expr a+shareB b = pair+    where+    {-# NOINLINE pair #-}+    pair = unsafePerformIO (fmap (flip Pair b) newNode)++{-----------------------------------------------------------------------------+    Smart constructors and class instances+------------------------------------------------------------------------------}+unE = id; unB = id++{- Note:+There is a fundamental problem with using Unique's for observable sharing.++The problem is the following:++    module Test where ..+    module Implementation where never = sharedE $ Never+    module Data.Unique++Imagine that the  Test  module contains an expression that contains sharing.+The  never  value contains a constant  Unique , which is evaluated as+soon as you evaluate  Test.expression  .+Now, reload the  Test  module. This will reset the counter for Data.Unique,+but it will *not* reset the Unique that is already contained in  never ,+because the CAF  never  itself will not be reset. This invariably leads to+a  Unique  being reused, leading to a program crash.++We solve the problem in Reactive.Banana.InputOutput+by using a better implementation of  Unique .++-}+{- Note:++Another good reason for not sharing `never` is that +it is *polymorphic*. The shared value may be instantiated to different types,+which is really bad.++-}+never             = shareE $ Never++unionWith f e1 e2 = shareE $ UnionWith f (unE e1) (unE e2)+filterE p e       = shareE $ FilterE p (unE e)+applyE b e        = shareE $ ApplyE (unB b) (unE e)+accumE acc e      = shareE $ AccumE acc (unE e)+inputE i          = shareE $ InputE i+inputPure i       = shareE $ InputPure i++stepperB acc e    = shareB $ Stepper acc (unE e)+inputB i          = shareB $ InputB i++mapE f  = applyE (pureB f)+pureB x = stepperB x never++applyB :: Behavior Expr (a -> b) -> Behavior Expr a -> Behavior Expr b+applyB (Pair _ (Stepper f fe)) (Pair _ (Stepper x xe)) =+    stepperB (f x) $ mapE (uncurry ($)) pair+    where+    pair = accumE (f,x) $ unionWith (.) (mapE changeL fe) (mapE changeR xe)+    changeL f (_,x) = (f,x)+    changeR x (f,_) = (f,x)+applyB _ _ = error "TODO: Don't know what to do with external behaviors."++-- instance Functor (Event Expr) where+instance Functor (Pair Node (EventD Expr)) where+    fmap   = mapE+-- instance Functor (Behavior Expr) where+instance Functor (Pair Node (BehaviorD Expr)) where+    fmap f = applyB (pureB f)++{-----------------------------------------------------------------------------+    The 'Node' type is used for observable sharing and must be defined here.+------------------------------------------------------------------------------}+-- | A 'Node' represents a unique identifier for an expression.+-- It actually contains keys for various 'Vault'.+--+-- TODO: Make a special case for the 'Never' constructor,+-- which cannot be shared.+data Node a+    = Node+    { -- use for Reactive.Banana.Internal.PushGraph+      keyValue   :: !(Vault.Key a)+    , keyFormula :: !(Vault.Key (FormulaD Nodes a))+    , keyOrder   :: !Unique+      -- use for Reactive.Banana.Internal.Model+    , keyModelE  :: !(Vault.Key (EventModel a))+    , keyModelB  :: !(Vault.Key (BehaviorModel a))+    }++newNode :: IO (Node a)+newNode = Node+    <$> Vault.newKey <*> Vault.newKey <*> newUnique+    <*> Vault.newKey <*> Vault.newKey++{-----------------------------------------------------------------------------+    Reactive.Banana.Internal.Model+------------------------------------------------------------------------------}+-- we have to define the interpretation types here+type EventModel a    = [Maybe a]+data BehaviorModel a = StepperB a (EventModel a)++{-----------------------------------------------------------------------------+    Reactive.Banana.Internal.PushGraph+------------------------------------------------------------------------------}+data Nodes+type instance Event    Nodes = Node+type instance Behavior Nodes = Node++-- | Formula that represents events and behaviors as one entity+data FormulaD t a where+    E :: EventD t a    -> FormulaD t a+    B :: BehaviorD t a -> FormulaD t a++caseFormula :: (EventD t a -> c) -> (BehaviorD t a -> c) -> FormulaD t a -> c+caseFormula e b (E x) = e x+caseFormula e b (B x) = b x++type family Formula t :: * -> *+type instance Formula Expr  = Pair Node (FormulaD Expr)+type instance Formula Nodes = Node++-- Helper class for embedding polymorphically in the type index+class ToFormula t where+    -- e :: Event    t a -> Formula t a+    -- b :: Behavior t a -> Formula t a+    +    ee :: Event t a    -> SomeFormula t+    bb :: Behavior t a -> SomeFormula t++instance ToFormula Expr where+    ee (Pair node e1) = Exists (Pair node $ E e1)+    bb (Pair node b1) = Exists (Pair node $ B b1)++instance ToFormula Nodes where+    ee node = Exists node+    bb node = Exists node+++-- | Formula, existentially quantified over the result type+data SomeFormula t where+    Exists :: Formula t a -> SomeFormula t+type SomeNode = SomeFormula Nodes++-- instances to store  SomeNode  in efficient maps+instance Eq SomeNode where+    (Exists x) == (Exists y) = (keyOrder x) == (keyOrder y)+instance Hashable SomeNode where+    hash (Exists x) = hash (keyOrder x)++instance Eq (SomeFormula Expr) where+    (Exists (Pair x _)) == (Exists (Pair y _)) = (keyOrder x) == (keyOrder y)+instance Hashable (SomeFormula Expr) where+    hash (Exists (Pair x _)) = hash (keyOrder x)++
+ src/Reactive/Banana/Internal/InputOutput.hs view
@@ -0,0 +1,91 @@+{-----------------------------------------------------------------------------+    Reactive Banana+------------------------------------------------------------------------------}+module Reactive.Banana.Internal.InputOutput (+    -- * Synopsis+    -- | Manage the input and output of event graphs.+    +    -- * Input+    -- | Utilities for managing heterogenous input values.+    Channel, InputChannel, InputValue,+    +    newInputChannel, getChannel,+    fromValue, toValue,+    +    -- * Output+    -- | Stepwise execution of an event graph.+    Automaton(..), fromStateful, unfoldAutomaton,++    -- * Uniques+    -- | Doesn't belong here, but we also have some stuff about uniques+    -- that we use for observable sharing.+    Unique, newUnique,+    ) where++import Control.Applicative+import Control.Exception (evaluate)++import qualified Data.Unique as Unique+import qualified Data.Vault  as Vault+import Data.Hashable+import System.Mem.StableName++{-----------------------------------------------------------------------------+    Better Uniques+------------------------------------------------------------------------------}+type ReallyUnique = StableName Unique.Unique+type Unique = ReallyUnique++-- instance Hashable Unique++newUnique :: IO Unique+newUnique = do+    x <- Unique.newUnique+    evaluate x+    makeStableName x++{-----------------------------------------------------------------------------+    Storing heterogenous input values+------------------------------------------------------------------------------}+type Channel  = ReallyUnique    -- identifies an input+type Key      = Vault.Key       -- key to retrieve a value+type Value    = Vault.Vault     -- value storage++data InputChannel a  = InputChannel { getChannelC :: Channel, getKey :: Key a }+data InputValue      = InputValue   { getChannelV :: Channel, getValue :: Value }++newInputChannel :: IO (InputChannel a)+newInputChannel = InputChannel <$> newUnique <*> Vault.newKey++fromValue :: InputChannel a -> InputValue -> Maybe a+fromValue i v = Vault.lookup (getKey i) (getValue v)++toValue :: InputChannel a -> a -> InputValue+toValue i a = InputValue (getChannelC i) $ Vault.insert (getKey i) a Vault.empty++-- convenience class for overloading+class HasChannel a where+    getChannel :: a -> Channel+instance HasChannel (InputChannel a) where getChannel = getChannelC+instance HasChannel (InputValue) where getChannel = getChannelV+++{-----------------------------------------------------------------------------+    Stepwise execution+------------------------------------------------------------------------------}+-- Automaton that takes input values and produces a result+data Automaton a = Step { runStep :: [InputValue] -> IO (Maybe a, Automaton a) }++fromStateful :: ([InputValue] -> s -> IO (Maybe a,s)) -> s -> Automaton a+fromStateful f s = Step $ \i -> do+    (a,s') <- f i s+    return (a, fromStateful f s')++-- | Apply an automaton to a list of input values+unfoldAutomaton :: Automaton b -> InputChannel a -> [a] -> IO [Maybe b]+unfoldAutomaton _    _ []     = return []+unfoldAutomaton auto i (x:xs) = do+    (b, auto) <- runStep auto $ [toValue i x]+    bs        <- unfoldAutomaton auto i xs+    return (b:bs)+    
+ src/Reactive/Banana/Internal/Model.hs view
@@ -0,0 +1,141 @@+{-----------------------------------------------------------------------------+    Reactive-Banana+------------------------------------------------------------------------------}+{-# LANGUAGE GADTs #-}+module Reactive.Banana.Internal.Model (+    -- * Synopsis+    -- | Model implementation of the abstract syntax tree.+    +    -- * Description+    -- $model++    -- Combinators+    -- Event(..), Behavior(..),+    -- never, filterE, unionWith, applyE, accumE, stepper,+   +    -- * Interpretation+    interpretModel,+    ) where++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Control.Monad.Trans.State+import qualified Data.Vault as Vault++import qualified Reactive.Banana.Internal.AST as AST+import Reactive.Banana.Internal.InputOutput++{-$model++This module contains the model implementation for the primitive combinators+defined "Reactive.Banana.Internal.AST"+which in turn are the basis for the official combinators+documented in "Reactive.Banana.Combinators".++This module does not export any combinators,+you have to look at the source code to make use of it.+(If there is no link to the source code at every type signature,+then you have to run cabal with --hyperlink-source flag.)++This model is /authoritative/: when observed with the 'interpretModel' function,+both the actual implementation and its model /must/ agree on the result.+Note that this must also hold for recursive and partial definitions+(at least in spirit, I'm not going to split hairs over @_|_@ vs @\\_ -> _|_@).++Concerning time and space complexity, the model is not authoritative, however.+Implementations are free to be much more efficient.+-}++{-----------------------------------------------------------------------------+    Combinators+------------------------------------------------------------------------------}+-- due to observable sharing, the types have to be imported from+-- the module Reactive.Banana.Internal.AST+type Event a    = AST.EventModel a     -- = [Maybe a]+type Behavior a = AST.BehaviorModel a  -- = StepperB a (Event a)++never :: Event a+never = repeat Nothing++filterE :: (a -> Bool) -> Event a -> Event a+filterE p = map (>>= \x -> if p x then Just x else Nothing)++unionWith :: (a -> a -> a) -> Event a -> Event a -> Event a+unionWith f = zipWith g+    where+    g (Just x) (Just y) = Just $ f x y+    g (Just x) Nothing  = Just x+    g Nothing  (Just y) = Just y+    g Nothing  Nothing  = Nothing++applyE :: Behavior (a -> b) -> Event a -> Event b+applyE _                   []     = []+applyE (AST.StepperB f fe) (x:xs) = fmap f x : applyE (step f fe) xs+    where+    step a (Nothing:b) = stepper a b+    step _ (Just a :b) = stepper a b++accumE :: a -> Event (a -> a) -> Event a+accumE x []           = []+accumE x (Nothing:fs) = Nothing : accumE x fs+accumE x (Just f :fs) = let y = f x in y `seq` (Just y:accumE y fs) ++stepper :: a -> [Maybe a] -> Behavior a+stepper = AST.StepperB++{-----------------------------------------------------------------------------+    Interpretation, pays attention to observable sharing+------------------------------------------------------------------------------}+-- state monad for evaluation+type Eval = State Vault.Vault++-- | Interpret an event graph with the model implementation.+-- Mainly useful for testing library internals.+interpretModel+    :: (AST.Event AST.Expr a -> AST.Event AST.Expr b)+    -> Event a -> IO (Event b)+interpretModel f input = do+    i0 <- newInputChannel+    +    let+        evalE :: AST.EventD AST.Expr a -> Eval (Event a)+        evalE (AST.Never)             = return $ never+        evalE (AST.UnionWith f e1 e2) = unionWith f <$> goE e1 <*> goE e2+        evalE (AST.FilterE p e)       = filterE p   <$> goE e+        evalE (AST.ApplyE b e )       = applyE      <$> goB b  <*> goE e+        evalE (AST.AccumE x e )       = accumE x    <$> goE e+        evalE (AST.InputPure i)       =+            return $ maybe err id $ fromValue i (toValue i0 input)+            where err = error "Reactive.Banana.PushIO.interpretModel: internal error: Input"+        evalE _                       =+            error "Reactive.Banana.PushIO.interpretModel: internal error: E"++        evalB :: AST.BehaviorD AST.Expr a -> Eval (Behavior a)+        evalB (AST.Stepper x e) = stepper x <$> goE e+        evalB _                 =+            error "Reactive.Banana.PushIO.interpretModel: internal error: B"++        goE :: AST.Event AST.Expr a -> Eval (Event a)+        goE (AST.Pair node e) = do+            values <- get+            case Vault.lookup (AST.keyModelE node) values of+                Nothing -> mfix $ \v -> do+                    modify $ Vault.insert (AST.keyModelE node) v+                    evalE e+                Just v  -> return v++        goB :: AST.Behavior AST.Expr a -> Eval (Behavior a)+        goB (AST.Pair node b) = do+            values <- get+            case Vault.lookup (AST.keyModelB node) values of+                Nothing -> mfix $ \v -> do+                    modify $ Vault.insert (AST.keyModelB node) v+                    evalB b+                Just v  -> return v+    +    return $+        zipWith const+            (evalState (goE $ f $ AST.inputPure i0) Vault.empty)+            input+
+ src/Reactive/Banana/Internal/PushGraph.hs view
@@ -0,0 +1,410 @@+{-----------------------------------------------------------------------------+    Reactive-Banana+------------------------------------------------------------------------------}+{-# LANGUAGE GADTs, TypeFamilies, RankNTypes, TypeOperators,+             TypeSynonymInstances, FlexibleInstances,+             ScopedTypeVariables #-}++module Reactive.Banana.Internal.PushGraph (+    -- * Synopsis+    -- | Push-driven implementation.++    compileToAutomaton+    ) where++import Control.Applicative+import Control.Arrow (first)+import Control.Category+import Prelude hiding ((.),id)++import Data.Label+import Data.Maybe+import Data.Monoid (Dual, Endo, Monoid(..))+import qualified Data.Vault as Vault++import Data.Hashable+import qualified Data.HashMap.Strict as Map+import qualified Data.HashSet as Set++import Reactive.Banana.Internal.AST+import Reactive.Banana.Internal.InputOutput+import Reactive.Banana.Internal.TotalOrder as TotalOrder++import Debug.Trace++type Map = Map.HashMap+type Set = Set.HashSet++{-----------------------------------------------------------------------------+    Representation of the dependency graph+    and associated lenses+------------------------------------------------------------------------------}+-- Dependency graph+data Graph b+    = Graph+    { grFormulas  :: Formulas                -- formulas for calculation+    , grChildren  :: Map SomeNode [SomeNode] -- reverse dependencies+    , grEvalOrder :: EvalOrder               -- evaluation order+    , grOutput    :: Node b                  -- root node+    , grInputs    :: Inputs                  -- input dispatcher+    }+type Formulas  = Vault.Vault            -- mapping from nodes to formulas+type EvalOrder = TotalOrder SomeNode    -- evaluation order+type Values    = Vault.Vault            -- current event values+type Inputs    = Map Channel [SomeNode] -- mapping from input channels to nodes++-- | Turn a 'Vault.Key' into a lens for the vault+vaultLens :: Vault.Key a -> (Vault.Vault :-> Maybe a)+vaultLens key = lens (Vault.lookup key) (adjust)+    where+    adjust Nothing  = Vault.delete key+    adjust (Just x) = Vault.insert key x ++-- | Formula used to calculate the value at a node.+formula :: Node a -> (Graph b :-> Maybe (FormulaD Nodes a))+formula node = vaultLens (keyFormula node) . formulaLens+    where formulaLens = lens grFormulas (\x g -> g { grFormulas = x})++-- | All nodes that directly depend on this one via the formula.+children :: Node a -> (Graph b :-> [SomeNode])+children node = lens (Map.lookupDefault [] (Exists node) . grChildren)+    (error "TODO: can't set children yet")++-- | Current value for a node.+value :: Node a -> (Values :-> Maybe a)+value node = vaultLens (keyValue node)++{-----------------------------------------------------------------------------+    Operations specific to the DSL+------------------------------------------------------------------------------}+-- | Extract the dependencies of a node from its formula.+-- (boilerplate)+dependencies :: ToFormula t => FormulaD t a -> [SomeFormula t]+dependencies = caseFormula goE goB+    where+    goE :: ToFormula t => EventD t a -> [SomeFormula t]+    goE (Never)             = []+    goE (UnionWith f e1 e2) = [ee e1,ee e2]+    goE (FilterE _ e1)      = [ee e1]+    goE (ApplyE  b1 e1)     = [bb b1, ee e1]+    goE (AccumE  _ e1)      = [ee e1]+    goE _                   = []++    goB :: ToFormula t => BehaviorD t a -> [SomeFormula t]+    goB (Stepper x e1)      = [ee e1]+    goB _                   = []++-- | Nodes whose *current* values are needed to calculate+-- the current value of the given node.+-- (boilerplate)+dependenciesEval :: ToFormula t => FormulaD t a -> [SomeFormula t]+dependenciesEval (E (ApplyE b e)) = [ee e]+dependenciesEval formula          = dependencies formula ++-- | Replace expressions by nodes.+-- (boilerplate)+toFormulaNodes :: FormulaD Expr a -> FormulaD Nodes a+toFormulaNodes = caseFormula (E . goE) (B . goB)+    where+    node :: Pair Node f a -> Node a+    node = fstPair+    +    goE :: forall a. EventD Expr a -> EventD Nodes a+    goE (Never)             = Never+    goE (UnionWith f e1 e2) = UnionWith f (node e1) (node e2)+    goE (FilterE p e)       = FilterE p (node e)+    goE (ApplyE  b e)       = ApplyE (node b) (node e)+    goE (AccumE  x e)       = AccumE x (node e)+    goE (InputE x)          = InputE x++    goB :: BehaviorD Expr a -> BehaviorD Nodes a+    goB (Stepper x e)       = Stepper x (node e)+    goB (InputB x)          = InputB x+++-- Evaluation++-- | Evaluate the current value of a given event expression.+calculateE+    :: forall a b.+       (forall e. Node e -> Maybe e)  -- retrieve current event values+    -> (forall b. Node b -> b)        -- retrieve old behavior values+    -> Node a                         -- node ID+    -> EventD Nodes a                 -- formula to evaluate+    -> ( Maybe a                      -- current event value+       , Graph b -> Graph b)          -- (maybe) change formulas in the graph +calculateE valueE valueB node =+    maybe (Nothing,id) (\(x,f) -> (Just x, f)) . goE+    where+    goE :: EventD Nodes a -> Maybe (a, Graph b -> Graph b)+    goE (Never)             = nothing+    goE (UnionWith f e1 e2) = case (valueE e1, valueE e2) of+        (Just e1, Just e2) -> just $ f e1 e2+        (Just e1, Nothing) -> just e1+        (Nothing, Just e2) -> just e2+        (Nothing, Nothing) -> nothing+    goE (FilterE p e)       = valueE e >>=+        \e -> if p e then just e else nothing+    goE (ApplyE  b e)       = (just . (valueB b $)) =<< valueE e+    goE (AccumE  x e)       = case valueE e of+        Nothing -> just x+        Just f  -> let y = f x in+            Just (y, set (formula node) . Just $ E (AccumE y e))+    goE (InputE _)          = -- input values can be retrieved by node+        just =<< valueE node++just x  = Just (x, id)+nothing = Nothing++-- | Evalute the new value of a given behavior expression+calculateB+    :: forall a b.+       (forall e. Node e -> Maybe e) -- retrieve current event values+    -> Node a                        -- node ID+    -> BehaviorD Nodes a             -- formula to evaluate+    -> Graph b -> Graph b            -- (maybe) change formulas in the graph+calculateB valueE node = maybe id id . goB+    where+    goB :: BehaviorD Nodes a -> Maybe (Graph b -> Graph b) +    goB (Stepper x e)     =+        (\y -> set (formula node) $ Just $ B (Stepper y e)) <$> valueE e+    goB (InputB x)        = error "TODO"+++{-----------------------------------------------------------------------------+    Building the dependency graph+------------------------------------------------------------------------------}+-- | Build full graph from an expression.+buildGraph :: Formula Expr b -> Graph b+buildGraph expr = graph+    where+    graph = Graph+        { grFormulas  = grFormulas+        , grChildren  = buildChildren  (Exists root) grFormulas+        , grEvalOrder = buildEvalOrder graph+        , grOutput    = root+        , grInputs    = buildInputs    (Exists root) grFormulas+        }+    grFormulas = buildFormulas (Exists expr)+    root       = fstPair expr++-- | Build a graph of formulas from an expression+buildFormulas :: SomeFormula Expr -> Formulas+buildFormulas expr =+    unfoldGraphDFSWith leftComposition f expr $ Vault.empty+    where+    f (Exists (Pair node formula)) =+        ( \formulas -> Vault.insert (keyFormula node) formula' formulas+        , dependencies formula )+        where+        formula' = toFormulaNodes formula++-- | Build reverse dependencies, starting from one node.+buildChildren :: SomeNode -> Formulas -> Map SomeNode [SomeNode]+buildChildren root formulas =+    unfoldGraphDFSWith leftComposition f root $ Map.empty+    where+    f (Exists node) = (addChild deps, deps)+        where+        addChild      = concatenate . map (\node -> Map.insertWith (++) node [child])+        child         = Exists node :: SomeNode+        Just formula' = getFormula' node formulas+        deps          = dependencies formula'++getFormula' node formulas = Vault.lookup (keyFormula node) formulas++concatenate :: [a -> a] -> (a -> a)+concatenate = foldr (.) id++-- | Start at some node and update the evaluation order of+-- the node and all of its dependencies.+updateEvalOrder :: SomeNode -> Formulas -> EvalOrder -> EvalOrder+updateEvalOrder = error "TODO"++-- | Build evaluation order from scratch+-- = topological sort+buildEvalOrder :: Graph a -> EvalOrder+buildEvalOrder graph =+    -- we have to build an evaluation order for the root node+    -- and for all the dependencies of a behavior+    TotalOrder.fromAscList $+        concatMap (\x -> unfoldGraphDFSWith leftComposition f x [])+                  (root:findBehaviors)+    where+    root = Exists $ grOutput graph+    f (Exists node) = ((Exists node:), dependenciesEval formula')+        where Just formula' = get (formula node) graph+    +    -- find all the behavior nodes in the graph+    findBehaviors :: [SomeNode]+    findBehaviors = traverseNodes g graph+        where+        g :: Node a -> FormulaD Nodes a -> [SomeNode]+        g node (B _) = [Exists node]+        g _    _     = []++-- | Build collection of input nodes from scratch+buildInputs :: SomeNode -> Formulas -> Inputs+buildInputs root formulas =+    unfoldGraphDFSWith leftComposition f root Map.empty+    where+    f (Exists node) = (addInput, dependencies formula')+        where+        Just formula' = getFormula' node formulas+        addInput :: Inputs -> Inputs+        addInput = case formula' of+            E (InputE i) -> Map.insertWith (++) (getChannel i) [Exists node]+            _            -> id++-- | Traverse all nodes of the graph.+-- The order in which this happens is left unspecified.+traverseNodes+    :: Monoid t+    => (forall a. Node a -> FormulaD Nodes a -> t) -- map nodes to monoid values+    -> Graph b+    -> t+traverseNodes f graph =+    unfoldGraphDFSWith reifyMonoid g (Exists $ grOutput graph)+    where+    g (Exists node) = (f node formula', dependencies formula')+        where Just formula' = get (formula node) graph++{-----------------------------------------------------------------------------+    Generic Graph Traversals+------------------------------------------------------------------------------}+-- | Dictionary for defining monoids on the fly.+data MonoidDict t = MonoidDict t (t -> t -> t)++reifyMonoid :: Monoid t => MonoidDict t+reifyMonoid = MonoidDict mempty mappend++-- | Unfold a graph,+-- i.e. unfold a given state  s  into a concatenation of monoid values+-- while ignoring duplicate states.+-- Depth-first order.+unfoldGraphDFSWith+    :: forall s t. (Hashable s, Eq s) => MonoidDict t -> (s -> (t,[s])) -> s -> t+unfoldGraphDFSWith (MonoidDict empty append) f s = go Set.empty [s]+    where+    go :: Set s -> [s] -> t+    go seen []      = empty+    go seen (x:xs)+        | x `Set.member` seen = go seen xs+        | otherwise           = t `append` go (Set.insert x seen) (ys++xs)+        where+        (t,ys) = f x++-- | Monoid of endomorphisms, leftmost function is applied *last*.+leftComposition :: MonoidDict (a -> a)+leftComposition = MonoidDict id (flip (.))++{-+testDFS :: Int -> [Int]+testDFS = unfoldGraphDFSWith (MonoidDict [] (++)) go+    where go n = ([n],if n <= 0 then [] else [n-2,n-1])+-}++{-----------------------------------------------------------------------------+    Reduction and Evaluation+------------------------------------------------------------------------------}+-- type Queue = [SomeNode]++-- | Perform evaluation steps until all values have percolated through the graph.+evaluate :: Queue q => q SomeNode -> Graph b -> Values -> (Maybe b, Graph b)+evaluate startQueue startGraph startValues =+    (get (value (grOutput startGraph)) endValues, endGraph)+    where    +    (_,endValues,endGraph) =+        until (isEmpty . queue) step (startQueue,startValues,startGraph)+    +    queue (q,_,_) = q+    step  (q,v,g) = (q',v',f g)+        where (q',v',f) = evaluationStep startGraph q v++-- | Perform a single evaluation step.+evaluationStep+    :: forall q b. Queue q+    => Graph b                      -- initial graph shape+    -> q SomeNode                   -- queue of nodes to process+    -> Values                       -- current event values+    -> (q SomeNode, Values, Graph b -> Graph b)+evaluationStep graph queue values = case minView queue of+        Just (Exists node, queue) -> go node queue+        Nothing                   -> error "evaluationStep: queue empty"+    where+    go :: forall a b.+        Node a -> q SomeNode -> (q SomeNode, Values, Graph b -> Graph b)+    go node queue =+        let -- lookup functions+            valueE :: forall e. Node e -> Maybe e+            valueE node = get (value node) values+            valueB :: forall b. Node b -> b+            valueB node = case get (formula node) graph of+                Just (B (Stepper b _)) -> b+                _               -> error "evaluationStep: behavior not found"++            err = error "evaluationStep: formula not found"+        in -- evaluation+            case maybe err id $ get (formula node) graph of+            B formulaB ->   -- evalute behavior+                (queue, values, calculateB valueE node formulaB)+            E formulaE ->   -- evaluate event+                let -- calculate current value+                    (maybeval, f) =+                        calculateE valueE valueB node formulaE+                    -- set value if applicable+                    setValue = case maybeval of+                        Just x  -> set (value node) (Just x)+                        Nothing -> id+                    -- evaluate children only if node doesn't return Nothing+                    setQueue = case maybeval of+                        Just _  -> insertList $ get (children node) graph+                        Nothing -> id+                in (setQueue queue, setValue values, f)++{-----------------------------------------------------------------------------+    Convert into an automaton+------------------------------------------------------------------------------}+compileToAutomaton :: Event Expr b -> IO (Automaton b)+compileToAutomaton expr = return $ fromStateful automatonStep $ buildGraph (e expr)+    where+    e :: Event Expr b -> Formula Expr b+    e (Pair n x) = Pair n (E x)+    +-- single step function of the automaton+automatonStep :: [InputValue] -> Graph b -> IO (Maybe b, Graph b)+automatonStep inputs graph = return (b, graph')+    where    +    -- figure out nodes corresponding to input values+    inputNodes :: [(InputValue, SomeNode)]+    inputNodes =+        [ (i, node)+        | i <- inputs+        , nodes <- maybeToList $ Map.lookup (getChannel i) (grInputs graph)+        , node  <- nodes]++    -- fill up values for start/input nodes+    startValues = foldr insertInput Vault.empty inputNodes+    -- insert a single input into the start values+    insertInput :: (InputValue, SomeNode) -> Values -> Values+    insertInput (i,somenode) = maybe id id $+        withInputNode somenode (\node channel ->+            maybe id (Vault.insert (keyValue node)) $ fromValue channel i+            )  ++    -- unpack  InputE  node if applicable+    withInputNode :: SomeNode+        -> (forall a. Node a -> InputChannel a -> b) -> Maybe b+    withInputNode somenode f = case somenode of+        Exists node ->+            let theformula = get (formula node) graph+            in case theformula of+                Just (E (InputE channel)) -> Just $ f node channel+                _ -> Nothing+    +    -- perform evaluation+    (b,graph') = withTotalOrder (grEvalOrder graph) $ \qempty ->+        evaluate (insertList (map snd inputNodes) qempty) graph startValues+++
+ src/Reactive/Banana/Internal/TotalOrder.hs view
@@ -0,0 +1,123 @@+{-----------------------------------------------------------------------------+    Reactive Banana+------------------------------------------------------------------------------}+{-# LANGUAGE Rank2Types, BangPatterns #-}+module Reactive.Banana.Internal.TotalOrder (+    -- * Synopsis+    -- | Data structure that represents a total order.+    +    -- * TotalOrder+    TotalOrder, TotalOrderZipper,+    open, close, fromAscList, ascend, descend, insertBeforeFocus, delete,+    withTotalOrder,+    +    -- * Queue+    Queue(..), insertList, isEmpty,+    ) where+++import Control.Applicative+import Control.Arrow (second)++import qualified Data.List+import Data.Maybe+import Data.Ord++import Data.Hashable+import qualified Data.HashMap.Strict as Map+import qualified Data.Set as Set++type Map = Map.HashMap+type Set = Set.Set++{-----------------------------------------------------------------------------+    Total Order implementation+------------------------------------------------------------------------------}+-- Data type representing a total order between elements+-- It's simply an ordered list of elements+newtype TotalOrder a = TO { unTO :: Map a Int }++-- Zipper variant of a total order.+data TotalOrderZipper a = TOZ { down :: [a], up :: [a] }++open  :: TotalOrder a -> TotalOrderZipper a+open (TO order) = TOZ { down = [], up = Map.keys order }++close :: (Hashable a, Eq a) => TotalOrderZipper a -> TotalOrder a+close order = TO $ Map.fromList $ zip (reverse (down order) ++ up order) [1..]++fromAscList :: (Hashable a, Eq a) => [a] -> TotalOrder a+fromAscList xs = close $ TOZ { down = [], up = xs }+++-- move to the next larger element+ascend       :: TotalOrderZipper a -> TotalOrderZipper a+ascend (TOZ xs []    ) = TOZ xs     []+ascend (TOZ xs (y:ys)) = TOZ (y:xs) ys++-- move to the next smaller element+descend      :: TotalOrderZipper a -> TotalOrderZipper a+descend (TOZ []     ys) = TOZ [] ys+descend (TOZ (x:xs) ys) = TOZ xs (x:ys)++-- insert an element before the current one+insertBeforeFocus :: a -> TotalOrderZipper a -> TotalOrderZipper a+insertBeforeFocus x (TOZ xs ys) = TOZ (x:xs) ys++-- delete an element from a total order+delete       :: Eq a => a -> TotalOrderZipper a -> TotalOrderZipper a+delete x (TOZ xs ys) = TOZ (delete' x xs) (delete' x ys)+    where delete' = Data.List.delete++{-----------------------------------------------------------------------------+   Queue based on a total order+------------------------------------------------------------------------------}+-- | Obtain a queue based on a particular total order.+--+-- The type system ensures that the queue is only used temporarily.+-- The argument passed to the function is the empty queue.+withTotalOrder :: TotalOrder a -> (forall q. Queue q => q a -> b) -> b+withTotalOrder order f = f empty+    where empty = Q { order = order, queue = Set.empty }++-- public interface+class Queue q where+    insert  :: (Hashable a, Eq a) => a -> q a -> q a+    minView :: q a -> Maybe (a, q a)+    size    :: q a -> Int++-- | Check whether a queue is empty.+isEmpty :: Queue q => q a -> Bool+isEmpty = isNothing . minView++-- | Insert a collection of elements+insertList :: (Queue q, Hashable a, Eq a) => [a] -> q a -> q a+insertList xs q = foldl (flip insert) q xs++-- concrete implementation+data MyQueue a = Q { order :: TotalOrder a, queue :: Set (Pair Int a) }++data Pair a b = Pair !a b+fstPair (Pair a _) = a+instance Eq a => Eq (Pair a b) where+    x == y = fstPair x == fstPair y+instance Ord a => Ord (Pair a b) where+    compare = comparing fstPair++-- set the queue field+setQueue :: MyQueue a -> Set (Pair Int a) -> MyQueue a+setQueue q b = q { queue = b }++-- find the index of a particular element in a Total Order+position :: (Hashable a, Eq a) => TotalOrder a -> a -> Int+position (TO order) x = pos+    where Just pos = Map.lookup x order++instance Queue MyQueue where+    insert x q = q { queue = Set.insert (Pair pos x) (queue q) }+        where pos = position (order q) x+    minView  q = f <$> Set.minView (queue q)+        where f (Pair _ a,set) = (a, setQueue q set)+    size     q = Set.size (queue q)++
− src/Reactive/Banana/Model.hs
@@ -1,290 +0,0 @@-{------------------------------------------------------------------------------    Reactive Banana-    -    Class interface + Semantic model-------------------------------------------------------------------------------}-{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, EmptyDataDecls,-  MultiParamTypeClasses #-}-module Reactive.Banana.Model (-    -- * Synopsis-    -- | Combinators for building event networks and their semantics.-    -    -- * Core Combinators-    module Control.Applicative,-    FRP(..),-    Event, Behavior,-    -- $classes-    -    -- * Derived Combinators-    whenE, filterJust, mapAccum, Apply(..),-    -    -- * Model implementation-    Model,-    Time, interpretTime, interpret,-    ) where--import Control.Applicative-import qualified Data.List-import Data.Maybe-import Prelude hiding (filter)-import Data.Monoid--{------------------------------------------------------------------------------    Class interface-------------------------------------------------------------------------------}-data family Event f    :: * -> *-data family Behavior f :: * -> *--{- | The 'FRP' class defines the primitive API for functional reactive programming.-Each instance 'f' defines two type constructors @Event f@ and @Behavior f@-and corresponding combinators.--@Event f a@ represents a stream of events as they occur in time.-Semantically, you can think of @Event f a@ as an infinite list of values-that are tagged with their corresponding time of occurence,--> type Event f a = [(Time,a)]--@Behavior f a@ represents a value that varies in time. Think of it as--> type Behavior f a = Time -> a--While these type synonyms are the way you should think about-'Behavior' and 'Event', they are a bit vague for formal manipulation.-To remedy this, the library provides a very simple model implementation,-called 'Model'.-This model is /authoritative/: every instance of the 'FRP' class /must/-give the same results as the model when observed with the 'interpret' function.-Note that this must also hold for recursive and partial definitions-(at least in spirit, I'm not going to split hairs over @_|_@ vs @\\_ -> _|_@).--Concerning time and space complexity, the model is not authoritative, however.-Implementations are free to be much more efficient.--Minimal complete definition of the 'FRP' class: One of 'filter' or 'filterApply'-and one of 'accumB' or 'stepper'.---}--class (Functor (Event f),-       Functor (Behavior f), Applicative (Behavior f)) => FRP f where-    -    -- | Event that never occurs.-    -- Think of it as @never = []@.-    never    :: Event f a-    -    -- | Merge two event streams of the same type.-    -- In case of simultaneous occurrences, the left argument comes first.-    -- Think of it as-    ---    -- > union ((timex,x):xs) ((timey,y):ys)-    -- >    | timex <= timey = (timex,x) : union xs ((timey,y):ys)-    -- >    | timex >  timey = (timey,y) : union ((timex,x):xs) ys-    union    :: Event f a -> Event f a -> Event f a-    -    -- | Apply a time-varying function to a stream of events.-    -- Think of it as-    -- -    -- > apply bf ex = [(time, bf time x) | (time, x) <- ex]-    apply    :: Behavior f (a -> b) -> Event f a -> Event f b---    -- | Allow all events that fulfill the predicate, discard the rest.-    -- Think of it as-    -- -    -- > filterE p es = [(time,a) | (time,a) <- es, p a]-    filterE   :: (a -> Bool) -> Event f a -> Event f a-    -    -- | Allow all events that fulfill the time-varying predicate, discard the rest.-    -- It's a slight generalization of 'filterE'.-    filterApply :: Behavior f (a -> Bool) -> Event f a -> Event f a-    -    -    -- Accumulation.-    -- Note: all accumulation functions are strict in the accumulated value!-    -- acc -> (x,acc) is the order used by  unfoldr  and  State--    -- | Construct a time-varying function from an initial value and -    -- a stream of new values. Think of it as-    ---    -- > stepper x0 ex = \time -> last (x0 : [x | (timex,x) <- ex, timex < time])-    -- -    -- Note that the smaller-than-sign in the comparision @timex < time@ means -    -- that the value of the behavior changes \"slightly after\"-    -- the event occurrences. This allows for recursive definitions.-    -- -    -- Also note that in the case of simultaneous occurrences,-    -- only the last one is kept.-    stepper :: a -> Event f a -> Behavior f a--    -- | The 'accumB' function is similar to a /strict/ left fold, 'foldl''.-    -- It starts with an initial value and combines it with incoming events.-    -- For example, think-    ---    -- > accumB "x" [(time1,(++"y")),(time2,(++"z"))]-    -- >    = stepper "x" [(time1,"xy"),(time2,"xyz")]-    -- -    -- Note that the value of the behavior changes \"slightly after\"-    -- the events occur. This allows for recursive definitions.-    accumB   :: a -> Event f (a -> a) -> Behavior f a-    -    -- | The 'accumE' function accumulates a stream of events.-    -- Example:-    ---    -- > accumE "x" [(time1,(++"y")),(time2,(++"z"))]-    -- >    = [(time1,"xy"),(time2,"xyz")]-    ---    -- Note that the output events are simultaneous with the input events,-    -- there is no \"delay\" like in the case of 'accumB'.-    accumE   :: a -> Event f (a -> a) -> Event f a-    -    -    -- implementation filter-    filterE p = filterApply (pure p)-    filterApply bp = fmap snd . filterE fst . apply ((\p a-> (p a,a)) <$> bp)    -    -    -- implementation accumulation-    accumB  acc = stepper acc . accumE acc-    stepper acc = accumB acc . fmap const--{-$classes--/Further combinators that Haddock can't document properly./--> instance FRP f => Monoid (Event f a)--The combinators 'never' and 'union' turn 'Event' into a monoid.--> instance FPR f => Applicative (Behavior f)--'Behavior' is an applicative functor. In particular, we have the following functions.--> pure :: FRP f => a -> Behavior f a--The constant time-varying value. Think of it as @pure x = \\time -> x@.--> (<*>) :: FRP f => Behavior f (a -> b) -> Behavior f a -> Behavior f b--Combine behaviors in applicative style.-Think of it as @bf \<*\> bx = \\time -> bf time $ bx time@.---}--instance FRP f => Monoid (Event f a) where-    mempty  = never-    mappend = union--{------------------------------------------------------------------------------    Derived Combinators-------------------------------------------------------------------------------}--- | Variant of 'filterApply'.-whenE :: FRP f => Behavior f Bool -> Event f a -> Event f a-whenE bf = filterApply (const <$> bf)---- | Variant of 'filterE'. Keep only the 'Just' values.-filterJust :: FRP f => Event f (Maybe a) -> Event f a-filterJust = fmap (maybe err id) . filterE isJust-    where err = error "Reactive.Banana.Model.filterJust: Internal error. :("---- | Efficient combination of 'accumE' and 'accumB'.-mapAccum :: FRP f => acc -> Event f (acc -> (x,acc)) -> (Event f x, Behavior f acc)-mapAccum acc ef = (fst <$> e, stepper acc (snd <$> e))-    where e = accumE (undefined,acc) ((. snd) <$> ef)---infixl 4 <@>, <@---- | Class for overloading the 'apply' function.-class (Functor f, Functor g) => Apply f g where-    -- | Infix operation for the 'apply' function, similar to '<*>'-    (<@>) :: f (a -> b) -> g a -> g b-    -- | Convenience function, similar to '<*'-    (<@)  :: f a -> g b -> g a-    -    f <@ g = (const <$> f) <@> g --instance FRP f => Apply (Behavior f) (Event f) where-    (<@>) = apply--{------------------------------------------------------------------------------    Semantic model-------------------------------------------------------------------------------}--- | The type index 'Model' represents the model implementation.--- You are encouraged to look at the source code!--- (If there is no link to the source code at every type signature,--- then you have to run @cabal@ with @--hyperlink-source@ flag.)-data Model---- Stream of events. Simultaneous events are grouped into lists.-newtype instance Event Model a = E { unE :: [[a]] }--- Stream of values that the behavior takes.-newtype instance Behavior Model a = B { unB :: [a] }---instance Functor (Event Model) where-    fmap f = E . map (map f) . unE--instance Applicative (Behavior Model) where-    pure x    = B $ repeat x-    bf <*> bx = B $ zipWith ($) (unB bf) (unB bx)--instance Functor (Behavior Model) where-    fmap = liftA--instance FRP Model where-    never       = E $ repeat []-    union e1 e2 = E $ zipWith (++) (unE e1) (unE e2)-    -    filterApply bp = E . zipWith (\p xs-> Data.List.filter p xs) (unB bp) . unE-    apply b    = E . zipWith (\f xs -> map f xs) (unB b) . unE--    stepper x  = B . scanl go x . unE-        where go x e = last (x:e)--    accumE acc = E . accumE' acc . unE-        where-        accumE' acc []     = []-        accumE' acc (e:es) = e' : accumE' acc' es-            where-            vals = scanl' (flip ($)) acc e-            e'   = tail $ vals-            acc' = last vals---- strict version of scanl-scanl' :: (a -> b -> a) -> a -> [b] -> [a]-scanl' f x ys = x : case ys of-    []   -> []-    y:ys -> let z = f x y in z `seq` scanl' f z ys---- | Slightly simpler interpreter that does not mention 'Time'.--- Returns lists of event values that occur simultaneously.-interpret :: (Event Model a -> Event Model b) -> [a] -> [[b]]-interpret f = unE . f . E . map (:[])--type Time = Double--- | Interpreter that corresponds to your mental model.-interpretTime :: (Event Model a -> Event Model b) -> [(Time,a)] -> [(Time,b)]-interpretTime f xs =-    concat . zipWith tag times . interpret f . map snd $ xs-    where-    times = map fst xs-    tag t xs = map (\x -> (t,x)) xs--{------------------------------------------------------------------------------    Example: Counter that can be decreased-------------------------------------------------------------------------------}-example :: FRP f => Event f () -> Event f Int-example edec = apply ((\c _ -> c) <$> bcounter) ecandecrease-    where-    bcounter     = accumB 10 $ (subtract 1) <$ ecandecrease-    ecandecrease = whenE ((>0) <$> bcounter) edec--testModel = interpret example $ replicate 15 ()--- > testModel--- [[10],[9],[8],[7],[6],[5],[4],[3],[2],[1],[],[],[],[],[]]--example2 :: FRP f => Event f () -> Event f Int-example2 e = apply (const <$> b) e-    where-    b = accumB 0 ((+1) <$ e)-
− src/Reactive/Banana/PushIO.hs
@@ -1,430 +0,0 @@-{------------------------------------------------------------------------------    Reactive Banana-    -    A push-driven implementation-------------------------------------------------------------------------------}-{-# LANGUAGE TypeFamilies, FlexibleInstances, EmptyDataDecls, GADTs,-     TupleSections, BangPatterns #-}-module Reactive.Banana.PushIO where--import Reactive.Banana.Model hiding (Event, Behavior, interpret)-import qualified Reactive.Banana.Model as Model--import Data.Vault (Vault)-import qualified Data.Vault as Vault---import Control.Applicative-import Control.Arrow (second)-import Control.Monad-import Control.Monad.IO.Class       (liftIO)-import Control.Monad.Trans.Class    (lift)-import Control.Monad.Trans.Identity-import Control.Monad.Trans.State-import Control.Monad.Trans.Writer-import Data.Maybe-import Data.Monoid-import System.IO.Unsafe--import System.IO--- debug s = hPutStrLn stderr s--{------------------------------------------------------------------------------    Observable sharing-    -    References can be used in the  Store  monad.-    This mimicks the case where unique IDs are used-    to look up a value in the environment.-    In this case, the environment is passed around by the  Store  monad.-------------------------------------------------------------------------------}--- store monad-type Store = StateT Vault IO--- references to observe sharing-type Ref a = Vault.Key a--runStore :: Store a -> IO a-runStore m = evalStateT m Vault.empty---- create a new reference.-newRef   :: IO (Ref a)--- read a reference. Only possible in the  Store  monad.-readRef  :: Ref a -> Store (Maybe a)-writeRef :: Ref a -> a -> Store ()--newRef         = Vault.newKey-readRef ref    = Vault.lookup ref <$> get-writeRef ref x = modify $ Vault.insert ref x---- invalid reference that may not store values-invalidRef = error "Store: invalidRef. This is an internal bug."--{------------------------------------------------------------------------------    Cache, generalities-------------------------------------------------------------------------------}--- A cache stores values of different types--- and finalizers to change them.-data Cache = Cache {-              vault :: Vault-            , initializers :: [VaultChanger]-            , finalizers   :: [VaultChanger] }-type VaultChanger = Run ()--emptyCache :: Cache-emptyCache = Cache Vault.empty [] []---- monad to build the network in-type Compile = StateT Cache Store--- monad to run the network in-type Run     = StateT Vault IO--runCompile :: Compile a -> Store (a, Cache)-runCompile m = runStateT m $ Cache { vault = Vault.empty, initializers = [], finalizers = [] }--registerInitializer, registerFinalizer :: VaultChanger -> Compile ()-registerFinalizer m   = modify $-    \cache -> cache { finalizers   = finalizers cache ++ [m] }-registerInitializer m = modify $-    \cache -> cache { initializers = initializers cache ++ [m] }--runRun :: Run a -> Cache -> IO (a, Cache)-runRun m cache = do-        let vault1 = vault cache-        -- run the initializers-        vault2     <- runVaultChangers (initializers cache) vault1-        -- run the action-        (x,vault3) <- runStateT m vault2-        -- run all the finalizers              -        vault4     <- runVaultChangers (finalizers cache) vault3-        -- return new cache-        return (x,cache{ vault = vault4 })-    where-    runVaultChangers = execStateT . sequence_---- helper functions for reading and writing keys into the vault cache-writeVaultKey ref x = do-    vault  <- get-    let vault' = Vault.insert ref x vault-    put $ vault'-readVaultKey ref = Vault.lookup ref <$> get--{------------------------------------------------------------------------------    Cache, particular reference types-------------------------------------------------------------------------------}--- CacheRef--- A simple value to be cached. Lasts one phase. Useful for sharing.-type CacheRef a = Vault.Key a--newCacheRef   :: Compile (CacheRef a)-readCacheRef  :: CacheRef a -> Run (Maybe a)-writeCacheRef :: CacheRef a -> a -> Run ()--newCacheRef      = do-    key <- liftIO $ Vault.newKey-    registerFinalizer $ put . Vault.delete key =<< get-    return key-readCacheRef  = readVaultKey-writeCacheRef = writeVaultKey---- Accumulation values.--- Cache and accumulate a value over several phases.-type AccumRef a = Vault.Key a--newAccumRef    :: a -> Compile (AccumRef a)-readAccumRef   :: AccumRef a -> Run a-updateAccumRef :: AccumRef a -> (a -> a) -> Run a -- strict!--newAccumRef x     = do-    ref    <- liftIO $ Vault.newKey-    vault2 <- Vault.insert ref x . vault <$> get-    modify $ \cache -> cache { vault = vault2 }-    return ref-readAccumRef ref  =-    fromJustError "Reactive.Banana.PushIO.readAccumRef: internal error"-    <$> readVaultKey ref-updateAccumRef ref f = do-    Just x <- readVaultKey ref -    let !y = f x-    writeVaultKey ref y-    return y--fromJustError e = maybe (error e) id---- BehaviorRef.--- Cache and accumulate a value over several phases,--- but updates are only visible at the beginning of a new phase.--- (accumulator, temporary reference for each phase)-type BehaviorRef a = (AccumRef a, CacheRef a)--newBehaviorRefPoll   :: IO a -> Compile (BehaviorRef a)-newBehaviorRefAccum  :: a -> Compile (BehaviorRef a)-readBehaviorRef      :: BehaviorRef a -> Run a-updateBehaviorRef    :: BehaviorRef a -> (a -> a) -> Run () -- strict!--newBehaviorRef m = do-    temp <- newCacheRef-    registerInitializer $ writeCacheRef temp =<< m-    return (undefined, temp)-newBehaviorRefPoll    = newBehaviorRef . liftIO-newBehaviorRefAccum x = do-    acc  <- newAccumRef x-    (_,temp) <- newBehaviorRef $ readAccumRef acc-    return (acc, temp)-readBehaviorRef   (_, temp)   =-    fromJustError "Reactive.Banana.PushIO.readBehaviorRef: internal error"-    <$> readCacheRef temp-updateBehaviorRef (acc, temp) = void . updateAccumRef acc---{------------------------------------------------------------------------------    Abstract syntax tree-------------------------------------------------------------------------------}-data Accum-data Shared-data Linear--type EventStore a = [(Channel, CacheRef a)]--type family   Event t a-type instance Event Accum  a = (Ref (EventStore a), EventD Accum a)-type instance Event Shared a = (Ref (EventStore a), EventD Shared a)-type instance Event Linear a = EventD Linear a--data EventD t :: * -> * where-    Filter    :: (a -> Bool) -> Event t a -> EventD t a-    ApplyE    :: Behavior t (a -> b) -> Event t a -> EventD t b-    AccumE    :: a -> Event t (a -> a) -> EventD t a-    Union     :: Event t a -> Event t a -> EventD t a-    Never     :: EventD t a-    -    -- internal combinators-    Input         :: Channel -> Key a -> EventD t a-    Reactimate    :: Event t (IO ()) -> EventD t ()-    -    ReadCache     :: Channel -> CacheRef a -> EventD t a-    WriteCache    :: CacheRef a -> Event t a -> EventD t a-    -    UpdateAccum    :: AccumRef a    -> Event t (a -> a) -> EventD t a-    UpdateBehavior :: BehaviorRef a -> Event t (a -> a) -> EventD t ()---type BehaviorStore a = BehaviorRef a--type family   Behavior t a-type instance Behavior Accum  a = (Ref (BehaviorStore a), BehaviorD Accum  a)-type instance Behavior Shared a = (Ref (BehaviorStore a), BehaviorD Linear a)-type instance Behavior Linear a = (Ref (BehaviorStore a), BehaviorD Linear a)--data BehaviorD t a where-    Pure         :: a -> BehaviorD t a-    ApplyB       :: Behavior t (a -> b) -> Behavior t a -> BehaviorD t b-    AccumB       :: a -> Event t (a -> a) -> BehaviorD t a-    Poll         :: IO a -> BehaviorD t a-    -    -- internal combinators-    ReadBehavior :: BehaviorRef a -> BehaviorD t a--{------------------------------------------------------------------------------    Storing heterogenous input values-------------------------------------------------------------------------------}-type Channel  = Integer     -- identifies an input-type Key      = Vault.Key -type Universe = Vault.Vault--newUniverseKey :: IO (Key a)-newUniverseKey = Vault.newKey--fromUniverse :: Key a -> Universe -> Maybe a-fromUniverse = Vault.lookup--toUniverse :: Key a -> a -> Universe-toUniverse k a = Vault.insert k a Vault.empty--{------------------------------------------------------------------------------    Compilation-------------------------------------------------------------------------------}--- allocated caches for acummulated and external behaviors,--- turn them into reads from the cache-type CompileReadBehavior = WriterT [Event Shared ()] Compile--compileReadBehavior :: Event Accum () -> Compile (Event Shared ())-compileReadBehavior e1 = do-        (e,es) <- runWriterT (goE e1)-        -- include updates to Behavior as additional events-        let union e1 e2 = (invalidRef, Union e1 e2)-        return $ foldr1 union (e:es)-    where    -    -- boilerplate traversal for events-    goE :: Event Accum a -> CompileReadBehavior (Event Shared a)-    goE (ref, Filter p e )      = (ref,) <$> (Filter p   <$> goE e)-    goE (ref, Union e1 e2)      = (ref,) <$> (Union      <$> goE e1 <*> goE e2)-    goE (ref, ApplyE b e )      = (ref,) <$> (ApplyE     <$> goB b  <*> goE e )-    goE (ref, AccumE x e )      = (ref,) <$> (AccumE x   <$> goE e)-    goE (ref, Reactimate e)     = (ref,) <$> (Reactimate <$> goE e)-    goE (ref, Never)            = (ref,) <$> (pure Never)-    goE (ref, Input c k)        = (ref,) <$> (pure $ Input c k)--    -- almost boilerplate traversal for behaviors-    goB :: Behavior Accum a -> CompileReadBehavior (Behavior Shared a)-    goB (ref, Pure x      ) = (ref,) <$> (Pure   <$> return x)-    goB (ref, ApplyB bf bx) = (ref,) <$> (ApplyB <$> goB bf <*> goB bx)-    goB (ref, Poll io     ) = (ref,) <$> (ReadBehavior <$> makeRef)-        where-        makeRef = do-            m <- lift . lift $ readRef ref-            case m of-                Just r  -> return r-                Nothing -> do-                    r <- lift $ newBehaviorRefPoll io-                    lift . lift $ writeRef ref r-                    return r-    goB (ref, AccumB x e  ) = (ref,) <$> (ReadBehavior <$> makeRef)-        where-        makeRef = do-            m <- lift . lift $ readRef ref-            case m of-                Just r  -> return r-                Nothing -> do-                    -- create new BehaviorRef and share it-                    r <- lift $ newBehaviorRefAccum x-                    lift . lift $ writeRef ref r--                    -- remove  accumB  from the other events-                    e <- goE e-                    tell [(invalidRef, UpdateBehavior r e)]-                    return r---- fan out unions into linear paths-type EventLinear a = (Channel, Event Linear a)--compileUnion :: Event Shared a -> Compile [Event Linear a]-compileUnion e = map snd <$> goE e-    where-    goE :: Event Shared a -> Compile [EventLinear a]-    goE (ref, Filter p e )        = cacheEvents ref (map2 (Filter p) <$> goE e)-    goE (ref, ApplyE b e )        = cacheEvents ref (map2 (ApplyE b) <$> goE e)-    goE (ref, AccumE x e )        = cacheEvents ref (compileAccumE x =<< goE e)-    goE (_  , UpdateBehavior b e) = map2 (UpdateBehavior b) <$> goE e-    goE (_  , Reactimate e)       = map2 (Reactimate)      <$> goE e-    goE (_  , Union e1 e2)        = (++) <$> goE e1 <*> goE e2-    goE (_  , Never      )        = return []-    goE (_  , Input channel key)  = return [(channel, Input channel key)]-    -    compileAccumE :: a -> [EventLinear (a -> a)] -> Compile [EventLinear a]-    compileAccumE x es = do-        ref <- newAccumRef x-        return $ map2 (UpdateAccum ref) es-    -    cacheEvents :: Ref (EventStore a)-                -> Compile [EventLinear a] -> Compile [EventLinear a]-    cacheEvents ref mes = do-        m <- lift $ readRef ref-        case m of-            Just cached -> do-                return $ map (\(c,r) -> (c, ReadCache c r)) cached-            Nothing     -> do-                -- compile input events-                es     <- mes-                -- allocate corresponding cache references and share them-                cached <- forM es $ \(c,_) -> do r <- newCacheRef; return (c,r)-                lift $ writeRef ref cached-                -- return events that also write to the cache-                return $ zipWith (second . (WriteCache . snd)) cached es--map2 = map . second---- compile a behavior--- FIXME: take care of sharing, caching-compileBehaviorEvaluation :: Behavior Linear a -> Run a-compileBehaviorEvaluation = goB-    where-    goB :: Behavior Linear a -> Run a-    goB (ref, Pure x)            = return x-    goB (ref, ApplyB bf bx)      = goB bf <*> goB bx-    goB (ref, ReadBehavior refb) = readBehaviorRef refb----- compile path into an IO action-type Path = (Channel, Universe -> Run (IO ()))--- (input_channel, \input_value ->---      do change event_graph_state; return (reactimates_to_be_run) )--compilePath :: Event Linear () -> Path-compilePath e = goE e (const $ return nop)-    where-    goE :: Event Linear a -> (a -> Run (IO ())) -> Path-    goE (Filter p e)         k = goE e $ \x -> if p x then k x else return nop-    goE (ApplyE b e)         k = goE e $ \x -> goB b >>= \f -> k (f x)-    goE (UpdateAccum    r e) k = goE e $ \f -> updateAccumRef r f >>= k-    goE (UpdateBehavior r e) _ = goE e $ \x -> do updateBehaviorRef r x; return nop-        -- note: no  k  here because writing behaviors is the end of a path-    goE (Reactimate e)       _ = goE e $ \x -> return x-    goE (ReadCache c ref)    k =-            (c, \_ -> readCacheRef ref >>= maybe (return nop) k)-    goE (WriteCache ref e)   k = goE e $ \x -> writeCacheRef ref x >> k x-    goE (Input channel key)  k =-            (channel, maybe (error "wrong channel") k . fromUniverse key)-    -    nop = return () :: IO ()-    -    goB :: Behavior Linear a -> Run a-    goB = compileBehaviorEvaluation---- compilation function-compile :: Event Accum () -> IO ([Path], Cache)-compile e = runStore $ runCompile $-    return . map compilePath =<< compileUnion =<< compileReadBehavior e---- debug :: MonadIO m => String -> m ()--- debug = liftIO . putStrLn--{------------------------------------------------------------------------------    Class instances-------------------------------------------------------------------------------}--- | The type index 'PushIO' represents the efficient push-driven implementation--- described here.--- It implements the same 'FRP' interface as the model implementation--- represented by 'Model'.-data PushIO---- type Behavior = Model.Behavior PushIO-newtype instance Model.Behavior PushIO a = Behavior (Behavior Accum a)---- type Event = Model.Event PushIO-newtype instance Model.Event PushIO a = Event (Event Accum a)--unEvent (Event e) = e---- sharing-behavior :: BehaviorD Accum a -> Model.Behavior PushIO a-behavior b = Behavior pair-    where-    {-# NOINLINE pair #-}-    -- mention argument to prevent let-floating  -    pair = unsafePerformIO (fmap (,b) newRef)--event :: EventD Accum a -> Model.Event PushIO a-event e = Event pair-    where-    {-# NOINLINE pair #-}-    -- mention argument to prevent let-floating-    pair = unsafePerformIO (fmap (,e) newRef)---- boilerplate class instances-instance Functor (Model.Event PushIO) where-    fmap f e = apply (pure f) e-    -instance Applicative (Model.Behavior PushIO) where-    pure x = behavior $ Pure x-    (Behavior bf) <*> (Behavior bx) = behavior $ ApplyB bf bx--instance Functor (Model.Behavior PushIO) where-    fmap = liftA--instance FRP PushIO where-    never = event $ Never-    union (Event e1) (Event e2) = event $ Union e1 e2-    filterE p (Event e) = event $ Filter p e-    apply (Behavior bf) (Event ex) = event $ ApplyE bf ex-    accumB x (Event e) = behavior $ AccumB x e-    accumE x (Event e) = event $ AccumE x e-
src/Reactive/Banana/Tests.hs view
@@ -4,31 +4,34 @@     Test cases and examples ------------------------------------------------------------------------------} {-# LANGUAGE Rank2Types, NoMonomorphismRestriction #-}+ module Reactive.Banana.Tests where  import Control.Monad (when) -import Reactive.Banana.Model as Model-import Reactive.Banana.Implementation as Impl+import Reactive.Banana.Combinators -import Test.QuickCheck+-- import Test.QuickCheck+-- import Test.QuickCheck.Property  {-----------------------------------------------------------------------------     Testing ------------------------------------------------------------------------------}-matchesModel :: (Typeable a, Show b, Eq b) =>-    (forall f. FRP f => Event f a -> Event f b) -> [a] -> IO Bool-matchesModel f = \xs -> do-        let bs1 = Model.interpret f xs-        bs2 <- Impl.interpret f xs-        when (bs1 /= bs2) $ print bs1 >> print bs2-        return $ bs1 == bs2-+matchesModel :: (Show b, Eq b)+             => (forall t. Event t a -> Event t b) -> [[a]] -> IO Bool+matchesModel f xs = do+    bs1 <- interpretModel     f xs+    bs2 <- interpretPushGraph f xs+    when (bs1 /= bs2) $ print bs1 >> print bs2+    return $ bs1 == bs2  testSuite = do         -- trivial unit tests-        test add1-        test filtering+        test id+        -- test never1+        test fmap1+        test filter1+        test filter2         test counter         test double         test sharing@@ -38,18 +41,24 @@         --  * algebraic laws         --  * larger examples         --  * quickcheck-    where-    test :: (Show b, Eq b) => (forall f. FRP f => Event f Int -> Event f b)-         -> IO ()-    test f = print =<< matchesModel f [1..8::Int] +test :: (Show b, Eq b) => (forall t. Event t Int -> Event t b) -> IO ()+test f = print =<< matchesModel f (singletons [1..8::Int])++singletons = map (\x -> [x])+ {-----------------------------------------------------------------------------     Examples ------------------------------------------------------------------------------}-test f = Impl.interpret f [1..8::Int]+testModel, testPush :: (forall t. Event t Int -> Event t b) -> IO [[b]]+testModel f = interpretModel     f $ singletons [1..8::Int]+testPush  f = interpretPushGraph f $ singletons [1..8::Int] -add1      = fmap (+1)-filtering = filterE (>= 3) . fmap (subtract 1)+never1 :: Event t Int -> Event t Int+never1    = const never+fmap1     = fmap (+1)+filter1   = filterE (>= 3)+filter2   = filterE (>= 3) . fmap (subtract 1) counter e = apply (pure const <*> bcounter) e     where bcounter = accumB 0 $ fmap (\_ -> (+1)) e double e  = union e e@@ -59,14 +68,14 @@ type Dummy = Int  -- counter that can be decreased as long as it's >= 0-decrease :: FRP f => Event f Dummy -> Event f Int+decrease :: Event t Dummy -> Event t Int decrease edec = apply (const <$> bcounter) ecandecrease     where     bcounter     = accumB 4 $ (subtract 1) <$ ecandecrease     ecandecrease = whenE ((>0) <$> bcounter) edec --- test accumE vs accumE-accumBvsE :: FRP f => Event f Dummy -> Event f Int+-- test accumE vs accumB+accumBvsE :: Event t Dummy -> Event t Int accumBvsE input = e1 `union` e2     where     e  = input `union` input