packages feed

reactive 0.5.0.1 → 0.8.3

raw patch · 37 files changed

+3455/−949 lines, 37 filesdep +QuickCheckdep +checkersdep +old-timedep ~TypeComposedep ~base

Dependencies added: QuickCheck, checkers, old-time, random, unamb, vector-space

Dependency ranges changed: TypeCompose, base

Files

+ Makefile view
@@ -0,0 +1,9 @@+# For special configuration, especially for docs.  Otherwise see README.++server = code.haskell.org+server-dir = /srv/code+server-url-dir =++# extra-configure-args += --enable-library-profiling --enable-executable-profiling++include ../my-cabal-make.inc
+ README view
@@ -0,0 +1,34 @@+_Reactive_ [1] is a simple foundation for programming reactive systems+functionally.  Like Fran/FRP, it has a notions of (reactive) behaviors and+events.  Like DataDriven [2], Reactive has a data-driven implementation.++The inspiration for Reactive was Mike Sperber's Lula [3] implementation of+FRP.  Mike used blocking threads, which I had never considered for FRP.+While playing with the idea, I realized that I could give a very elegant+and efficient solution to caching, which DataDriven doesn't do.  (For an+application "f <*> a" of a varying function to a varying argument, caching+remembers the latest function to apply to a new argument and the last+argument to which to apply a new function.)++The theory and implementation of Reactive are described in the paper "Simply+efficient functional reactivity" [4].++Please share any comments & suggestions on the discussion (talk) page [1].++Note that cabal[5], version 1.4.0.1 or greater is required for installation.++You can configure, build, and install all in the usual way with Cabal+commands.++  runhaskell Setup.lhs configure+  runhaskell Setup.lhs build+  runhaskell Setup.lhs install+++References:++[1] http://haskell.org/haskellwiki/Reactive+[2] http://haskell.org/haskellwiki/DataDriven+[3] http://www-pu.informatik.uni-tuebingen.de/lula/deutsch/publications.html+[4] http://conal.net/papers/simply-reactive+[5] http://www.haskell.org/cabal/download.html
+ TODO view
@@ -0,0 +1,19 @@+For next release:+++ Move all of the legacy-adapter stuff to a new LegacyAdapter directory.+  Use David's boingeeFieldTrip adapter as a basis.  Separate out all of+  the GLUT stuff.+++ Much-simplified interfaces.  FRP.Reactive has just behaviors and events.+  FRP.Reactive.LegacyAdapter for building legacy adapters.+++ Something for accessing representations but not exposed in FRP.Reactive.++----++Beyond:+++ Revisit choice of time as double.  Consider nanoseconds or microseconds+  instead, for regularity of time-stepping.+++ Semantics-based fusion rules.  Inquire about fusion vs sharing.
+ changes.tw view
@@ -0,0 +1,29 @@+== Version 0 ==++=== Version 0.8 ===++=== Version 0.8.1 ===++* Adding QuickCheck tests.++''Fill in missing versions''+++=== Version 0.3 ===++* Commented out LANGUAGE pragmas and added OPTIONS_GHC -fglasgow-exts for ghc-6.6 compatibility.++=== Version 0.2 ===++* Fixed <hask>switcher</hask>.  Didn't terminate.  Thanks to Ivan Tomac for the bug report.++=== Version 0.1 ===++* Added <hask>Never</hask> constructor for Future.  Allows optimizations, including a huge improvement for <hask>(>>=)</hask> on <hask>Event</hask> (which had been piling up <hask>never</hask>s).+* removed <code>-threaded</code> comment+* added <hask>traceR</hask> (reactive value tracing)+* use idler in <code>src/Examples.hs</code> (for single-threaded use of wxHaskell)++=== Version 0.0 ===++* New project.
reactive.cabal view
@@ -1,42 +1,67 @@ Name:                reactive-Version:             0.5.0.1-Synopsis: 	     Simple foundation for functional reactive programming+Version:             0.8.3+Synopsis:            Simple foundation for functional reactive programming Category:            reactivity, FRP Description:   /Reactive/ is a simple foundation for programming reactive systems   functionally.  Like Fran\/FRP, it has a notions of (reactive) behaviors and-  events.  Like DataDriven, Reactive has a data-driven implementation.-  The main difference between Reactive and DataDriven is that Reactive-  builds on functional \"futures\" (using threading), while DataDriven-  builds on continuation-based computations.+  events.  Unlike most previous FRP implementations, Reactive has a hybrid+  demand/data-driven implementation, as described in the paper \"Simply+  efficient functional reactivity\", <http://conal.net/papers/simply-reactive/>.   .   Please see the project wiki page: <http://haskell.org/haskellwiki/reactive>   .   The module documentation pages have links to colorized source code and   to wiki pages where you can read and contribute user comments.  Enjoy!   .-  &#169; 2007 by Conal Elliott; BSD3 license.+  &#169; 2007-2008 by Conal Elliott; BSD3 license. Author:              Conal Elliott  Maintainer:          conal@conal.net Homepage:            http://haskell.org/haskellwiki/reactive-Package-Url:	     http://darcs.haskell.org/packages/reactive+Package-Url:         http://code.haskell.org/reactive Copyright:           (c) 2007-2008 by Conal Elliott+Cabal-Version:       >= 1.2 License:             BSD3 Stability:           provisional-build-type:	     Simple-Hs-Source-Dirs:      src-Extensions:          -Build-Depends:       base >= 3.0.3.2 && < 5, TypeCompose>=0.6.7-Exposed-Modules:     -		     Data.SFuture-		     Data.Future-		     Data.Fun-		     Data.Reactive+Build-Type:          Simple Extra-Source-Files:-ghc-options:         -Wall+Library+    Build-Depends:       base, old-time, random, QuickCheck < 2.0,+                         TypeCompose>=0.3, vector-space, unamb, checkers+    -- This library uses the ImpredicativeTypes flag, and it depends+    -- on vector-space, which needs ghc >= 6.9+    if impl(ghc < 6.9) {+      buildable: False+    }+    Hs-Source-Dirs:      src+    Exposed-Modules:     +        FRP.Reactive --- Experimental modules:--- 		     Data.SEvent--- 		     Data.MEvent--- 		     Data.EventExtras--- 		     Data.SReactive+        FRP.Reactive.Future+        FRP.Reactive.PrimReactive+        FRP.Reactive.Reactive+        FRP.Reactive.Behavior+        FRP.Reactive.Fun+        FRP.Reactive.Improving+        FRP.Reactive.Num+        FRP.Reactive.VectorSpace++        -- FRP.Reactive.LegacyAdapters++        FRP.Reactive.Internal.Misc+        FRP.Reactive.Internal.Fun+        FRP.Reactive.Internal.Future+        FRP.Reactive.Internal.Reactive+        FRP.Reactive.Internal.Behavior+        FRP.Reactive.Internal.Clock+        FRP.Reactive.Internal.Timing++        Data.AddBounds+        Data.Min+        Data.Max+        Data.PairMonad+        -- Probably eliminate the next few+        FRP.Reactive.Internal.IVar+        FRP.Reactive.Internal.Serial+        FRP.Reactive.Internal.TVal+    ghc-options:         -Wall
+ src/Data/AddBounds.hs view
@@ -0,0 +1,76 @@+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  Data.AddBounds+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Add bounds to an ordered type+----------------------------------------------------------------------++module Data.AddBounds (AddBounds(..)) where++import Control.Applicative (pure,(<$>))++-- Testing+import Test.QuickCheck+import Test.QuickCheck.Checkers+++-- | Wrap a type into one having new least and greatest elements,+-- preserving the existing ordering.+data AddBounds a = MinBound | NoBound a | MaxBound+	deriving (Eq {-, Ord-}, Read, Show)++instance Bounded (AddBounds a) where+	minBound = MinBound+	maxBound = MaxBound+++-- Normally, I'd derive 'Ord' as well, but there's a sticky point.  The+-- derived instance uses the default definition of 'min', which is uses+-- '(<=)' and thus cannot exploit any partial information.  So, define our+-- own 'min' in terms of 'min' on @a@.+-- Examples:+--   (NoBound undefined) `min` (NoBound undefined) can return (NoBound _|_)+--   using this definition, but will not produce any output using the+--   default min.+--   +--   (NoBound a) `min` (NoBound b) can return partial information from+--   a `min` b while the default implementation cannot.++instance Ord a => Ord (AddBounds a) where+  MinBound  <= _         = True+  NoBound _ <= MinBound  = False+  NoBound a <= NoBound b = a <= b+  NoBound _ <= MaxBound  = True+  MaxBound  <= MaxBound  = True+  MaxBound  <= _         = False        -- given previous +  +  MinBound  `min` _         = MinBound+  _         `min` MinBound  = MinBound+  NoBound a `min` NoBound b = NoBound (a `min` b)+  u         `min` MaxBound  = u+  MaxBound  `min` v         = v+  +  MinBound  `max` v         = v+  u         `max` MinBound  = u+  NoBound a `max` NoBound b = NoBound (a `max` b)+  _         `max` MaxBound  = MaxBound+  MaxBound  `max` _         = MaxBound+++instance Arbitrary a => Arbitrary (AddBounds a) where+  arbitrary = frequency [ (1 ,pure MinBound)+                        , (10, NoBound <$> arbitrary)+                        , (1 ,pure MaxBound) ]+  coarbitrary MinBound    = variant 0+  coarbitrary (NoBound a) = variant 1 . coarbitrary a+  coarbitrary MaxBound    = variant 2++instance (EqProp a, Eq a) => EqProp (AddBounds a) where+  NoBound a =-= NoBound b = a =-= b+  u =-= v = u `eq` v
− src/Data/Fun.hs
@@ -1,62 +0,0 @@-------------------------------------------------------------------------- |--- Module      :  Data.Fun--- Copyright   :  (c) Conal Elliott 2007--- License     :  BSD3--- --- Maintainer  :  conal@conal.net--- Stability   :  experimental--- --- Functions, with constant functions optimized.  With instances of--- 'Functor', 'Applicative', 'Monad', and 'Arrow'-------------------------------------------------------------------------module Data.Fun (Fun(..), apply) where--import Data.Monoid (Monoid(..))-import Control.Applicative (Applicative(..))-import qualified Control.Category (Category, (.), id)-import Control.Arrow (Arrow, arr, first, second, (***), (>>>))---- | Constant-optimized functions-data Fun t a = K a                      -- ^ constant function-             | Fun (t -> a)             -- ^ non-constant function---- | 'Fun' as a function-apply :: Fun t a -> (t -> a)-apply (K   a) = const a-apply (Fun f) = f--instance Monoid a => Monoid (Fun t a) where-  mempty = K mempty-  K a  `mappend` K a' = K (a `mappend` a')-  funa `mappend` funb = Fun (apply funa `mappend` apply funb)--instance Functor (Fun t) where-  fmap f (K   a) = K   (f a)-  fmap f (Fun g) = Fun (f.g)-  -- Or use-  --  fmap f = (pure f <*>)--instance Applicative (Fun t) where-  pure        = K-  K f <*> K x = K   (f x)-  cf  <*> cx  = Fun (apply cf <*> apply cx)--instance Monad (Fun t) where-  return = pure-  K   a >>= h = h a-  Fun f >>= h = Fun (f >>= apply . h)--instance Control.Category.Category Fun where-  id          = arr id-  K b   . _   = K   b-  Fun g . K a = K   (g a)-  Fun f . Fun g = Fun (f . g)--instance Arrow Fun where-  arr             = Fun-  first           = Fun . first  . apply-  second          = Fun . second . apply-  K a'  *** K b'  = K (a',b')-  f     *** g     = first f >>> second g
− src/Data/Future.hs
@@ -1,171 +0,0 @@-{-# LANGUAGE RecursiveDo #-}--- For ghc-6.6 compatibility--- {-# OPTIONS_GHC -fglasgow-exts #-}--------------------------------------------------------------------------- |--- Module      :  Data.Future--- Copyright   :  (c) Conal Elliott 2007--- License     :  BSD3--- --- Maintainer  :  conal@conal.net--- Stability   :  experimental--- --- A /future value/ is a value that will become knowable only later.  This--- module gives a way to manipulate them functionally.  For instance,--- @a+b@ becomes knowable when the later of @a@ and @b@ becomes knowable.--- See <http://en.wikipedia.org/wiki/Futures_and_promises>.--- --- Primitive futures can be things like /the value of the next key you--- press/, or /the value of LambdaPix stock at noon next Monday/.--- --- Composition is via standard type classes: 'Functor', 'Applicative',--- 'Monad', and 'Monoid'.  Some comments on the 'Future' instances of--- these classes:--- --- * Monoid: 'mempty' is a future that never becomes knowable.---   @a `mappend` b@ is whichever of @a@ and @b@ is knowable first.--- --- * 'Functor': apply a function to a future.  The result is knowable when---   the given future is knowable.--- --- * 'Applicative': 'pure' gives value knowable since the beginning of---   time.  '(\<*\>)' applies a future function to a future argument.---   Result available when /both/ are available, i.e., it becomes knowable---   when the later of the two futures becomes knowable.--- --- * 'Monad': 'return' is the same as 'pure' (as always).  @(>>=)@ cascades---   futures.  'join' resolves a future future into a future.--- --- The current implementation is nondeterministic in 'mappend' for futures--- that become knowable at the same time or nearly the same time.  I--- want to make a deterministic implementation.--- --- See "Data.SFuture" for a simple denotational semantics of futures.  The--- current implementation /does not/ quite implement this target semantics--- for 'mappend' when futures are available simultaneously or nearly--- simultaneously.  I'm still noodling how to implement that semantics.-------------------------------------------------------------------------module Data.Future-  ( Future(..), force, newFuture-  , future-  , runFuture-  ) where--import Control.Concurrent-import Data.Monoid (Monoid(..))-import Control.Applicative-import Control.Monad (join,forever)-import System.IO.Unsafe--- import Foreign (unsafePerformIO)---- TypeCompose-import Control.Instances () -- IO monoid---- About determinacy: for @f1 `mappend` f2@, we might get @f2@ instead of--- @f1@ even if they're available simultaneously.  It's even possible to--- get the later of the two if they're nearly simultaneous.--- --- What will it take to get deterministic semantics for @f1 `mappend` f2@?--- Idea: make an "event occurrence" type, which is a future with a time--- and a value.  (The time is useful for snapshotting continuous--- behaviors.)  When one occurrence happens with a time @t@, query whether--- the other one occurs by the same time.  What does it take to support--- this query operation?--- --- Another idea: speculative execution.  When one event occurs, continue--- to compute consequences.  If it turns out that an earlier occurrence--- arrives later, do some kind of 'retry'.---- The implementation is very like IVars.  Each future contains an MVar--- reader.  'force' blocks until the MVar is written.---- | Value available in the future.-data Future a =-    -- | Future that may arrive.  The 'IO' blocks until available.  No side-effect.-    Future (IO a)-    -- | Future that never arrives.-  | Never---- Why not simply use @a@ (plain-old lazy value) in place of @IO a@ in--- 'Future'?  Several of the definitions below get simpler, and many--- examples work.  See NewFuture.hs.  But sometimes that implementation--- mysteriously crashes or just doesn't update.  Odd.---- | Access a future value.  Blocks until available.-force :: Future a -> IO a-force (Future io) = io-force Never       = hang---- | Block forever-hang :: IO a-hang = do -- putStrLn "warning: blocking forever."-          -- Any never-terminating computation goes here-          -- This one can yield an exception "thread blocked indefinitely"-          -- newEmptyMVar >>= takeMVar-          -- sjanssen suggests this alternative:-          forever $ threadDelay maxBound-          -- forever's return type is (), though it could be fully-          -- polymorphic.  Until it's fixed, I need the following line.-          return undefined---- | Make a 'Future' and a way to fill it.  The filler should be invoked--- only once.-newFuture :: IO (Future a, a -> IO ())-newFuture = do v <- newEmptyMVar-               return (Future (readMVar v), putMVar v)---- | Make a 'Future', given a way to compute a value.-future :: IO a -> Future a-future mka = unsafePerformIO $-             do (fut,sink) <- newFuture-                forkIO $ mka >>= sink-                return fut-{-# NOINLINE future #-}--instance Functor Future where-  fmap f (Future get) = future (fmap f get)-  fmap _ Never        = Never--instance Applicative Future where-  pure a                      = Future (pure a)-  Future getf <*> Future getx = future (getf <*> getx)-  _           <*> _           = Never---- Note Applicative's pure uses 'Future' as an optimization over--- 'future'.  No thread or MVar.--instance Monad Future where-  return            = pure-  Future geta >>= h = future (geta >>= force . h)-  Never       >>= _ = Never--instance Monoid (Future a) where-  mempty  = Never-  mappend = race---- | Race to extract a value.-race :: Future a -> Future a -> Future a-Never `race` b     = b-a     `race` Never = a-a     `race` b     = unsafePerformIO $-                     do (c,sink) <- newFuture-                        lock     <- newEmptyMVar  -- to avoid double-kill-                        let run fut tid = forkIO $ do x <- force fut-                                                      putMVar lock ()-                                                      killThread tid-                                                      sink x-                        mdo ta <- run a tb-                            tb <- run b ta-                            return ()-                        return c-{-# NOINLINE race #-}---- TODO: make race deterministic, using explicit times.  Figure out how--- one thread can inquire whether the other whether it is available by a--- given time, and if so, what time.---- | Run an 'IO'-action-valued 'Future'.-runFuture :: Future (IO ()) -> IO ()-runFuture = join . force
+ src/Data/Max.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  Data.Max+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Max monoid+----------------------------------------------------------------------++module Data.Max (Max(..)) where+++import Data.Monoid (Monoid(..))++import Test.QuickCheck+import Test.QuickCheck.Checkers+++-- | Ordered monoid under 'max'.+newtype Max a = Max { getMax :: a }+	deriving (Eq, Ord, Bounded, Read, Show, EqProp, Arbitrary)++instance (Ord a, Bounded a) => Monoid (Max a) where+	mempty = Max minBound+	Max a `mappend` Max b = Max (a `max` b)
+ src/Data/Min.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  Data.Min+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Min monoid+----------------------------------------------------------------------++module Data.Min (Min(..)) where++import Data.Monoid (Monoid(..))++import Test.QuickCheck+import Test.QuickCheck.Checkers++-- | Ordered monoid under 'min'.+newtype Min a = Min { getMin :: a }+  deriving (Eq, Ord, Read, Show, Bounded, EqProp, Arbitrary)++instance (Ord a, Bounded a) => Monoid (Min a) where+  mempty = Min maxBound+  Min a `mappend` Min b = Min (a `min` b)
+ src/Data/PairMonad.hs view
@@ -0,0 +1,40 @@+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+----------------------------------------------------------------------+-- |+-- Module      :  Data.PairMonad+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Writer monad as a pair.  Until it's in Control.Monad.Instances.+-- +-- Use @import Data.PairMonad ()@+----------------------------------------------------------------------++module Data.PairMonad () where++import Data.Monoid+import Control.Applicative+++-- Orphan instance:++-- Equivalent to the Monad Writer instance.+instance Monoid o => Monad ((,) o) where+  return      = pure+  (o,a) >>= f = (o `mappend` o', a') where (o',a') = f a++-- Alternatively,+--   m >>= f = join (fmap f m)+--    where+--      join ((o, (o',a))) = (o `mappend` o', a)+-- Or even,+--   (o,a) >>= f = (o,id) <*> f a+-- +-- I prefer the join version, because it's the standard (>>=)-via-join,+-- plus a very simple definition for join.  Too bad join isn't a method of+-- Monad, with (>>=) and join defined in terms of each other.  Why isn't+-- it?  Probably because Monad isn't derived from Functor.  Was that an+-- oversight?
− src/Data/Reactive.hs
@@ -1,498 +0,0 @@--- {-# LANGUAGE TypeOperators, ScopedTypeVariables, PatternSignatures---            , FlexibleInstances---  #-}---- For ghc-6.6 compatibility-{-# OPTIONS_GHC -fglasgow-exts #-}--------------------------------------------------------------------------- |--- Module      :  Data.Reactive--- Copyright   :  (c) Conal Elliott 2007--- License     :  BSD3--- --- Maintainer  :  conal@conal.net--- Stability   :  experimental--- --- Functional /events/ and /reactive values/.  An 'Event' is stream of--- future values in time order.  A 'Reactive' value is a discretly--- time-varying value.  These two types are closely linked: a reactive--- value is defined by an initial value and an event that yields future--- values; while an event is simply a future reactive value.--- --- Many of the operations on events and reactive values are packaged as--- instances of the standard type classes 'Monoid', 'Functor',--- 'Applicative', and 'Monad'.--- --- Although the basic 'Reactive' type describes /discretely/-changing--- values, /continuously/-changing values are modeled simply as reactive--- functions.  For convenience, this module defines 'ReactiveB' as a type--- composition of 'Reactive' and a constant-optimized representation of--- functions of time.--- --- The exact packaging of discrete vs continuous will probably change with--- more experience.-------------------------------------------------------------------------module Data.Reactive-  ( -- * Events and reactive values-    Event(..), Reactive(..), Source, inEvent, inEvent2-  , stepper, switcher, mkEvent, mkEventTrace, mkEventShow-  , runE, forkE, subscribe, forkR-    -- * Event extras-  , accumE, scanlE, monoidE-  , withPrevE, countE, countE_, diffE-  , snapshot, snapshot_, whenE, once, traceE, eventX-    -- * Reactive extras-  , mkReactive, accumR, scanlR, monoidR, maybeR, flipFlop, countR, traceR-    -- * Reactive behaviors-  , Time, ReactiveB-    -- * To be moved elsewhere-  , replace, forget-  , Action, Sink-  , joinMaybes, filterMP-  ) where--import Data.Monoid-import Control.Arrow (first,second)-import Control.Applicative-import Control.Monad-import Debug.Trace (trace)-import Data.IORef-import Control.Concurrent -- (forkIO,ThreadId)--import Data.Maybe---- TypeCompose-import Control.Compose (Unop,(:.)(..), inO2, Monoid_f(..))-import Data.Pair--import Data.Future-import Data.Fun---{---------------------------------------------------------------------    Events and reactive values---------------------------------------------------------------------}---- | Event, i.e., a stream of future values.  Instances:--- --- * 'Monoid': 'mempty' is the event that never occurs, and @e `mappend`--- e'@ is the event that combines occurrences from @e@ and @e'@.  (Fran's--- @neverE@ and @(.|.)@.)--- --- * 'Functor': @fmap f e@ is the event that occurs whenever @e@ occurs,--- and whose occurrence values come from applying @f@ to the values from--- @e@.  (Fran's @(==>)@.)--- --- * 'Applicative': @pure a@ is an event with a single occurrence,--- available from the beginning of time.  @ef \<*\> ex@ is an event whose--- occurrences are made from the /product/ of the occurrences of @ef@ and--- @ex@.  For every occurrence @f@ at time @tf@ of @ef@ and occurrence @x@--- at time @tx@ of @ex@, @ef \<*\> ex@ has an occurrence @f x@ at time @max--- tf tx@.--- --- * 'Monad': @return a@ is the same as @pure a@ (as always).  In @e >>=--- f@, each occurrence of @e@ leads, through @f@, to a new event.--- Similarly for @join ee@, which is somehow simpler for me to think--- about.  The occurrences of @e >>= f@ (or @join ee@) correspond to the--- union of the occurrences of all such events.  For example, suppose--- we're playing Asteroids and tracking collisions.  Each collision can--- break an asteroid into more of them, each of which has to be tracked--- for more collisions.  Another example: A chat room has an /enter/--- event, whose occurrences contain new events like /speak/.  An--- especially useful monad-based function is 'joinMaybes', which filters a--- Maybe-valued event.--- -newtype Event a = Event { eFuture :: Future (Reactive a) }---- | Reactive value: a discretely changing value.  Reactive values can be--- understood in terms of (a) a simple denotational semantics of reactive--- values as functions of time, and (b) the corresponding instances for--- functions.  The semantics is given by the function @(%$) :: Reactive a--- -> (Time -> a)@.  A reactive value also has a current value and an--- event (stream of future values).--- --- Instances for 'Reactive'--- --- * 'Monoid': a typical lifted monoid.  If @o@ is a monoid, then--- @Reactive o@ is a monoid, with @mempty = pure mempty@, and @mappend =--- liftA2 mappend@.  In other words, @mempty %$ t == mempty@, and @(r--- `mappend` s) %$ t == (r %$ t) `mappend` (s %$ t).@--- --- * 'Functor': @fmap f r %$ t == f (r %$ t)@.--- --- * 'Applicative': @pure a %$ t == a@, and @(s \<*\> r) %$ t ==--- (s %$ t) (r %$ t)@.--- --- * 'Monad': @return a %$ t == a@, and @join rr %$ t == (rr %$ t)--- %$ t@.  As always, @(r >>= f) == join (fmap f r)@.--- -data Reactive a =-  Stepper {-    rInit  :: a                         -- ^ initial value-  , rEvent :: Event a                   -- ^ waiting for event-  }---- data Reactive a = a `Stepper` Event a---- | Reactive value from an initial value and a new-value event.-stepper :: a -> Event a -> Reactive a-stepper = Stepper---- | Compatibility synonym (for ease of transition from DataDriven)-type Source = Reactive---- | Apply a unary function inside an 'Event' representation.-inEvent :: (Future (Reactive a) -> Future (Reactive b)) -> (Event a -> Event b)-inEvent f = Event . f . eFuture---- | Apply a unary function inside an 'Event' representation.-inEvent2 :: (Future (Reactive a) -> Future (Reactive b) -> Future (Reactive c))-         -> (Event a -> Event b -> Event c)-inEvent2 f = inEvent . f . eFuture---- Why the newtype for Event?  Because the 'Monoid' instance of 'Future'--- does not do what I want for 'Event'.  It will pick just the--- earlier-occurring event, while I want an interleaving of occurrences--- from each.--instance Monoid (Event a) where-  mempty  = Event mempty-  mappend = inEvent2 merge---- Standard instance for Applicative of Monoid-instance Monoid a => Monoid (Reactive a) where-  mempty  = pure mempty-  mappend = liftA2 mappend---- | Merge two 'Future' streams into one.-merge :: Future (Reactive a) -> Future (Reactive a) -> Future (Reactive a)-Never `merge` fut   = fut-fut   `merge` Never = fut-u     `merge` v     =-  (onFut (`merge` v) <$> u) `mappend` (onFut (u `merge`) <$> v)- where-   onFut f (a `Stepper` Event t') = a `stepper` Event (f t')--instance Functor Event where-  fmap f = inEvent $ (fmap.fmap) f---- I could probably define an Applicative instance like []'s for Event,--- i.e., apply all functions to all arguments.  I don't think I want that--- semantics.--instance Functor Reactive where-  fmap f (a `Stepper` e) = f a `stepper` fmap f e--instance Applicative Event where { pure = return; (<*>) = ap }--instance Applicative Reactive where-  pure a = a `stepper` mempty-  rf@(f `Stepper` Event futf) <*> rx@(x `Stepper` Event futx) =-    f x `stepper` Event fut-   where-     fut = fmap (\ rf' -> rf' <*> rx ) futf `mappend`-           fmap (\ rx' -> rf  <*> rx') futx---- More succinctly,--- ---   rf@(f `Stepper` Event futf) <*> rx@(x `Stepper` Event futx) =---    f x `stepper` Event (((<*> rx) <$> futf) `mappend` ((rf <*>) <$> futx))----- A wonderful thing about the <*> definition for Reactive is that it--- automatically caches the previous value of the function or argument--- when the argument or function changes.---- TODO: The definitions of merge and <*> have some similarities.  Can I--- factor out a common pattern?--instance Monad Event where-  return a = Event (pure (pure a))-  e >>= f  = joinE (fmap f e)--joinE :: forall a. Event (Event a) -> Event a-joinE = inEvent q- where-   q :: Future (Reactive (Event a)) -> Future (Reactive a)-   q = (>>= eFuture . h)-   h :: Reactive (Event a) -> Event a-   h (ea `Stepper` eea) = ea `mappend` joinE eea--instance MonadPlus Event where { mzero = mempty; mplus = mappend }--instance Monad Reactive where-  return  = pure-  r >>= h = joinR (fmap h r)---- | Switch between reactive values.-switcher :: Reactive a -> Event (Reactive a) -> Reactive a-r `switcher` e = joinR (r `stepper` e)---- Reactive 'join'-joinR :: Reactive (Reactive a) -> Reactive a-joinR ((a `Stepper` Event fut) `Stepper` e'@(Event fut')) =-  a `stepper` Event fut''- where-   -- If fut  arrives first, switch and continue waiting for e'.-   -- If fut' arrives first, abandon fut and keep switching with new-   -- reactive values from fut'.-   fut'' = fmap (`switcher` e') fut `mappend` fmap join fut'---- | Make an event and a sink for feeding the event.  Each value sent to--- the sink becomes an occurrence of the event.-mkEvent :: IO (Event a, Sink a)-mkEvent = do (fut,snk) <- newFuture-             -- remember how to save the next occurrence.-             r <- newIORef snk-             return (Event fut, writeTo r)- where-   -- Fill in an occurrence while preparing for the next one-   writeTo r a = do snk  <- readIORef r-                    (fut',snk') <- newFuture-                    writeIORef r snk'-                    snk (a `stepper` Event fut')---- | Tracing variant of 'mkEvent'-mkEventTrace :: (a -> String) -> IO (Event a, Sink a)-mkEventTrace shw = second tr <$> mkEvent- where-   tr snk = (putStrLn.shw) `mappend` snk---- | Show specialization of 'mkEventTrace'-mkEventShow :: Show a => String -> IO (Event a, Sink a)-mkEventShow str = mkEventTrace ((str ++).(' ':).show)---- | Run an event in a new thread.-forkE :: Event (IO b) -> IO ThreadId-forkE = forkIO . runE---- | Subscribe a listener to an event.  Wrapper around 'forkE' and 'fmap'.-subscribe :: Event a -> Sink a -> IO ThreadId-subscribe e snk = forkE (snk <$> e)---- | Run an event in the current thread.-runE :: Event (IO b) -> IO a-runE (Event fut) = do act `Stepper` e' <- force fut-                      act-                      runE e'-                    --- | Run a reactive value in a new thread.  The initial action happens in--- the current thread.-forkR :: Reactive (IO b) -> IO ThreadId-forkR (act `Stepper` e) = act >> forkE e---{---------------------------------------------------------------------    Event extras---------------------------------------------------------------------}---- | Accumulating event, starting from an initial value and a--- update-function event.  See also 'accumR'.-accumE :: a -> Event (a -> a) -> Event a-accumE a = inEvent $ fmap $ \ (f `Stepper` e') -> f a `accumR` e'---- | Like 'scanl' for events.  See also 'scanlR'.-scanlE :: (a -> b -> a) -> a -> Event b -> Event a-scanlE f a e = a `accumE` (flip f <$> e)---- | Accumulate values from a monoid-valued event.  Specialization of--- 'scanlE', using 'mappend' and 'mempty'.  See also 'monoidR'.-monoidE :: Monoid o => Event o -> Event o-monoidE = scanlE mappend mempty---- | Pair each event value with the previous one, given an initial value.-withPrevE :: Event a -> Event (a,a)-withPrevE e = (joinMaybes . fmap combineMaybes) $-              (Nothing,Nothing) `accumE` fmap (shift.Just) e- where-   -- Shift newer value into (old,new) pair if present.-   shift :: u -> Unop (u,u)-   shift new (_,old) = (old,new)-   combineMaybes :: (Maybe u, Maybe v) -> Maybe (u,v)-   combineMaybes = uncurry (liftA2 (,))---- | Count occurrences of an event, remembering the occurrence values.--- See also 'countE_'.-countE :: Num n => Event b -> Event (b,n)-countE = scanlE h (b0,0)- where-   b0        = error "withCountE: no initial value"-   h (_,n) b = (b,n+1)---- | Count occurrences of an event, forgetting the occurrence values.  See--- also 'countE'.  See also 'countR'.-countE_ :: Num n => Event b -> Event n-countE_ e = snd <$> countE e---- | Difference of successive event occurrences.-diffE :: Num n => Event n -> Event n-diffE e = uncurry (-) <$> withPrevE e---- | Snapshot a reactive value whenever an event occurs.-snapshot :: Event a -> Reactive b -> Event (a,b)-e `snapshot` r = joinMaybes $ e `snap` r---- This variant of 'snapshot' yields 'Just's when @e@ happens and--- 'Nothing's when @r@ changes.-snap :: forall a b. Event a -> Reactive b -> Event (Maybe (a,b))-e@(Event ve) `snap` r@(b `Stepper` Event vr) =-  Event ((g <$> ve) `mappend` (h <$> vr))- where-   -- When e occurs, produce a pair, and start snapshotting the old-   -- reactive value with the new event.-   g :: Reactive a -> Reactive (Maybe (a,b))-   g (a `Stepper` e') = Just (a,b) `stepper` (e' `snap` r)-   -- When r changes, produce no pair, and start snapshotting the new-   -- reactive value with the old event.-   h :: Reactive b -> Reactive (Maybe (a,b))-   h r' = Nothing `stepper` (e `snap` r')---- Introducing Nothing above allows the mappend to commit to the RHS.---- | Like 'snapshot' but discarding event data (often @a@ is @()@).-snapshot_ :: Event a -> Reactive b -> Event b-e `snapshot_` src = snd <$> (e `snapshot` src)---- | Filter an event according to whether a boolean source is true.-whenE :: Event a -> Reactive Bool -> Event a-whenE e = joinMaybes . fmap h . snapshot e- where-   h (a,True)  = Just a-   h (_,False) = Nothing---- | Just the first occurrence of an event.-once :: Event a -> Event a-once = inEvent $ fmap $ pure . rInit---- | Tracing of events.-traceE :: (a -> String) -> Unop (Event a)-traceE shw = fmap (\ a -> trace (shw a) a)----- | Make an extensible event.  The returned sink is a way to add new--- events to mix.  You can often use '(>>=)' or 'join' instead.  Warning:--- this function might be removed at some point.-eventX :: IO (Event a, Sink (Event a))-eventX = first join <$> mkEvent---{---------------------------------------------------------------------    Reactive extras---------------------------------------------------------------------}--mkReactive :: a -> IO (Reactive a, Sink a)-mkReactive a0 = first (a0 `stepper`) <$> mkEvent---- | Reactive value from an initial value and an updater event.  See also--- 'accumE'.-accumR :: a -> Event (a -> a) -> Reactive a-a `accumR` e = a `stepper` (a `accumE` e)---- | Like 'scanl' for reactive values.  See also 'scanlE'.-scanlR :: (a -> b -> a) -> a -> Event b -> Reactive a-scanlR f a e = a `stepper` scanlE f a e---- | Accumulate values from a monoid-valued event.  Specialization of--- 'scanlE', using 'mappend' and 'mempty'.  See also 'monoidE'.-monoidR :: Monoid a => Event a -> Reactive a-monoidR = scanlR mappend mempty---- | Start out blank ('Nothing'), latching onto each new @a@, and blanking--- on each @b@.  If you just want to latch and not blank, then use--- 'mempty' for @lose@.-maybeR :: Event a -> Event b -> Reactive (Maybe a)-maybeR get lose =-  Nothing `stepper` (fmap Just get `mappend` replace Nothing lose)---- | Flip-flopping source.  Turns true when @ea@ occurs and false when--- @eb@ occurs.-flipFlop :: Event a -> Event b -> Reactive Bool-flipFlop ea eb =-  False `stepper` (replace True ea `mappend` replace False eb)---- TODO: generalize 'maybeR' & 'flipFlop'.  Perhaps using 'Monoid'.--- Note that Nothing and (Any False) are mempty.---- | Count occurrences of an event.  See also 'countE'.-countR :: Num n => Event a -> Reactive n-countR e = 0 `stepper` countE_ e---- | Tracing of reactive values-traceR :: (a -> String) -> Unop (Reactive a)-traceR shw (a `Stepper` e) = a `Stepper` traceE shw e---{---------------------------------------------------------------------    Other instances---------------------------------------------------------------------}---- Standard instances-instance Pair Reactive where pair = liftA2 (,)-instance (Monoid_f f) => Monoid_f (Reactive :. f) where-    { mempty_f = O (pure mempty_f); mappend_f = inO2 (liftA2 mappend_f) }-instance Pair f => Pair (Reactive :. f) where pair = apPair--instance Unpair Reactive where {fsts = fmap fst; snds = fmap snd}---- Standard instances-instance Monoid_f Event where-  { mempty_f = mempty ; mappend_f = mappend }-instance Monoid ((Event :. f) a) where-  { mempty = O mempty; mappend = inO2 mappend }-instance Monoid_f (Event :. f) where-  { mempty_f = mempty ; mappend_f = mappend }-instance Copair f => Pair (Event :. f) where-  pair = copair---- Standard instance for functors-instance Unpair Event where {fsts = fmap fst; snds = fmap snd}----{---------------------------------------------------------------------    Reactive behaviors over continuous time---------------------------------------------------------------------}---- | Time for continuous behaviors-type Time = Double---- | Reactive behaviors.  Simply a reactive 'Fun'ction value.  Wrapped in--- a type composition to get 'Functor' and 'Applicative' for free.-type ReactiveB = Reactive :. Fun Time---{---------------------------------------------------------------------    To be moved elsewhere---------------------------------------------------------------------}---- | Replace a functor value with a given one.-replace :: Functor f => b -> f a -> f b-replace b = fmap (const b)---- | Forget a functor value, replace with @()@-forget :: Functor f => f a -> f ()-forget = replace ()---- | Convenient alias for dropping parentheses.-type Action = IO ()---- | Value sink-type Sink a = a -> Action---- | Pass through @Just@ occurrences.-joinMaybes :: MonadPlus m => m (Maybe a) -> m a-joinMaybes = (>>= maybe mzero return)---- | Pass through values satisfying @p@.-filterMP :: MonadPlus m => (a -> Bool) -> m a -> m a-filterMP p m = joinMaybes (liftM f m)- where-   f a | p a       = Just a-       | otherwise = Nothing---- Alternatively:--- filterMP p m = m >>= guarded p---  where---    guarded p x = guard (p x) >> return x
− src/Data/SFuture.hs
@@ -1,195 +0,0 @@--- {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# OPTIONS -Wall -fno-warn-orphans #-}--- For ghc-6.6 compatibility-{-# OPTIONS_GHC -fglasgow-exts #-}--------------------------------------------------------------------------- |--- Module      :  Data.SFuture--- Copyright   :  (c) Conal Elliott 2007--- License     :  LGPL--- --- Maintainer  :  conal@conal.net--- Stability   :  experimental--- --- A sort of semantic prototype for functional /futures/, roughly as--- described at <http://en.wikipedia.org/wiki/Futures_and_promises>.--- --- A /future/ is a value that will become knowable only later.  This--- module gives a way to manipulate them functionally.  For instance,--- @a+b@ becomes knowable when the later of @a@ and @b@ becomes knowable.--- --- Primitive futures can be things like /the value of the next key you--- press/, or /the value of LambdaPix stock at noon next Monday/.--- --- Composition is via standard type classes: 'Ord', 'Functor',--- 'Applicative', 'Monad', and 'Monoid'.  Some comments on the 'Future'--- instances of these classes:--- --- * 'Ord': @a `min` b@ is whichever of @a@ and @b@ is knowable first.  @a---   `max` b@ is whichever of @a@ and @b@ is knowable last.--- --- * Monoid: 'mempty' is a future that never becomes knowable.  'mappend'---   is the same as 'min'.--- --- * 'Functor': apply a function to a future.  The result is knowable when---   the given future is knowable.--- --- * 'Applicative': 'pure' gives value knowable since the beginning of---   time.  '(\<*\>)' applies a future function to a future argument.---   Result available when /both/ are available, i.e., it becomes knowable---   when the later of the two futures becomes knowable.--- --- * 'Monad': 'return' is the same as 'pure' (as always).  @(>>=)@---   cascades futures.  'join' resolves a future future value into a---   future value.--- --- Futures are parametric over /time/ as well as /value/ types.  The time--- parameter can be any ordered type.--- --- Please keep in mind that this module specifies the interface and--- semantics, rather than a useful implementation.  See "Data.Future" for--- an implementation that nearly implements the semantics described here.--- --- On second thought, I'm experimenting with using this module in an--- usable implementation of events.  See Data.MEvent.-------------------------------------------------------------------------module Data.SFuture -  (-    -- * Time & futures-    Time, Future(..), futTime, futVal, sequenceF-    -- * To go elsewhere-  , Max(..), Min(..), AddBounds(..)-  ) where--import Data.Monoid (Monoid(..))-import Control.Applicative (Applicative(..))-import Data.Function (on)---{-----------------------------------------------------------    Time and futures-----------------------------------------------------------}---- | Time of some event occurrence, which can be any @Ord@ type.  In an--- actual implementation, we would not usually have access to the time--- value until (slightly after) that time.  Extracting the actual time--- would block until the time is known.  The added bounds represent--- -Infinity and +Infinity.  Pure values have time minBound (-Infinity),--- while eternally unknowable values (non-occurring events) have time--- maxBound (+Infinity).-type Time t = Max (AddBounds t)---- | A future value of type @a@ with time type @t@.  Semantically, just a--- time\/value pair, but those values would not be available until--- 'force'd, which could block.-newtype Future t a = Future { unFuture :: (Time t, a) }-  deriving (Functor, Applicative, Monad, Show)----  The 'Applicative' instance relies on the 'Monoid' instance of 'Max'.---- | A future's time-futTime :: Future t a -> Time t-futTime = fst . unFuture---- | A future's value-futVal :: Future t a -> a-futVal = snd . unFuture----- -- The Monoid instance picks the earlier future--- instance Ord t => Monoid (Future t a) where---   mempty  = Future (maxBound, error "it'll never happen, buddy")---   fut@(Future (t,_)) `mappend` fut'@(Future (t',_)) =---     if t <= t' then fut else fut'---- or:---instance Eq (Future t a) where-  (==) = error "sorry, no (==) for futures"--instance Ord t => Ord (Future t a) where-  (<=) = (<=) `on` futTime-  -- We could leave 'min' to the default in terms of '(<=)', but the-  -- following can yield partial time info, as much as allowed by the time-  -- parameter type @t@ and its 'min'.-  Future (s,a) `min` Future (t,b) =-    Future (s `min` t, if s <= t then a else b)---- For some choices of @t@, there may be an efficient combination of 'min'--- and '(<=)'.  In particular, 'Improving' has 'minI'.--instance Ord t => Monoid (Future t a) where-  mempty  = Future (maxBound, error "it'll never happen, buddy")-  mappend = min---- 'sequenceF' is like 'sequenceA' from "Data.Traversable".  However,--- the @Traversable@ class assumes @Foldable@, which I'm not confident--- how to implement usefully.  (I could of course just strip off the--- 'Future' constructor and the time.  Why is Foldable required?---- | Make a future container into a container of futures.-sequenceF :: Functor f => Future t (f a) -> f (Future t a)-sequenceF (Future (tt, f)) = fmap (Future . ((,) tt)) f----{-----------------------------------------------------------    To go elsewhere-----------------------------------------------------------}---- For Data.Monoid:---- | Ordered monoid under 'max'.-newtype Max a = Max { getMax :: a }-	deriving (Eq, Ord, Read, Show, Bounded)--instance (Ord a, Bounded a) => Monoid (Max a) where-	mempty = Max minBound-	Max a `mappend` Max b = Max (a `max` b)---- | Ordered monoid under 'min'.-newtype Min a = Min { getMin :: a }-	deriving (Eq, Ord, Read, Show, Bounded)--instance (Ord a, Bounded a) => Monoid (Min a) where-	mempty = Min maxBound-	Min a `mappend` Min b = Min (a `min` b)---- I have a niggling uncertainty about the 'Ord' & 'Bounded' instances for--- @Min a@?  Is there a reason flip the @a@ ordering instead of preserving--- it?---- For Control.Monad.Instances---- Equivalent to the Monad Writer instance.--- import Data.Monoid-instance Monoid o => Monad ((,) o) where-  return      = pure-  (o,a) >>= f = (o `mappend` o', a') where (o',a') = f a---- Alternatively,---   m >>= f = join (fmap f m)---    where---      join ((o, (o',a))) = (o `mappend` o', a)--- Or even,---   (o,a) >>= f = (o,id) <*> f a--- --- I prefer the join version, because it's the standard (>>=)-via-join,--- plus a very simple definition for join.  Too bad join isn't a method of--- Monad, with (>>=) and join defined in terms of each other.  Why isn't--- it?  Probably because Monad isn't derived from Functor.  Was that an--- oversight?---- Where to put this definition?  Prelude?---- | Wrap a type into one having new least and greatest elements,--- preserving the existing ordering.-data AddBounds a = MinBound | NoBound a | MaxBound-	deriving (Eq, Ord, Read, Show)--instance Bounded (AddBounds a) where-	minBound = MinBound-	maxBound = MaxBound
+ src/Examples.hs view
@@ -0,0 +1,311 @@+{-# LANGUAGE TypeOperators, FlexibleContexts, TypeSynonymInstances, FlexibleInstances #-}++----------------------------------------------------------------------+-- |+-- Module      :  Examples+-- Copyright   :  (c) Conal Elliott 2007+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Simple test for Reactive+----------------------------------------------------------------------++-- module Main where++-- base+import Data.Monoid+import Data.IORef+import Control.Monad+import Control.Applicative+import Control.Arrow (first,second)+import Control.Concurrent (yield, forkIO, killThread, threadDelay, ThreadId)++-- wxHaskell+import Graphics.UI.WX hiding (Event,Reactive)+import qualified Graphics.UI.WX as WX+-- TypeCompose+import Control.Compose ((:.)(..), inO,inO2)+import Data.Title++-- Reactive+import Reactive.Reactive+++{--------------------------------------------------------------------+    Mini-Phooey+--------------------------------------------------------------------}++type Win = Panel ()++type Wio = ((->) Win) :. IO :. (,) Layout++type Wio' a = Win -> IO (Layout,a)+++wio :: Wio' a -> Wio a+wio = O . O++unWio :: Wio a -> Wio' a+unWio = unO . unO++inWio :: (Wio' a -> Wio' b) -> (Wio a -> Wio b)+inWio f = wio . f . unWio++inWio2 :: (Wio' a -> Wio' b -> Wio' c) -> (Wio a -> Wio b -> Wio c)+inWio2 f = inWio . f . unWio++instance Title_f Wio where+  title_f str = inWio ((fmap.fmap.first) (boxed str))++-- Bake in vertical layout.  See phooey for flexible layout.+instance Monoid Layout where+  mempty  = WX.empty+  mappend = above++instance Monoid a => Monoid (Wio a) where+  mempty  = wio    mempty+  mappend = inWio2 mappend++type WioE a = Wio (Event    a)+type WioR a = Wio (Reactive a)++buttonE :: String -> WioE ()+buttonE str = wio $ \ win ->+  do (e, snk) <- mkEvent+     b <- button win [ text := str, on command := snk () ]+     return (hwidget b, e)++buttonE' :: String -> a -> WioE a+buttonE' str a = (a `replace`) <$> buttonE str++sliderE :: (Int,Int) -> Int -> WioE Int+sliderE (lo,hi) initial = wio $ \ win ->+  do (e, snk) <- mkEvent+     s <- hslider win True lo hi+            [ selection := initial ]+     set s [ on command := getAttr selection s >>= snk ]+     return (hwidget s, e)++sliderR :: (Int,Int) -> Int -> WioR Int+sliderR lh initial = stepper initial <$> sliderE lh initial++stringO :: Wio (Sink String)+stringO = attrO (flip textEntry []) text++-- Make an output.  The returned sink collects updates.  On idle, the+-- latest update gets stored in the given attribute.+attrO :: Widget w => (Win -> IO w) -> Attr w a -> Wio (Sink a)+attrO mk attr = wio $ \ win ->+  do ctl <- mk win+     ref <- newIORef Nothing+     setAttr (on idle) win $+       do readIORef ref >>= maybe mempty (setAttr attr ctl)+          writeIORef ref Nothing+          return True+     return (hwidget ctl , writeIORef ref . Just)++-- -- The following alternative ought to be more efficient.  Oddly, the timer+-- -- doesn't get restarted, although enabled gets set to True.++-- stringO = wio $ \ win ->+--   do ctl <- textEntry win []+--      ref <- newIORef (error "stringO: no initial value")+--      tim <- timer win [ interval := 10, enabled := False ]+--      let enable b = do putStrLn $ "enable: " ++ show b+--                        setAttr enabled tim b+--      set tim [ on command := do putStrLn "timer"+--                                 readIORef ref >>= setAttr text ctl+--                                 enable False+--              ]+--      return ( hwidget ctl+--             , \ str -> writeIORef ref str >> enable True )++showO :: Show a => Wio (Sink a)+showO = (. show) <$> stringO++showR :: Show a => WioR (Sink a)+showR = pure <$> showO+++-- | Horizontally-filled widget layout+hwidget :: Widget w => w -> Layout+hwidget = hfill . widget++-- | Binary layout combinator+above, leftOf :: Layout -> Layout -> Layout+la `above`   lb = fill (column  0 [la,lb])+la `leftOf`  lb = fill (row     0 [la,lb])++-- |  Get attribute.  Just a flipped 'get'.  Handy for partial application.+getAttr :: Attr w a -> w -> IO a+getAttr = flip get++-- | Set a single attribute.  Handy for partial application.+setAttr :: Attr w a -> w -> Sink a+setAttr attr ctl x = set ctl [ attr := x ]+++{--------------------------------------------------------------------+    Running+--------------------------------------------------------------------}++-- | Fork a 'Wio': handle frame & widget creation, and apply layout.+forkWio :: (o -> IO ThreadId) -> String -> Wio o -> IO ()+forkWio forker name w = start $+  do  f     <- frame [ visible := False, text := name ]+      pan   <- panel f []+      (l,o) <- unWio w pan+      set pan [ layout := l ]+      forker o+      -- Yield regularly, to allow other threads to continue.  Unnecessary+      -- when apps are compiled with -threaded.+      -- timer pan [interval := 10, on command := yield]+      set f   [ layout  := fill (widget pan)+              , visible := True+              ]++-- | Fork a 'WioE'+forkWioE :: String -> WioE Action -> IO ()+forkWioE = forkWio forkE++-- | Fork a 'WioR'+forkWioR :: String -> WioR Action -> IO ()+forkWioR = forkWio forkR+++{--------------------------------------------------------------------+    Examples+--------------------------------------------------------------------}++alarm :: Double -> Int -> IO (Event Int)+alarm secs reps =+  do (e,snk) <- mkEvent+     forkIO $ forM_ [1 .. reps] $ \ i ->+               do threadDelay micros+                  snk i+     return e+ where+   micros = round (1.0e6 * secs)+                          ++t0 = alarm 0.5 10 >>= \ e -> runE $ print <$> {-traceE (const "boo!")-} e++mkAB :: WioE String+mkAB = buttonE' "a" "a" `mappend` buttonE' "b" "b"+++t1 = forkWioE "t1" $ liftA2 (<$>) stringO mkAB++acc :: WioE String+acc = g <$> mkAB+ where+   g :: Event String -> Event String+   g e = "" `accumE` (flip (++) <$> e)++t2 = forkWioE "t2" $ liftA2 (<$>) stringO acc++total :: Show a => WioR (Sink a)+total = title "total" showR++sl :: Int -> WioR Int+sl = sliderR (0,100)++apples, bananas, fruit :: WioR Int+apples  = title "apples"  $ sl 3+bananas = title "bananas" $ sl 7+fruit   = title "fruit"   $ (liftA2.liftA2) (+) apples bananas++t3 = forkWioR "t3" $ liftA2 (<**>) fruit total ++t4 = forkWioR "t4" $ liftA2 (<*>) showR (sl 0)++t5 = forkWioR "t5" $ liftA2 (<$>) showO (sl 0)++-- This example shows what happens with expensive computations.  There's a+-- lag between slider movement and shown result.  Can even get more than+-- one computation behind.+t6 = forkWioR "t6" $ liftA2 (<$>) showO (fmap (ack 2) <$> sliderR (0,1000) 0)++ack 0 n = n+1+ack m 0 = ack (m-1) 1+ack m n = ack (m-1) (ack m (n-1))++-- Test switchers.  Ivan Tomac's example.+sw1 = do (e, snk) <- mkEvent+         forkR $ print <$> pure "init" `switcher` ((\_ -> pure "next") <$> e)+         snk ()+         snk ()++-- TODO: replace sw1 with a declarative GUI example, say switching between+-- two different previous GUI examples.++main = t6+++updPair :: Either c d -> (c,d) -> (c,d)+updPair = (first.const) `either` (second.const)++-- updPair (Left  c') (_,d) = (c',d)+-- updPair (Right d') (c,_) = (c,d')++-- mixEither :: (Event c, Event d) -> Event (Either c d)+-- mixEither :: (Functor f, Monoid (f (Either a b))) =>+--               (f a, f b) -> f (Either a b)+mixEither :: MonadPlus m => (m a, m b) -> m (Either a b)+mixEither (ec,ed) = liftM Left ec `mplus` liftM Right ed++-- unmixEither :: Event (Either c d) -> (Event c, Event d)+unmixEither :: MonadPlus m => m (Either c d) -> (m c, m d)+unmixEither ecd = (filt left, filt right)+ where+   filt f = joinMaybes (liftM f ecd)++left :: Either c d -> Maybe c+left (Left  c) = Just c+left _         = Nothing++right :: Either c d -> Maybe d+right (Right d) = Just d+right _         = Nothing+++-- pairEditE :: (Event c, Event d) -> Event ((c,d) -> (c,d))++-- pairEditE :: (Functor f, Monoid (f ((d, a) -> (d, a)))) =>+--              (f d, f a) -> f ((d, a) -> (d, a))+-- pairEditE (ce,de) =+--   ((first.const) <$> ce) `mappend` ((second.const) <$> de)++-- pairEditE :: (Functor m, MonadPlus m) => (m d, m a) -> m ((d, a) -> (d, a))+-- pairEditE (ce,de) =+--   ((first.const) <$> ce) `mplus` ((second.const) <$> de)++pairEditE :: MonadPlus m => (m c,m d) -> m ((c,d) -> (c,d))+pairEditE = liftM updPair . mixEither++-- pairEditE cde = liftM updPair (mixEither cde)++-- or, skipping sums++-- pairEditE (ce,de) =+--   liftM (first.const) ce `mplus` liftM (second.const) de++pairE :: (c,d) -> (Event c, Event d) -> Event (c,d)+pairE cd cde = cd `accumE` pairEditE cde++pairR :: Reactive c -> Reactive d -> Reactive (c,d)++-- (c `Stepper` ce) `pairR` (d `Stepper` de) =+--   (c,d) `stepper` pairE (c,d) (ce,de)++-- More directly:++(c `Stepper` ce) `pairR` (d `Stepper` de) =+  (c,d) `accumR` pairEditE (ce,de)++-- pairR' :: Reactive c -> Reactive d -> Reactive (c,d)+-- (c `Stepper` ce) `pairR'` (d `Stepper` de) =+--   (c,d) `accumR` pairEditE (ce,de)+
+ src/FRP/Reactive.hs view
@@ -0,0 +1,46 @@+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- A library for programming with functional reactive behaviors.+----------------------------------------------------------------------++module FRP.Reactive+  (+    -- * Events+    TimeT, ITime+  , EventG, Event+  , accumE+  , withTimeE+  , pairE, scanlE, monoidE+  , stateE, stateE_, countE, countE_, diffE+  , withPrevE, withPrevEWith+  , whenE, eitherE+    -- ** More esoteric+  , listE, atTimes, atTime, once+  , firstRestE, firstE, restE+  , withRestE, untilE+  , splitE, switchE+    -- ** Useful with events.+  , joinMaybes, filterMP+    -- * Behaviors+  , BehaviorG, Behavior+  , time+  , stepper, switcher --, select+  , snapshotWith, snapshot, snapshot_+  , accumB+  , scanlB, monoidB, maybeB, flipFlop, countB+  , sumB, integral+  ) where++-- Reactive.Reactive exports reactive values as well.  Filter them out.++import FRP.Reactive.Reactive hiding+  (stepper,switcher,snapshotWith,snapshot,snapshot_,flipFlop,integral)+import FRP.Reactive.Behavior
+ src/FRP/Reactive/Behavior.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Behavior+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Reactive behaviors (continuous time)+----------------------------------------------------------------------++module FRP.Reactive.Behavior+  (+    BehaviorG, Behavior+  , time+  , stepper, switcher --, select+  , snapshotWith, snapshot, snapshot_+  , accumB+  , scanlB, monoidB, maybeB, flipFlop, countB+  , sumB, integral+  ) where++import Data.Monoid (Monoid)+import Control.Applicative (Applicative,pure,(<$>))++import Data.VectorSpace++import qualified FRP.Reactive.Reactive as R+import FRP.Reactive.Reactive (TimeT, ITime, Event, withTimeE, diffE)+import FRP.Reactive.Fun+import FRP.Reactive.Internal.Behavior+++-- | Time-specialized behaviors.+-- Note: The signatures of all of the behavior functions can be generalized.  Is+-- the interface generality worth the complexity?+type Behavior = BehaviorG ITime TimeT++-- | The identity generalized behavior.  Has value @t@ at time @t@.+time :: Behavior TimeT+time = beh (pure (fun id))++-- Turn a reactive value into a discretly changing behavior.+rToB :: R.Reactive a -> Behavior a+rToB = beh . fmap pure++-- Then use 'rToB' to promote reactive value functions to behavior+-- functions.++-- | Discretely changing behavior, based on an initial value and a+-- new-value event.+stepper :: a -> Event a -> Behavior a+stepper = (fmap.fmap) rToB R.stepper++-- Suggested by Robin Green:++-- stepper = select pure++-- -- | Use a key event to key into a behaviour-valued function+-- select :: (a -> Behavior b) -> a -> Event a -> Behavior b+-- select f a e = f a `switcher` (f <$> e)++-- Looking for a more descriptive name.++-- | Switch between behaviors.+switcher :: Behavior a -> Event (Behavior a) -> Behavior a+b `switcher` eb = beh (unb b `R.switcher` (unb <$> eb))++-- | Snapshots a behavior whenever an event occurs and combines the values+-- using the combining function passed.+snapshotWith :: (a -> b -> c) -> Event a -> Behavior b -> Event c+snapshotWith h e b = f <$> (withTimeE e `R.snapshot` unb b)+ where+   f ((a,t),tfun) = h a (tfun `apply` t)++-- | Snapshot a behavior whenever an event occurs.  See also 'snapshotWith'.+snapshot :: Event a -> Behavior b -> Event (a,b)+snapshot = snapshotWith (,)++-- Alternative implementations:+--   snapshotWith c e b = uncurry c <$> snapshot e b+--   snapshotWith c = (fmap.fmap.fmap) (uncurry c) snapshot++-- | Like 'snapshot' but discarding event data (often @a@ is '()').+snapshot_ :: Event a -> Behavior b -> Event b+snapshot_ = snapshotWith (flip const)++-- Alternative implementations+-- e `snapshot_` src = snd <$> (e `snapshot` src)+-- snapshot_ = (fmap.fmap.fmap) snd snapshot++-- | Behavior from an initial value and an updater event.  See also+-- 'accumE'.+accumB :: a -> Event (a -> a) -> Behavior a+accumB = (fmap.fmap) rToB R.accumR++-- | Like 'scanl' for behaviors.  See also 'scanlE'.+scanlB :: (a -> b -> a) -> a -> Event b -> Behavior a+scanlB = (fmap.fmap.fmap) rToB R.scanlR++-- | Accumulate values from a monoid-valued event.  Specialization of+-- 'scanlE', using 'mappend' and 'mempty'.  See also 'monoidE'.+monoidB :: Monoid a => Event a -> Behavior a+monoidB = fmap rToB R.monoidR++-- | Like 'sum' for behaviors.+sumB :: VectorSpace v s => Event v -> Behavior v+sumB = fmap rToB R.sumR++-- | Start out blank ('Nothing'), latching onto each new @a@, and blanking+-- on each @b@.  If you just want to latch and not blank, then use+-- 'mempty' for the second event.+maybeB :: Event a -> Event b -> Behavior (Maybe a)+maybeB = (fmap.fmap) rToB R.maybeR++-- | Flip-flopping behavior.  Turns true whenever first event occurs and+-- false whenever the second event occurs.+flipFlop :: Event a -> Event b -> Behavior Bool+flipFlop = (fmap.fmap) rToB R.flipFlop++-- | Count occurrences of an event.  See also 'countE'.+countB :: Num n => Event a -> Behavior n+countB = fmap rToB R.countR++-- | Euler integral.+integral :: VectorSpace v TimeT =>+            Event () -> Behavior v -> Behavior v+integral t = sumB . snapshotWith (*^) (diffE (t `snapshot_` time))++-- TODO: find out whether this integral works recursively.  If not, then+-- fix the implementation, rather than changing the semantics.  (No+-- "delayed integral".)+-- +-- Early experiments suggest that recursive integration gets stuck.+-- Investigate.
+ src/FRP/Reactive/Fun.hs view
@@ -0,0 +1,139 @@+{-# LANGUAGE CPP, MultiParamTypeClasses, ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Fun+-- Copyright   :  (c) Conal Elliott 2007+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Functions, with constant functions optimized.  With instances of+-- 'Functor', 'Applicative', 'Monad', and 'Arrow'+----------------------------------------------------------------------++module FRP.Reactive.Fun (Fun, fun, apply, batch) where++import Data.Monoid (Monoid(..))+import Control.Applicative (Applicative(..),liftA)+#if __GLASGOW_HASKELL__ >= 609+import Control.Category+import Prelude hiding ((.), id)+#endif+import Control.Arrow +#if __GLASGOW_HASKELL__ < 610+                     hiding (pure)+#endif++import Test.QuickCheck+import Test.QuickCheck.Checkers+import Test.QuickCheck.Classes+import Test.QuickCheck.Applicative ()++import Text.Show.Functions ()++import FRP.Reactive.Internal.Fun+++-- TODO: write RULE for fun . const = K+fun :: (t -> a) -> Fun t a+fun = Fun++instance (Arbitrary a,Arbitrary b) => Arbitrary (Fun a b) where+  arbitrary = oneof [liftA K arbitrary, liftA Fun arbitrary]+  coarbitrary (K a)   = variant 0 . coarbitrary a+  coarbitrary (Fun x) = variant 1 . coarbitrary x++instance Show b => Show (Fun a b) where+  show (K x)   = "K " ++ show x+  show (Fun f) = "Fun " ++ show f++instance (Show a, Arbitrary a, EqProp a, EqProp b) => EqProp (Fun a b) where+  (=-=) = eqModels++instance Model (Fun a b) (a -> b) where+  model = apply++instance Model1 (Fun a) ((->) a) where+  model1 = apply++-- | 'Fun' as a function+apply :: Fun t a -> (t -> a)+apply (K   a) = const a+apply (Fun f) = f++instance Monoid a => Monoid (Fun t a) where+  mempty = K mempty+  K a  `mappend` K a' = K (a `mappend` a')+  funa `mappend` funb = Fun (apply funa `mappend` apply funb)++instance Functor (Fun t) where+  fmap f (K   a) = K   (f a)+  fmap f (Fun g) = Fun (f.g)+  -- Or use+  --  fmap f = (pure f <*>)++instance Applicative (Fun t) where+  pure        = K+  K f <*> K x = K   (f x)+  cf  <*> cx  = Fun (apply cf <*> apply cx)++instance Monad (Fun t) where+  return = pure+  K   a >>= h = h a+  Fun f >>= h = Fun (f >>= apply . h)++#if __GLASGOW_HASKELL__ >= 609+instance Category Fun where+  id = Fun id+  K   b . _     = K   b+  Fun g . K   a = K   (g a)+  Fun f . Fun g = Fun (f . g)+#endif++instance Arrow Fun where+  arr             = Fun+#if __GLASGOW_HASKELL__ < 609+  _     >>> K b   = K   b+  K a   >>> Fun g = K   (g a)+  Fun g >>> Fun f = Fun (g >>> f)+#endif+  first           = Fun . first  . apply+  second          = Fun . second . apply+  K a'  *** K b'  = K (a',b')+  f     *** g     = first f >>> second g++batch :: TestBatch+batch = ( "FRP.Reactive.Fun"+        , concatMap unbatch+            [ monoid              (undefined :: Fun NumT+                                                    [T])+            , semanticMonoid      (undefined :: Fun NumT+                                                    [T])+            , functor             (undefined :: Fun NumT+                                                    (NumT+                                                    ,T+                                                    ,NumT))+            , semanticFunctor     (undefined :: Fun NumT ())+            , applicative         (undefined :: Fun NumT+                                                    (NumT+                                                    ,T+                                                    ,NumT))+            , semanticApplicative (undefined :: Fun NumT ())+            , monad               (undefined :: Fun NumT+                                                    (NumT+                                                    ,T+                                                    ,NumT))+            , semanticMonad       (undefined :: Fun NumT ())+            , arrow               (undefined :: Fun NumT+                                                    (NumT+                                                    ,T+                                                    ,NumT))+            , ("specifics",+                [("Constants are"+                 ,property (\x -> (K (x :: NumT)) =-=+                                  ((fun . const $ x) :: Fun T+                                                            NumT)))])+            ]+        )
+ src/FRP/Reactive/Future.hs view
@@ -0,0 +1,196 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}++----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Future+-- Copyright   :  (c) Conal Elliott 2007-2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- A simple formulation of functional /futures/, roughly as+-- described at <http://en.wikipedia.org/wiki/Futures_and_promises>.+-- +-- A /future/ is a value with an associated time of /arrival/.  Typically,+-- neither the time nor the value can be known until the arrival time.+-- +-- Primitive futures can be things like /the value of the next key you+-- press/, or /the value of LambdaPix stock at noon next Monday/.+-- +-- Composition is via standard type classes: 'Functor', 'Applicative',+-- 'Monad', and 'Monoid'.  Some comments on the 'Future' instances of+-- these classes:+-- +-- * Monoid: 'mempty' is a future that never arrives (infinite time and+--   undefined value), and @a `mappend` b@ is the earlier of @a@ and @b@,+--   preferring @a@ when simultaneous.+-- +-- * 'Functor': apply a function to a future argument.  The (future)+-- result arrives simultaneously with the argument.+-- +-- * 'Applicative': 'pure' gives value arriving negative infinity.+-- '(\<*\>)' applies a future function to a future argument, yielding a+-- future result that arrives once /both/ function and argument have+-- arrived (coinciding with the later of the two times).+-- +-- * 'Monad': 'return' is the same as 'pure' (as usual).  @(>>=)@ cascades+-- futures.  'join' resolves a future future value into a future value.+-- +-- Futures are parametric over /time/ as well as /value/ types.  The time+-- parameter can be any ordered type and is particularly useful with time+-- types that have rich partial information structure, such as /improving+-- values/.+----------------------------------------------------------------------++module FRP.Reactive.Future +  (+    -- * Time & futures+    Time, ftime+  , FutureG(..), inFuture, inFuture2, futTime, futVal, future+  , withTimeF+  -- * Tests+  , batch+  ) where++import Data.Monoid (Monoid(..))++import Data.Max+import Data.AddBounds+import FRP.Reactive.Internal.Future++-- Testing+import Test.QuickCheck+import Test.QuickCheck.Checkers+import Test.QuickCheck.Classes++{----------------------------------------------------------+    Time and futures+----------------------------------------------------------}++-- | Make a finite time+ftime :: t -> Time t+ftime = Max . NoBound++-- FutureG representation in Internal.Future++instance (EqProp t, Eq t, EqProp a) => EqProp (FutureG t a) where+  Future (Max MaxBound,_) =-= Future (Max MaxBound,_) = property True+  Future a =-= Future b = a =-= b++-- | A future's time+futTime :: FutureG t a -> Time t+futTime = fst . unFuture++-- | A future's value+futVal :: FutureG t a -> a+futVal = snd . unFuture++-- | A future value with given time & value+future :: t -> a -> FutureG t a+future t a = Future (ftime t, a)++-- | Access time of future+withTimeF :: FutureG t a -> FutureG t (Time t, a)+withTimeF = inFuture $ \ (t,a) -> (t,(t,a))++instance Ord t => Monoid (FutureG t a) where+  mempty = Future (maxBound, error "Future mempty: it'll never happen, buddy")+  -- Pick the earlier future.+  Future (s,a) `mappend` Future (t,b) =+    Future (s `min` t, if s <= t then a else b)++-- -- A future known never to happen (by construction), i.e., infinite time.+-- isNever :: FutureG t a -> Bool+-- isNever = isMaxBound . futTime+--  where+--    isMaxBound (Max MaxBound) = True+--    isMaxBound _              = False++-- Consider the following simpler definition:+-- +--   fa@(Future (s,_)) `mappend` fb@(Future (t,_)) =+--     if s <= t then fa else fb+-- +-- Nothing can be known about the resulting future until @s <= t@ is+-- determined.  In particular, we cannot know lower bounds for the time.+-- In contrast, the actual 'mappend' definition can potentially yield+-- useful partial information, such as lower bounds, about the future+-- time, if the type parameter @t@ has rich partial information structure+-- (non-flat).++-- For some choices of @t@, there may be an efficient combination of 'min'+-- and '(<=)', so the 'mappend' definition is sub-optimal.  In particular,+-- 'Improving' has 'minI'.++++{----------------------------------------------------------+    Tests+----------------------------------------------------------}++-- Represents times at a given instant.+newtype TimeInfo t = TimeInfo (Maybe t)+  deriving EqProp++-- A time at a given instant can be some unknown time in the future+unknownTimeInFuture :: TimeInfo a+unknownTimeInFuture = TimeInfo Nothing++-- or, a known time in the past. We're ignoring known future times for now.+knownTimeInPast :: a -> TimeInfo a+knownTimeInPast = TimeInfo . Just++instance Eq a => Eq (TimeInfo a) where+  TimeInfo Nothing == TimeInfo Nothing = error "Cannot tell if two unknown times in the future are equal"+  TimeInfo (Just _) == TimeInfo Nothing = False+  TimeInfo Nothing == TimeInfo (Just _) = False+  TimeInfo (Just a) == TimeInfo (Just b) = a == b++instance Ord a => Ord (TimeInfo a) where+  -- The minimum of two unknown times in the future is an unkown time in the+  -- future.+  TimeInfo Nothing `min` TimeInfo Nothing = unknownTimeInFuture+  TimeInfo Nothing `min` b = b+  a `min` TimeInfo Nothing = a+  TimeInfo (Just a) `min` TimeInfo (Just b) = (TimeInfo . Just) (a `min` b)+  +  TimeInfo Nothing <= TimeInfo Nothing = error "Cannot tell if one unknown time in the future is less than another."+  TimeInfo Nothing <= TimeInfo (Just _) = False+  TimeInfo (Just _) <= TimeInfo Nothing = True+  TimeInfo (Just a) <= TimeInfo (Just b) = a <= b++batch :: TestBatch+batch = ( "FRP.Reactive.Future"+        , concatMap unbatch+            [ monoid (undefined :: FutureG NumT T)+            , functorMonoid (undefined :: FutureG NumT+                                                  (T,NumT))+            -- Checking the semantics here isn't necessary because+            -- the implementation is identical to them.+            --+            -- Also, Functor, Applicative, and Monad don't require checking+            -- since they are automatically derived.+            --+            -- , semanticMonoid' (undefined :: FutureG NumT T)+            -- , functor (undefined :: FutureG NumT (T,NumT,T))+            -- , semanticFunctor (undefined :: FutureG NumT ())+            -- , applicative (undefined :: FutureG NumT (NumT,T,NumT))+            -- , semanticApplicative (undefined :: FutureG NumT ())+            -- , monad (undefined :: FutureG NumT (NumT,T,NumT))+            -- , semanticMonad (undefined :: FutureG NumT ())++            , ("specifics",+                [ ("laziness", property laziness )+                       ])+            ]+        )+ where+   laziness :: NumT -> T -> Property+   laziness t a = (uf `mappend` uf) `mappend` kf  =-= kf+      where+        uf = unknownFuture+        kf = knownFuture+        knownFuture = future (knownTimeInPast t) a+        unknownFuture = future unknownTimeInFuture (error "cannot retrieve value at unknown time at the future")
+ src/FRP/Reactive/Improving.hs view
@@ -0,0 +1,79 @@+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Improving+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Improving values -- efficient version+----------------------------------------------------------------------++module FRP.Reactive.Improving+  (+    Improving(..), exactly, minI, maxI+  ) where+++import Data.Function (on)++import Data.Unamb (unamb,asAgree)+import Test.QuickCheck.Checkers++{----------------------------------------------------------+    Improving values+----------------------------------------------------------}++-- | An improving value.+data Improving a = Imp { exact :: a, compareI :: a -> Ordering }++-- | A known improving value (which doesn't really improve)+exactly :: Ord a => a -> Improving a+exactly a = Imp a (compare a)++instance Eq a => Eq (Improving a) where+  (==) = (==) `on` exact++instance Ord a => Ord (Improving a) where+  s `min` t = fst (s `minI` t)+  s  <=   t = snd (s `minI` t)++-- | Efficient combination of 'min' and '(<=)'+minI :: Ord a => Improving a -> Improving a -> (Improving a,Bool)+Imp u uComp `minI` Imp v vComp = (Imp uMinV wComp, uLeqV)+ where+   uMinV = if uLeqV then u else v+   -- u <= v: Try @v `compare` u /= LT@ and @u `compare` v /= GT@.+   uLeqV = (vComp u /= LT) `unamb` (uComp v /= GT)+   minComp = if uLeqV then uComp else vComp+   -- (u `min` v) `compare` t: Try comparing according to whether u <= v,+   -- or go with either answer if they agree, e.g., if both say GT.+   wComp t = minComp t `unamb` (uComp t `asAgree` vComp t)++-- | Efficient combination of 'max' and '(>=)'+maxI :: Ord a => Improving a -> Improving a -> (Improving a,Bool)+Imp u uComp `maxI` Imp v vComp = (Imp uMaxV wComp, uGeqV)+ where+   uMaxV = if uGeqV then u else v+   -- u >= v: Try @v `compare` u /= GT@ and @u `compare` v /= LT@.+   uGeqV = (vComp u /= GT) `unamb` (uComp v /= LT)+   -- (u `max` v) `compare` t: Try comparing according to whether u >= v,+   -- or go with either answer if they agree, e.g., if both say LT.+   maxComp = if uGeqV then uComp else vComp+   wComp t = maxComp t `unamb` (uComp t `asAgree` vComp t)++-- TODO: factor commonality out of 'minI' and 'maxI' or combine into+-- a single function.++-- -- | Interpret 'Nothing' values as lower bounds+-- improveMbs :: [(t, Maybe a)] -> [(Improving t, a)]+-- ...++-- No.  Don't implement & export improveMbs.  If it's being used, then+-- we're not benefitting from this fancy multi-threaded implementation of+-- Improving.++instance (EqProp a) => EqProp (Improving a) where+  (Imp a _) =-= (Imp b _) = a =-= b
+ src/FRP/Reactive/Internal/Behavior.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE TypeOperators, GeneralizedNewtypeDeriving #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Behavior+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Representation of reactive behaviors+----------------------------------------------------------------------++module FRP.Reactive.Internal.Behavior (BehaviorG(..), beh, unb) where+++import Control.Applicative (Applicative)++-- TypeCompose+import Control.Compose ((:.)(..))++import qualified FRP.Reactive.Reactive as R+-- import FRP.Reactive.Reactive (TimeT)+import FRP.Reactive.Fun+++-- Reactive behaviors.  Simply a reactive 'Fun'ction value.  Wrapped in+-- a type composition to get 'Functor' and 'Applicative' for free.++-- | Reactive behaviors.  They can be understood in terms of a simple+-- model (denotational semantics) as functions of time, namely @at ::+-- BehaviorG t a -> (t -> a)@.+-- +-- The semantics of 'BehaviorG' instances are given by corresponding+-- instances for the semantic model (functions).  See+-- <http://conal.net/blog/posts/simplifying-semantics-with-type-class-morphisms/>.+-- +-- * 'Functor': @at (fmap f r) == fmap f (at r)@, i.e., @fmap f r `at`+--   t == f (r `at` t)@.+-- +-- * 'Applicative': @at (pure a) == pure a@, and @at (s \<*\> r) == at s+--   \<*\> at t@.  That is, @pure a `at` t == a@, and @(s \<*\> r) `at` t+--   == (s `at` t) (r `at` t)@.+-- +-- * 'Monad': @at (return a) == return a@, and @at (join rr) == join (at+--   . at rr)@.  That is, @return a `at` t == a@, and @join rr `at` t ==+--   (rr `at` t) `at` t@.  As always, @(r >>= f) == join (fmap f r)@.+--   @at (r >>= f) == at r >>= at . f@.+-- +-- * 'Monoid': a typical lifted monoid.  If @o@ is a monoid, then+--   @Reactive o@ is a monoid, with @mempty == pure mempty@, and @mappend+--   == liftA2 mappend@.  That is, @mempty `at` t == mempty@, and @(r+--   `mappend` s) `at` t == (r `at` t) `mappend` (s `at` t).@+newtype BehaviorG tr tf a = Beh { unBeh :: (R.ReactiveG tr :. Fun tf) a }+   deriving (Functor,Applicative)++-- | Wrap a reactive time fun as a behavior.+beh :: R.ReactiveG tr (Fun tf a) -> BehaviorG tr tf a+beh = Beh . O++-- | Unwrap a behavior.+unb :: BehaviorG tr tf a -> R.ReactiveG tr (Fun tf a)+unb = unO . unBeh
+ src/FRP/Reactive/Internal/Clock.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE ScopedTypeVariables, Rank2Types #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Clock+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Serializing clocks+-- +-- Thanks to Luke Palmer for help with this module.+----------------------------------------------------------------------++module FRP.Reactive.Internal.Clock+  (Clock(..), makeClock) where++import Control.Applicative (liftA2)+import System.Time++import FRP.Reactive.Reactive (TimeT)+-- import FRP.Reactive.Internal.Misc (Sink)+import FRP.Reactive.Internal.Serial+++-- | Waits a specified duration and then execute an action+-- type Delay t = t -> forall a. IO a -> IO a++-- | Waits until just after a specified time and then execute an action,+-- passing in the actual time.+-- type Schedule t = t -> Sink (Sink t)++-- | A serializing clock.  Can (a) produce a time and (b) serialize an+-- action.+data Clock t = Clock { cGetTime   :: IO t+                     , cSerialize :: Serial+                     }++-- | Make a clock, given a way to delay actions.  For instance, the delay+-- could be 'sleepThen' in thread-safe situations, but could also+-- involve a GUI toolkit wake-up event.+makeClock :: IO (Clock TimeT)+makeClock = liftA2 clock getClockTime makeSerial+ where+   clock :: ClockTime -> Serial -> Clock TimeT+   clock refTime serial =+     Clock (currRelTime refTime) serial+++-- TODO: How can i know that actions are carried out monotonically?++-- | Get the current time in seconds, relative to a start 'ClockTime'.+currRelTime :: ClockTime -> IO TimeT+currRelTime (TOD sec0 pico0) = fmap delta getClockTime+ where+   delta (TOD sec pico) =+     fromIntegral (sec-sec0) + 1.0e-12 * fromIntegral (pico-pico0)
+ src/FRP/Reactive/Internal/Fun.hs view
@@ -0,0 +1,18 @@+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Fun+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Constant-optimized representation of functions.+----------------------------------------------------------------------++module FRP.Reactive.Internal.Fun (Fun(..)) where++-- | Constant-optimized functions+data Fun t a = K a                      -- ^ constant function+             | Fun (t -> a)             -- ^ non-constant function
+ src/FRP/Reactive/Internal/Future.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Future+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Representation of future values+----------------------------------------------------------------------++module FRP.Reactive.Internal.Future+  (+    -- * Time & futures+    Time+  , FutureG(..), inFuture, inFuture2+  , runF+  ) where+++import Control.Applicative (Applicative(..))++import Test.QuickCheck++import FRP.Reactive.Internal.Misc (Sink)+import Data.Max+import Data.AddBounds+import Data.PairMonad ()+++-- | Time used in futures.  The parameter @t@ can be any @Ord@ type.  The+-- added bounds represent -Infinity and +Infinity.  Pure values have time+-- minBound (-Infinity), while never-occurring futures have time maxBound+-- (+Infinity).+type Time t = Max (AddBounds t)+++-- | A future value of type @a@ with time type @t@.  Simply a+-- time\/value pair.  Particularly useful with time types that have+-- non-flat structure.+newtype FutureG t a = Future { unFuture :: (Time t, a) }+  deriving (Functor, Applicative, Monad, Show,  Arbitrary)++-- TODO: see if the following definition is really necessary, instead of deriving.++-- -- This instance needs to be lazy; automatic deriving doesn't work.+-- -- Probably the other instances need this too!  TODO (find out).+-- instance Functor (FutureG t) where+--     fmap f (Future ~(t,x)) = Future (t, f x)++--  The 'Applicative' and 'Monad' instances rely on the 'Monoid' instance+-- of 'Max'.+++-- | Apply a unary function within the 'FutureG' representation.+inFuture :: ((Time t, a) -> (Time t', b))+         -> FutureG t a  -> FutureG t' b+inFuture f = Future . f . unFuture++-- | Apply a binary function within the 'FutureG' representation.+inFuture2 :: ((Time t, a) -> (Time t', b) -> (Time t', c))+          ->  FutureG t a -> FutureG t' b -> FutureG t' c+inFuture2 f =  inFuture . f . unFuture+++-- | Run a future in the current thread.  Use the given time sink to sync+-- time, i.e., to wait for an output time before performing the action.+runF :: Ord t => Sink t -> FutureG t (IO a) -> IO a+runF sync (Future (Max t,io)) = tsync t >> io+ where+   tsync MinBound     = putStrLn "runE: skipping MinBound"+   tsync (NoBound t') = sync t'+   tsync MaxBound     = error "runE: infinite wait"+
+ src/FRP/Reactive/Internal/IVar.hs view
@@ -0,0 +1,44 @@+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.IVar+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Write-once variables.+----------------------------------------------------------------------++module FRP.Reactive.Internal.IVar +    ( IVar, newEmptyIVar, readIVar, tryReadIVar, writeIVar )+where++import Control.Concurrent.MVar+import Control.Applicative ((<$>))+import System.IO.Unsafe (unsafePerformIO)++newtype IVar a = IVar (MVar a)++newEmptyIVar :: IO (IVar a)+newEmptyIVar = IVar <$> newEmptyMVar++-- | Returns the value in the IVar.  The *value* will block+-- until the variable becomes filled.+readIVar :: IVar a -> a+readIVar (IVar v) = unsafePerformIO $ readMVar v++-- | Returns Nothing if the IVar has no value yet, otherwise+-- returns the value.+tryReadIVar :: IVar a -> IO (Maybe a)+tryReadIVar (IVar v) = do+    empty <- isEmptyMVar v+    if empty+       then return Nothing+       else Just <$> readMVar v++-- | Puts the value of the IVar.  If it already has a value,+-- block forever.+writeIVar :: IVar a -> a -> IO ()+writeIVar (IVar v) x = putMVar v x
+ src/FRP/Reactive/Internal/Misc.hs view
@@ -0,0 +1,23 @@+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Misc+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Misc Reactive internal defs+----------------------------------------------------------------------++module FRP.Reactive.Internal.Misc+  (+    Action, Sink+  ) where+++-- | Convenient alias for dropping parentheses.+type Action = IO ()++-- | Value consumer+type Sink a = a -> Action
+ src/FRP/Reactive/Internal/Reactive.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS -Wall #-}++----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Reactive+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Representation for 'Reactive' and 'Event' types.  Combined here,+-- because they're mutually recursive.+-- +-- The representation used in this module is based on a close connection+-- between these two types.  A reactive value is defined by an initial+-- value and an event that yields future values; while an event is given+-- as a future reactive value.+----------------------------------------------------------------------++module FRP.Reactive.Internal.Reactive+  (+    EventG(..), inEvent, inEvent2, eFutures+  , ReactiveG(..), inREvent, inFutR+  , runE, runR, forkE, forkR+  ) where++import Data.List (intersperse)++import Control.Concurrent (forkIO,ThreadId)++import FRP.Reactive.Internal.Misc+import FRP.Reactive.Internal.Future+import Data.Max+import Data.AddBounds++-- | Events.  Semantically: time-ordered list of future values.+-- Instances: +-- +-- * 'Monoid': 'mempty' is the event that never occurs, and @e `mappend`+--   e'@ is the event that combines occurrences from @e@ and @e'@.+-- +-- * 'Functor': @fmap f e@ is the event that occurs whenever @e@ occurs,+--   and whose occurrence values come from applying @f@ to the values from+--   @e@.+-- +-- * 'Applicative': @pure a@ is an event with a single occurrence at time+--   -Infinity.  @ef \<*\> ex@ is an event whose occurrences are made from+--   the /product/ of the occurrences of @ef@ and @ex@.  For every occurrence+--   @f@ at time @tf@ of @ef@ and occurrence @x@ at time @tx@ of @ex@, @ef+--   \<*\> ex@ has an occurrence @f x@ at time @tf `max` tx@.  N.B.: I+--   don't expect this instance to be very useful.  If @ef@ has @nf@+--   instances and @ex@ has @nx@ instances, then @ef \<*\> ex@ has @nf*nx@+--   instances.  However, there are only @nf+nx@ possibilities for @tf+--   `max` tx@, so many of the occurrences are simultaneous.  If you think+--   you want to use this instance, consider using 'Reactive' instead.+-- +-- * 'Monad': @return a@ is the same as @pure a@ (as usual).  In @e >>= f@,+--   each occurrence of @e@ leads, through @f@, to a new event.  Similarly+--   for @join ee@, which is somehow simpler for me to think about.  The+--   occurrences of @e >>= f@ (or @join ee@) correspond to the union of the+--   occurrences (temporal interleaving) of all such events.  For example,+--   suppose we're playing Asteroids and tracking collisions.  Each collision+--   can break an asteroid into more of them, each of which has to be tracked+--   for more collisions.  Another example: A chat room has an /enter/ event,+--   whose occurrences contain new events like /speak/.  An especially useful+--   monad-based function is 'joinMaybes', which filters a Maybe-valued+--   event.++newtype EventG t a = Event { eFuture :: FutureG t (ReactiveG t a) }++-- The event representation requires temporal monotonicity but does not+-- enforce it, which invites bugs.  Every operation therefore must be+-- tested for preserving monotonicity.  (Better yet, find an efficient+-- representation that either enforces or doesn't require monotonicity.)++-- Why the newtype for 'EventG?'  Because the 'Monoid' instance of 'Future'+-- does not do what I want for 'EventG'.  It will pick just the+-- earlier-occurring event, while I want an interleaving of occurrences+-- from each.  Similarly for other classes.+++-- TODO: Alternative and MonadPlus instances for EventG++-- | Reactive value: a discretely changing value.  Reactive values can be+-- understood in terms of (a) a simple denotational semantics of reactive+-- values as functions of time, and (b) the corresponding instances for+-- functions.  The semantics is given by the function @at :: ReactiveG t a ->+-- (t -> a)@.  A reactive value may also be thought of (and in this module+-- is implemented as) a current value and an event (stream of future values).+-- +-- The semantics of 'ReactiveG' instances are given by corresponding+-- instances for the semantic model (functions):+-- +-- * 'Functor': @at (fmap f r) == fmap f (at r)@, i.e., @fmap f r `at`+--   t == f (r `at` t)@.+-- +-- * 'Applicative': @at (pure a) == pure a@, and @at (s \<*\> r) == at s+--   \<*\> at t@.  That is, @pure a `at` t == a@, and @(s \<*\> r) `at` t+--   == (s `at` t) (r `at` t)@.+-- +-- * 'Monad': @at (return a) == return a@, and @at (join rr) == join (at+--   . at rr)@.  That is, @return a `at` t == a@, and @join rr `at` t ==+--   (rr `at` t) `at` t@.  As always, @(r >>= f) == join (fmap f r)@.+--   @at (r >>= f) == at r >>= at . f@.+-- +-- * 'Monoid': a typical lifted monoid.  If @o@ is a monoid, then+--   @Reactive o@ is a monoid, with @mempty == pure mempty@, and @mappend+--   == liftA2 mappend@.  That is, @mempty `at` t == mempty@, and @(r+--   `mappend` s) `at` t == (r `at` t) `mappend` (s `at` t).@++data ReactiveG t a = a `Stepper` EventG t a+++{--------------------------------------------------------------------+    Applying functions inside of representations+--------------------------------------------------------------------}++-- | Apply a unary function inside an 'EventG' representation.+inEvent :: (FutureG s (ReactiveG s a) -> FutureG t (ReactiveG t b))+        -> (EventG s a -> EventG t b)+inEvent f = Event . f . eFuture++-- | Apply a unary function inside an 'EventG' representation.+inEvent2 :: (FutureG t (ReactiveG t a) -> FutureG t (ReactiveG t b)+                                       -> FutureG t (ReactiveG t c))+         -> (EventG t a -> EventG t b -> EventG t c)+inEvent2 f = inEvent . f . eFuture++-- | Apply a unary function inside the 'rEvent' part of a 'Reactive'+-- representation.+inREvent :: (EventG    s a -> EventG    t a)+         -> (ReactiveG s a -> ReactiveG t a)+inREvent f (a `Stepper` e) = a `Stepper` f e++-- | Apply a unary function inside the future reactive inside a 'Reactive'+-- representation.+inFutR :: (FutureG s (ReactiveG s b) -> FutureG t (ReactiveG t b))+       -> (ReactiveG s b -> ReactiveG t b)+inFutR = inREvent . inEvent+++{--------------------------------------------------------------------+    Showing values (exposing rep)+--------------------------------------------------------------------}++-- | Make the event into a list of futures+eFutures :: EventG t a -> [FutureG t a]+eFutures (Event (Future (Max MaxBound,_)))  = []+eFutures (Event (Future (t,a `Stepper` e))) = Future (t,a) : eFutures e++-- TODO: redefine 'eFutures' as an unfold+++-- Show a future+sFuture :: (Show t, Show a) => FutureG t a -> String+sFuture (Future (Max MinBound,a)) = "(-infty," ++ show a ++ ")"+sFuture (Future (Max MaxBound,_)) = "(infty,_)"+sFuture (Future (Max (NoBound t),a)) = "(" ++ show t ++ "," ++ show a ++ ")"++-- TODO: Better re-use in sFuture.++-- Truncated show+sFutures :: (Show t, Show a) => [FutureG t a] -> String+sFutures fs =+  let maxleng = 20+      a   = (intersperse "->" . map sFuture) fs+      inf = length (take maxleng a) == maxleng+  in+    if not inf then concat a+               else concat (take maxleng a) ++ "..."++-- TODO: clean up sFutures def: use intercalate, concat before trimming,+-- and define&use a general function for truncating and adding "...".+-- Test.++instance (Show a, Show b) => Show (EventG a b) where+  show = sFutures . eFutures++instance (Show x, Show y) => Show (ReactiveG x y) where+  show (x `Stepper` e) = show x ++ " `Stepper` " ++ show e+++{--------------------------------------------------------------------+    Execution+--------------------------------------------------------------------}++-- | Run an event in the current thread.  Use the given time sink to sync+-- time, i.e., to wait for an output time before performing the action.+runE :: forall t. Ord t => Sink t -> Sink (EventG t Action)+runE sync (Event (Future (Max bt,r))) = tsync bt (runR sync r)+ where+   tsync :: AddBounds t -> Sink Action+   tsync MinBound    = id                               -- no wait+   tsync (NoBound t) = (sync t >>)                      -- wait+   tsync MaxBound    = const (return ())                -- finished!++-- TODO: I'm not sure about the MaxBound case.  We could instead just wait+-- forever (cheaply).  Try out this terminating definition instead.++-- | Run an event in a new thread, using the given time sink to sync time.+forkE :: Ord t => Sink t -> EventG t Action -> IO ThreadId+forkE = (fmap.fmap) forkIO runE++-- TODO: Revisit this tsync definition.  For instance, maybe the MaxBound+-- case ought to simply return.++-- | Run a reactive value in the current thread, using the given time sink+-- to sync time.+runR :: Ord t => Sink t -> Sink (ReactiveG t Action)+runR sync (act `Stepper` e) = act >> runE sync e+                      +-- | Run a reactive value in a new thread, using the given time sink to+-- sync time.  The initial action happens in the current thread.+forkR :: Ord t => Sink t -> ReactiveG t Action -> IO ThreadId+forkR = (fmap.fmap) forkIO runR
+ src/FRP/Reactive/Internal/Serial.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE Rank2Types, ImpredicativeTypes #-}+-- We need ImpredicativeTypes, but GHC 6.8 doesn't think it+-- has them.  The cabal file configures this in a compiler-dependent+-- way.+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Serial+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Serialize actions.+----------------------------------------------------------------------++module FRP.Reactive.Internal.Serial+  ( Serial, makeSerial, locking+  ) where++import Control.Concurrent.MVar+import Control.Applicative((<$>))+import Control.Exception (bracket_)++-- | Serializer.  Turns actions into equivalent but serialized actions+type Serial = forall a. IO a -> IO a++-- | Make a locking serializer+makeSerial :: IO Serial+makeSerial = locking <$> newEmptyMVar++-- | Make a locking serializer with a given lock+locking :: MVar () -> Serial+locking lock = bracket_ (putMVar lock ()) (takeMVar lock)
+ src/FRP/Reactive/Internal/TVal.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.TVal+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Timed values.  A primitive interface for futures.+----------------------------------------------------------------------++module FRP.Reactive.Internal.TVal+  (+    makeFuture, makeEvent, Fed, MkFed+  ) where+++import Control.Arrow (first)+import Control.Applicative ((<$>))+import Control.Monad (forever)+import Control.Concurrent (forkIO,yield)+import Control.Concurrent.Chan+import System.IO.Unsafe (unsafePerformIO)++import Data.Unamb (unamb,assuming)++import FRP.Reactive.Improving (Improving(..))+import FRP.Reactive.Future (FutureG,future)+import FRP.Reactive.Reactive (Event,TimeT,ITime)+import FRP.Reactive.PrimReactive (futuresE)++import FRP.Reactive.Internal.Misc (Sink)+import FRP.Reactive.Internal.Clock+import FRP.Reactive.Internal.Timing (sleepPast)+import FRP.Reactive.Internal.IVar++-- | A value that becomes defined at some time.  'timeVal' may block if+-- forced before the time & value are knowable.  'definedAt' says whether+-- the value is defined at (and after) a given time and likely blocks+-- until the earlier of the query time and the value's actual time.+data TVal t a = TVal { timeVal :: (t,a), definedAt :: t -> Bool }++-- | Make a 'TVal' and a sink to write to it (at most once).+makeTVal :: Clock TimeT -> IO (TVal TimeT a, Sink a)+makeTVal (Clock getT serial) = f <$> newEmptyIVar+ where+   f v = ( TVal (readIVar v)+                (\ t -> unsafePerformIO $ do +                            sleepPast getT t+                            do value <- tryReadIVar v+                               return $ case value of+                                           -- We're past t, so if it's not +                                           -- defined now, it wasn't at t.+                                           Nothing -> False+                                           -- If it became defined before+                                           -- t, then it's defined now.+                                           Just (t',_) -> t' < t)+         , \ a -> serial (getT >>= \ t -> writeIVar v (t,a))+         )++-- TODO: oops - the definedAt in makeTVal always waits until the given+-- time.  It could also grab the time and compare with t.  Currently that+-- comparison is done in tValImp.  How can we avoid the redundant test?  We+-- don't really have to avoid it, since makeTVal isn't exported.++-- | 'TVal' as 'Future'+tValFuture :: Ord t => TVal t a -> FutureG (Improving t) a+tValFuture v = future (tValImp v) (snd (timeVal v))++-- | 'TVal' as 'Improving'+tValImp :: Ord t => TVal t a -> Improving t+tValImp v = Imp ta (\ t -> assuming (not (definedAt v t)) GT+                            `unamb` (ta `compare` t))+ where+   ta = fst (timeVal v)++-- | An @a@ that's fed by a @b@+type Fed a b = (a, Sink b)++-- | Make a 'Fed'.+type MkFed a b = IO (Fed a b)++-- | Make a connected sink/future pair.  The sink may only be written to once.+makeFuture :: Clock TimeT -> MkFed (FutureG ITime a) a+makeFuture = (fmap.fmap.first) tValFuture makeTVal++-- | Make a new event and a sink that writes to it.  Uses the given+-- clock to serialize and time-stamp.+makeEvent :: Clock TimeT -> MkFed (Event a) a+makeEvent clock = (fmap.first) futuresE (listSink (makeFuture clock))++listSink :: MkFed a b -> MkFed [a] b+listSink mk = do chanA <- newChan+                 chanB <- newChan+                 forkIO . forever $ do+                     (a,snk) <- mk+                     writeChan chanB a+                     readChan  chanA >>= snk+                 as <- getChanContents chanB+                 return (as, writeChanY chanA)+ where+   -- Yield control after each input write.  Helps responsiveness+   -- tremendously.+   writeChanY ch x = writeChan ch x >> yield+   -- writeChanY = (fmap.fmap) (>> yield) writeChan
+ src/FRP/Reactive/Internal/Timing.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE BangPatterns #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Internal.Timing+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- +----------------------------------------------------------------------++module FRP.Reactive.Internal.Timing (mkUpdater,sleepPast) where++import Data.Monoid (mempty)+import Control.Applicative ((<$>))+import Control.Monad (unless)+import Data.IORef+import Control.Concurrent (threadDelay)+import Control.Concurrent.SampleVar++-- For IO monoid+import Control.Instances ()++import FRP.Reactive.Reactive (TimeT)+import FRP.Reactive.Improving (Improving,exact)+import FRP.Reactive.Behavior (Behavior)++import FRP.Reactive.Internal.Misc (Action,Sink)+import FRP.Reactive.Internal.Reactive (forkR)+import FRP.Reactive.Internal.Behavior (unb)+import FRP.Reactive.Internal.Fun++-- | If a sample variable is full, act on the contents, leaving it empty.+drainS :: SampleVar a -> Sink (Sink a)+drainS sv snk = do emptySVar <- isEmptySampleVar sv+                   unless emptySVar (readSampleVar sv >>= snk)++-- TODO: Generalize from TimeT below, using BehaviorG.++noSink :: Sink t+noSink = mempty -- const (putStrLn "noSink")++-- | Make an action to be executed regularly, given a time-source and a+-- action-behavior.  The generated action is optimized to do almost no+-- work during known-constant phases of the given behavior.+mkUpdater :: IO TimeT -> Behavior Action -> IO Action+mkUpdater getT acts =+  -- The plan: Stash new phases (time functions) in a sample variable as+  -- they arise.  Every minPeriod, check the sample var for a new value.+  do actSVar <- newEmptySampleVar+     _       <- forkR (sleepPast getT . exact)+                      (writeSampleVar' actSVar <$> unb acts) +     tfunRef <- newIORef (noSink :: Sink TimeT)+     return $+       do -- When there's a new time fun, execute it once if+          -- constant, or remember for repeated execution if+          -- non-constant.+          now <- getT+          -- putStrLn ("scheduler: time == " ++ show now)+          drainS actSVar $ \ actF ->+            case actF of +              K   c -> do -- putStrLn "K"+                          writeIORef tfunRef noSink >> c+              Fun f -> do -- putStrLn "Fun"+                          writeIORef tfunRef f+          readIORef tfunRef >>= ($ now)+          -- yield  -- experiment+ where+   writeSampleVar' v x = do -- putStrLn "writeSampleVar"+                            writeSampleVar v x++-- | Pause a thread for the given duration in seconds+sleep :: Sink TimeT+sleep = threadDelay . ceiling . (1.0e6 *)++-- | Sleep past a given time+sleepPast :: IO TimeT -> Sink TimeT+sleepPast getT !target = loop+ where+   -- Snooze until strictly after the target.+   loop = do -- The strict evaluation of target is essential here.+             -- (See bang pattern.)  Otherwise, the next line will grab a+             -- time before a possibly long block, and then sleep much+             -- longer than necessary.+             now <- getT+             -- putStrLn $ "sleep loop: now == " ++ show now+             --            ++ ", target == " ++ show target+             unless (now > target) $+                sleep (target-now) -- >> loop
+ src/FRP/Reactive/LegacyAdapters.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE Rank2Types #-}+{-# OPTIONS_GHC -Wall #-}+module FRP.Reactive.LegacyAdapters +    ( BehaviorMachine(..)+    , makeBehaviorMachine+    , makeEvent, Sink+    -- * Deprecated and/or for testing.+    , forkE+    , forkB+    ) where++import Control.Compose ((:.)(O))+import Control.Concurrent(ThreadId)++import System.Time++import FRP.Reactive.Improving+import FRP.Reactive.Future+import FRP.Reactive.Fun+import FRP.Reactive.Reactive+import Data.Max+import Data.AddBounds++import qualified FRP.Reactive.Internal.Reactive as R+import FRP.Reactive.Internal.Misc (Sink)+import FRP.Reactive.Behavior (Behavior)+import FRP.Reactive.Internal.Behavior (BehaviorG(..))++import qualified FRP.Reactive.Internal.TVal as TVal+import FRP.Reactive.Internal.Clock+++data BehaviorMachine a+    = BehaviorMachine { currentValue :: a+                      , currentTime :: TimeT+                      , waitChange :: IO (BehaviorMachine a)+                      }++makeBehaviorMachine :: ClockTime -> Behavior a -> IO (BehaviorMachine a)+makeBehaviorMachine refTime (Beh (O (R.Stepper initval ev))) = do+    clock <- makeClock refTime+    curTime <- currRelTime refTime+    return $ eventBehaviorMachine refTime clock initval curTime ev++eventBehaviorMachine :: ClockTime -> Clock TimeT +                     -> Fun TimeT a -> TimeT -> Event (Fun TimeT a) -> BehaviorMachine a+eventBehaviorMachine refTime clock = go +    where+    go initVal initTime event = +        BehaviorMachine { currentValue = apply initVal initTime+                        , currentTime = initTime+                        , waitChange = do+                            let fut = eventOcc event+                            schedule clock (fromTime (futTime fut))+                            let (v,nexte) = futVal fut+                            curTime <- currRelTime refTime+                            return $ go v curTime nexte+                        }++fromTime :: Num a => Max (AddBounds (Improving a)) -> a+fromTime (Max MinBound) = 0+fromTime (Max (NoBound t)) = exact t+fromTime (Max MaxBound) = error "maxbound"+++makeEvent :: ClockTime -> IO (Event a, Sink a)+makeEvent refTime = TVal.makeEvent =<< makeClock refTime++-- | Forks a behavior given a reference time and a time function sinker. This+-- function is deprecated, but will remain until something better, and working,+-- comes along.+forkB :: ClockTime -> -- The reference time+         Behavior a -> -- The behavior+         Sink (Fun TimeT a) -> -- An action that takes in a Fun of time to a+         IO ThreadId+forkB refTime (Beh (O r)) fSync = do+  clock <- makeClock refTime+  R.forkR (schedule clock . exact) (fmap fSync r)++-- | A version of forkE that acts more like makeEvent and uses Clock as a+-- basis.  Takes reference time and the event to fork.+forkE :: ClockTime    ->+         Event (IO a) ->+         IO ThreadId+forkE refTime e = do+  clock <- makeClock refTime+  R.forkE (schedule clock . exact) e
+ src/FRP/Reactive/Num.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}++module FRP.Reactive.Num () where++import FRP.Reactive.Behavior+import Control.Applicative++noOv :: String -> String -> a+noOv ty meth = error $ meth ++ ": No overloading for " ++ ty++noFun :: String -> a+noFun = noOv "function"++-- Eq & Show are prerequisites for Num, so they need to be faked here+instance Eq (Behavior b) where+  (==) = noFun "(==)"+  (/=) = noFun "(/=)"++instance Ord b => Ord (Behavior b) where+  min = liftA2 min+  max = liftA2 max++instance Show (Behavior b) where+  show      = noFun "show"+  showsPrec = noFun "showsPrec"+  showList  = noFun "showList"++instance Num b => Num (Behavior b) where+  negate      = fmap negate+  (+)         = liftA2 (+)+  (*)         = liftA2 (*)+  fromInteger = pure . fromInteger+  abs         = fmap abs+  signum      = fmap signum++instance Fractional b => Fractional (Behavior b) where+  recip        = fmap recip+  fromRational = pure . fromRational++instance Floating b => Floating (Behavior b) where+  pi    = pure pi+  sqrt  = fmap sqrt+  exp   = fmap exp+  log   = fmap log+  sin   = fmap sin+  cos   = fmap cos+  asin  = fmap asin+  atan  = fmap atan+  acos  = fmap acos+  sinh  = fmap sinh+  cosh  = fmap cosh+  asinh = fmap asinh+  atanh = fmap atanh+  acosh = fmap acosh+
+ src/FRP/Reactive/PrimReactive.hs view
@@ -0,0 +1,759 @@+{-# LANGUAGE TypeOperators, ScopedTypeVariables+           , FlexibleInstances, MultiParamTypeClasses+           , GeneralizedNewtypeDeriving+ #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}++-- For ghc-6.6 compatibility+-- {-# OPTIONS_GHC -fglasgow-exts -Wall #-}++----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.PrimReactive+-- Copyright   :  (c) Conal Elliott 2007+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Functional /events/ and /reactive values/.  Semantically, an 'Event' is+-- stream of future values in time order.  A 'Reactive' value is a+-- discretly time-varying value.+-- +-- Many of the operations on events and reactive values are packaged as+-- instances of the standard type classes 'Monoid', 'Functor',+-- 'Applicative', and 'Monad'.+-- +-- This module focuses on representation and primitives defined in terms+-- of the representation.  See also "FRP.Reactive.Reactive", which+-- re-exports this module, plus extras that do not exploit the+-- representation.  My intention for this separation is to ease+-- experimentation with alternative representations.+-- +-- Although the basic 'Reactive' type describes /discretely/-changing+-- values, /continuously/-changing values can be modeled simply as+-- reactive functions.  See "FRP.Reactive.Behavior" for a convenient type+-- composition of 'Reactive' and a constant-optimized representation of+-- functions of time.  The exact packaging of discrete vs continuous will+-- probably change with more experience.+----------------------------------------------------------------------++module FRP.Reactive.PrimReactive+  ( -- * Events and reactive values+    EventG, ReactiveG+    -- * Operations on events and reactive values+  , stepper, switcher, withTimeGE, withTimeGR+  , futuresE, listEG, atTimesG, atTimeG+  , snapshotWith, accumE, accumR, once+  , firstRestE, firstE, restE+  , remainderR, withRestE, untilE+  -- , traceE, traceR+  -- , mkEvent, mkEventTrace, mkEventShow+  , eventOcc+    -- * To be moved elsewhere+  , joinMaybes, filterMP+  -- * To be removed when it gets used somewhere+  , isMonotoneR+  -- * Testing+  , batch, infE+  ) where++import Data.Monoid+import Control.Applicative+import Control.Monad+import Data.Function (on)++-- TODO: eliminate the needs for this stuff.+import Control.Concurrent (threadDelay)+import Control.Exception (evaluate)+import System.IO.Unsafe++import Test.QuickCheck hiding (evaluate)+import Test.QuickCheck.Instances+import Test.QuickCheck.Checkers+import Test.QuickCheck.Classes+-- import Data.List++-- TypeCompose+import Control.Compose ((:.)(..), inO2, Monoid_f(..))+import Data.Pair+import Control.Instances () -- Monoid (IO ())++import Data.Unamb (race)++import Data.Max+import Data.AddBounds+import FRP.Reactive.Future hiding (batch)+import FRP.Reactive.Internal.Reactive++{--------------------------------------------------------------------+    Events and reactive values+--------------------------------------------------------------------}++-- Bogus EqProp instance.  TODO: replace with a random equality test, such+-- that the collection of all generated tests covers equality.++instance (Eq a, Eq b, EqProp a, EqProp b) => EqProp (EventG a b) where+  a =-= b = foldr (.&.) (property True) $ zipWith (=-=) (f a) (f b)+    where+      f = take 20 . eFutures++arbitraryE :: (Num t, Ord t, Arbitrary t, Arbitrary u) => Gen (EventG t u)+arbitraryE = frequency +  [ (1, liftA2 ((liftA. liftA) futuresE addStart) arbitrary futureList)+  , (4, liftA futuresE futureList)+  ]+  where+    earliestFuture = Future . (,) (Max MinBound)+    addStart = (:).earliestFuture+    futureList = frequency [(10, futureListFinite), (1,futureListInf)]+    futureListFinite = liftA2 (zipWith future) nondecreasing arbitrary+    futureListInf =+      liftA2 (zipWith future) (resize 10 nondecreasingInf)+                              (infiniteList arbitrary)++instance (Arbitrary t, Ord t, Num t, Arbitrary a) => Arbitrary (EventG t a) where+  arbitrary   = arbitraryE+  -- TODO: Fix this coarbitrary instance  -- David+  coarbitrary = error "coarbitrary Events not supported"+  -- coarbitrary = coarbitrary . eFuture++----++-- Arbitrary works just like pairs:+instance (Arbitrary t, Arbitrary a, Num t, Ord t) => Arbitrary (ReactiveG t a) where+  arbitrary = liftA2 Stepper arbitrary arbitrary+  coarbitrary (a `Stepper` e) = coarbitrary e . coarbitrary a++instance Ord t => Model (ReactiveG t a) (t -> a) where+  model = rat++instance (Ord t, Arbitrary t, Show t, EqProp a) => EqProp (ReactiveG t a)+ where+   (=-=) = (=-=) `on` model++-- Initial value of a 'Reactive'+rInit :: ReactiveG t a -> a+rInit (a `Stepper` _) = a+++{--------------------------------------------------------------------+    Instances+--------------------------------------------------------------------}++instance Ord t => Monoid (EventG t a) where+  mempty  = Event mempty+  mappend = inEvent2 merge++-- Standard instance for Applicative of Monoid+instance (Ord t, Monoid a) => Monoid (ReactiveG t a) where+  mempty  = pure mempty+  mappend = liftA2 mappend++-- | Merge two 'Future' streams into one.+merge :: Ord t => Binop (FutureG t (ReactiveG t a))+-- The following two lines seem to be too strict and are causing+-- reactive to lock up.  I.e. the time argument of one of these+-- must have been _|_, so when we pattern match against it, we +-- block.+Future (Max MaxBound,_) `merge` v = v+u `merge` Future (Max MaxBound,_) = u+u `merge` v = +  (inFutR (`merge` v) <$> u) `mappend` (inFutR (u `merge`) <$> v)++-- What's going on in this 'merge' definition?  Try two different+-- future paths.  If u arrives before v (or simultaneously), then+-- begin as u begins and then merge v with the rest of u.  Otherwise,+-- begin as v begins and then merge u with the rest of v.  Because of+-- the left-bias, make sure u fragments are always the first argument+-- to merge and v fragments are always the second.+++-- Define functor instances in terms of each other.+instance Functor (EventG t) where+  fmap f = inEvent $ (fmap.fmap) f++instance Functor (ReactiveG t) where+  fmap f (a `Stepper` e) = f a `stepper` fmap f e++-- standard instance+instance Ord t => Applicative (EventG t) where+  pure = return+  _ <*> (Event (Future (Max MaxBound,_))) = mempty+  x <*> y = x `ap` y++-- standard instance+instance Ord t => Alternative (EventG t) where+  { empty = mempty; (<|>) = mappend }++instance Ord t => Pair (ReactiveG t) where+  -- pair :: ReactiveG t a -> ReactiveG t b -> ReactiveG t (a,b)+  (c `Stepper` ce) `pair` (d `Stepper` de) =+    (c,d) `accumR` pairEdit (ce,de)++instance Ord t => Applicative (ReactiveG t) where+  pure a = a `stepper` mempty+  -- Standard definition.  See 'Pair'.+  rf <*> rx = uncurry ($) <$> (rf `pair` rx)++-- A wonderful thing about the <*> definition for ReactiveG is that it+-- automatically caches the previous value of the function or argument+-- when the argument or function changes.+++instance Ord t => Monad (EventG t) where+  return a = Event (pure (pure a))+  e >>= f  = joinE (fmap f e)++-- happy a t b. Same as (a `mappend` b) except takes advantage of knowledge+-- that t is a lower bound for the occurences of b. This allows for extra+-- laziness.+happy :: (Ord t) => EventG t a ->+                    Time t ->+                    EventG t a ->+                    EventG t a+happy a (Max MaxBound) _ = a+happy (Event (Future (Max MaxBound, _))) _ b = b+happy a@(Event (Future (t0, e `Stepper` ee'))) t b +  | t0 <= t = (Event (Future (t0, e `Stepper` (happy ee' t b))))+  | otherwise = a `mappend` b+++-- Note, joinE should not be called with an infinite list of events that all+-- occur at the same time.  It can't decide which occurs first.+joinE :: (Ord t) => EventG t (EventG t a) -> EventG t a+joinE (Event (Future (Max MaxBound, _))) = mempty+joinE (Event (Future (t0h, e `Stepper` ((Event (Future (Max MaxBound, _)))))))+  = adjustE t0h e+joinE (Event (Future (t0h, e `Stepper` ee'@((Event (Future (t1h, _)))))))+  = happy (adjustE t0h e) t1h (adjustTopE t0h (joinE ee'))++-- Original Version:+-- joinE (Event (Future (t0h, e `Stepper` ee'))) =+--   adjustE t0h e `mappend` adjustTopE t0h (joinE ee')++adjustTopE :: Ord t => Time t -> EventG t t1 -> EventG t t1+adjustTopE t0h (Event (Future (tah, r))) =+  Event (Future (t0h `max` tah,r))++adjustE :: Ord t => Time t -> EventG t t1 -> EventG t t1+adjustE _ e@(Event (Future (Max MaxBound, _))) = e++adjustE t0h (Event (Future (tah, a `Stepper` e))) =+  Event (Future (t1h,a `Stepper` adjustE t1h e))+  where+    t1h = t0h `max` tah++-- TODO: add adjustE explanation.  What's going on and why t1 in the+-- recursive call?  David's comment:+-- If we have an event [t1, t2] we know t2 >= t1 so (max t t2) == (max (max t t1) t2).+-- See http://hpaste.org/11518 for a def that doesn't change the lower bound.+-- +-- What I remember is that this function is quite subtle w.r.t laziness.+-- There are some notes in the paper.  If i find instead that a simpler+-- definition is possible, so much the better.++-- Here's an alternative to joinE that is less strict, and doesn't cause+-- reactive to lock up.  Need to verify correctness.  (Does lock up with+-- the mappend optimization that eliminates a space/time leak.)+{-+joinE :: Ord t => EventG t (EventG t a) -> EventG t a+joinE (Event (Future (t0h, ~(e `Stepper` ee')))) =+   adjustE t0h (e `mappend` joinE ee')++adjustE t0h (Event (Future (tah, ~(a `Stepper` e)))) =+  Event (Future (t1h,a `Stepper` adjustE t1h e))+  where+    t1h = t0h `max` tah+-}++instance Ord t => MonadPlus (EventG t) where { mzero = mempty; mplus = mappend }++-- Standard instance for Applicative w/ join+instance Ord t => Monad (ReactiveG t) where+  return  = pure+  r >>= f = joinR (f <$> r)+++{--------------------------------------------------------------------+    Operations on events and reactive values+--------------------------------------------------------------------}++-- | Reactive value from an initial value and a new-value event.+stepper :: a -> EventG t a -> ReactiveG t a+stepper = Stepper++-- -- | Turn a reactive value into an event, with the initial value+-- -- occurring at -Infinity.+-- --+-- -- Oops: breaks the semantic abstraction of 'Reactive' as a step+-- function.+-- rToE :: Ord t => ReactiveG t a -> EventG t a+-- rToE (a `Stepper` e) = pure a `mappend` e++-- | Switch between reactive values.+switcher :: Ord t => ReactiveG t a -> EventG t (ReactiveG t a) -> ReactiveG t a+r `switcher` e = join (r `stepper` e)++-- | Reactive 'join' (equivalent to 'join' but slightly more efficient, I think)+joinR :: Ord t => ReactiveG t (ReactiveG t a) -> ReactiveG t a++joinR ((a `Stepper` Event ur) `Stepper` e'@(Event urr)) = a `stepper` Event u+ where+   u = ((`switcher` e') <$> ur) `mappend` (join <$> urr)++-- The following simpler definition is wrong.  It keeps listening to @e@+-- even after @er@ has occurred.+-- joinR ((a `Stepper` e) `Stepper` er) = +--   a `stepper` (e `mappend` join (rToE <$> er))++-- e  :: EventG t a+-- er :: EventG t (ReactiveG t a)+-- +-- rToE <$> er ::: EventG t (EventG t a)+-- join (rToE <$> er) ::: EventG t a++-- | Access occurrence times in an event.  See also 'withTimeGR'.+withTimeGE :: EventG t a -> EventG t (a, Time t)+withTimeGE = inEvent $ inFuture $ \ (t,r) -> (t, withTimeGR t r)++-- | Access occurrence times in a reactive value.  See also 'withTimeGE'.+withTimeGR :: Time t -> ReactiveG t a -> ReactiveG t (a, Time t)+withTimeGR t (a `Stepper` e) = (a,t) `Stepper` withTimeGE e++-- | Convert a temporally monotonic list of futures to an event.  See also+-- the specialization 'listE'+listEG :: Ord t => [(t,a)] -> EventG t a+listEG = futuresE . map (uncurry future)++-- | Convert a temporally monotonic list of futures to an event+futuresE :: Ord t => [FutureG t a] -> EventG t a+futuresE [] = mempty+futuresE (Future (t,a) : futs) =+  -- trace ("l2E: "++show t) $+  Event (Future (t, a `stepper` futuresE futs))++-- TODO: redefine 'futuresE' as a fold+-- futuresE = foldr (\ fut e -> Event ((`stepper` e) <$> fut)) mempty++-- TODO: hide futuresE.  currently exported for use in TVal.  If I move to+-- Internal/Reactive, I have to move the monoid instance there, which+-- requires moving others as well.++-- | Event at given times.  See also 'atTimeG'.+atTimesG :: Ord t => [t] -> EventG t ()+atTimesG = listEG . fmap (flip (,) ())++-- | Single-occurrence event at given time.+atTimeG :: Ord t => t -> EventG t ()+atTimeG t = futuresE (pure (future t ()))++-- This variant of 'snapshot' has 'Nothing's where @b@ changed and @a@+-- didn't.+snap :: forall a b t. Ord t =>+        EventG t a -> ReactiveG t b -> EventG t (Maybe a, b)+ea `snap` (b0 `Stepper` eb) =+  (Nothing, b0) `accumE` (fmap fa ea `mappend` fmap fb eb)+ where+   fa :: a -> Unop (Maybe a, b)+   fb :: b -> Unop (Maybe a, b)+   fa a (_,b) = (Just a , b)+   fb b _     = (Nothing, b)++-- | Snapshot a reactive value whenever an event occurs and apply a+-- combining function to the event and reactive's values.+snapshotWith :: Ord t => (a -> b -> c) -> EventG t a -> ReactiveG t b -> EventG t c+snapshotWith f e r = joinMaybes $ fmap h (e `snap` r)+ where+   h (Nothing,_) = Nothing+   h (Just a ,b) = Just (f a b)++-- | Accumulating event, starting from an initial value and a+-- update-function event.  See also 'accumR'.+-- Example: (using a list rempresentation for events, for clarity+--   @10 `accumE`+--      [(5 seconds, (+2)),(10 seconds, (subtract 30)),(20 seconds,(*10))]+--     = [(5 seconds, 12),(10 seconds, -18),(20 seconds, -180)]@+-- If you want an initial occurance at @-infinity@ you can use @pure a+-- `mappend` accumE a e@+accumE :: a -> EventG t (a -> a) -> EventG t a+accumE a = inEvent $ fmap $ \ (f `Stepper` e') -> f a `accumR` e'++-- | Reactive value from an initial value and an updater event.  See also+-- 'accumE'.+accumR :: a -> EventG t (a -> a) -> ReactiveG t a+a `accumR` e = a `stepper` (a `accumE` e)++-- | Just the first occurrence of an event.+once :: Ord t => EventG t a -> EventG t a+once = inEvent $ fmap $ pure . rInit++-- | Decompose an event into its first occurrence value and a remainder+-- event.  See also 'firstE' and 'restE'.+firstRestE :: Ord t => EventG t a -> (a, EventG t a)+firstRestE (Event fut) = f (futVal fut)+  where+    f (a `Stepper` b) = (a,b)++-- | Extract the first occurrence value of an event.  See also+-- 'firstRestE' and 'restE'.+firstE :: Ord t => EventG t a -> a+firstE = fst . firstRestE++-- | Extract the remainder an event, after its first occurrence.  See also+-- 'firstRestE' and 'firstE'.+restE :: Ord t => EventG t a -> EventG t a+restE = snd . firstRestE+++-- | Remaining part of an event.  See also 'withRestE'.+remainderR :: Ord t => EventG t a -> ReactiveG t (EventG t a)+remainderR e = e `stepper` (snd <$> withRestE e)++-- -- | Event remainders.  Replace event values with a reactive that starts+-- -- with that value and follows the event.  Sort of like 'tails'.+-- eventR :: Ord t => EventG t a -> EventG t (ReactiveG t a)+-- eventR = inEvent $ fmap $ \ r@(_ `Stepper` e') -> r `Stepper` eventR e'++-- Also try the following definition of remainderR++-- remainderR :: forall t a. Ord t => EventG t a -> ReactiveG t (EventG t a)+-- remainderR e = e `accumR` (next <$ e)+--  where+--    next :: Unop (EventG t a)+--    next ~(Event (Future (_, _ `Stepper` e'))) = e'++-- newtype EventG t a = Event { eFuture :: FutureG t (ReactiveG t a) }++-- | Access the remainder with each event occurrence.+withRestE :: EventG t a -> EventG t (a, EventG t a)+withRestE = inEvent $ fmap $+	    \ (a `Stepper` e') -> (a,e') `stepper` withRestE e'+++-- | Truncate first event at first occurrence of second event.+untilE :: Ord t => EventG t a -> EventG t b -> EventG t a+ea `untilE` Event (Future ~(tb,_)) = ea `untilET` tb++-- | Truncate first event at the given time.+untilET :: Ord t => EventG t a -> Time t -> EventG t a++-- Event (Future (ta, ~(a `Stepper` e'))) `untilET` t = +--   if ta < t then+--     Event (Future (ta, a `Stepper` (e' `untilET` t)))+--   else+--     mempty++-- Hm.  I doubt that the definition above gives sufficient temporal+-- laziness.  No information can come out of the result until the value of+-- @ta < t@ is determined, which is usually at about time @ta `min` t@.++-- So, try the following definition instead.  It immediately provides+-- lower bounds of both @ta@ and @t@ as lower bounds of the constructed+-- event occurrences.++Event (Future ~(ta, a `Stepper` e')) `untilET` t = +  Event (Future (ta', a `Stepper` (e' `untilET` t)))+ where+   ta' = (ta `min` t) `max` (if ta < t then ta else maxBound)++-- I'm not sure about @<@ vs @<=@ above.++{-+-- | Tracing of events.+traceE :: Show t => (a -> String) -> EventG t a -> EventG t a++-- traceE shw = fmap (\ (t,a) -> trace (shw' t a) a) . withTimeGE+--  where+--    shw' t a = "time "++show t++": "++shw a++-- traceE shw = fmap (\ (t,a) -> trace (shw' t) a) . withTimeGE+--  where+--    shw' t = "time "++show t++"\n"++-- traceE shw = fmap (\ a -> trace (shw a) a)++-- Something is wonky.  Try this version, avoiding withTimeGE++traceE shw ~(Event (Future (t,r))) =+  Event (Future (trace ("time "++show t) t, traceR shw r))++-- | Tracing of reactive values+traceR :: Show t => (a -> String) -> Unop (ReactiveG t a)+traceR shw ~(a `Stepper` e) = trace ("val: "++shw a) $+                              a `Stepper` traceE shw e+-}++-- I'm experimenting with lazy patterns here.  They didn't help.+-- When time tracing is on, mappends don't work.  I think the problem is+-- that show extracts *all* information from a time, while 'min' and+-- '(<=)' don't.  Of course: consider two future occurrences being+-- compared.  Before any outer info can be extracted, the trace will+-- evaluate the whole time of a occurrence that hasn't happened yet.+-- +-- To trace an event then, I really want to put partial traces into the+-- times, which will have to work specially for the time type.  Or I could+-- make a Traceable class.++{-++-- | Make an event and a sink for feeding the event.  Each value sent to+-- the sink becomes an occurrence of the event.+mkEvent :: Ord t => IO (EventG t a, SinkG t a)+mkEvent = do (fut,handler) <- newFuture+             -- remember how to save the next occurrence.+             r <- newIORef handler+             return (Event fut, writeTo r)+ where+   -- Fill in an occurrence while preparing for the next one+   writeTo r fut = do handler  <- readIORef r+                      (fut',handler') <- newFuture+                      writeIORef r handler'+                      handler $ fmap (`stepper` Event fut') fut++-- TODO: replace IORefs by mvars.  When I tried before, GuiTV input hung.++-- | Tracing variant of 'mkEvent'+mkEventTrace :: (Ord t, Show t) =>+                (a -> String) -> IO (EventG t a, SinkG t a)+mkEventTrace shw = second tr <$> mkEvent+ where+   tr handler = (putStrLn.shw') `mappend` handler+   shw' (Future (t,a)) = "Occurrence at time "++show t++": "++shw a++-- | Show specialization of 'mkEventTrace'+mkEventShow :: (Ord t, Show t, Show a) => String -> IO (EventG t a, SinkG t a)+mkEventShow str = mkEventTrace ((str ++).(' ':).show)++-}++-- | Get a future representing the first occurrence of the event together+-- with the event of all occurrences after that one.+eventOcc :: (Ord t) => EventG t a -> FutureG t (a, EventG t a)+eventOcc (Event fut)  = (\ (Stepper a e) -> (a,e)) <$> fut+++-- | Sample a reactive value at a sequence of monotonically non-decreasing+-- times.  Deprecated, because it does not reveal when value is known to+-- be repeated in the output.  Those values won't be recomputed, but they+-- may be re-displayed.+rats :: Ord t => ReactiveG t a -> [t] -> [a] -- increasing times++_ `rats` [] = []++r@(a `Stepper` Event (Future (tr',r'))) `rats` ts@(t:ts')+  | ftime t <= tr' = a : r `rats` ts'+  | otherwise      = r' `rats` ts++-- Just for testing+rat :: Ord t => ReactiveG t a -> t -> a+rat r = head . rats r . (:[])+++{--------------------------------------------------------------------+    Other instances+--------------------------------------------------------------------}++-- Standard instances+instance (Monoid_f f, Ord t) => Monoid_f (ReactiveG t :. f) where+    { mempty_f = O (pure mempty_f); mappend_f = inO2 (liftA2 mappend_f) }+instance (Ord t, Pair f) => Pair (ReactiveG t :. f) where pair = apPair++instance Unpair (ReactiveG t) where {pfst = fmap fst; psnd = fmap snd}++-- Standard instances+instance Ord t => Monoid_f (EventG t) where+  { mempty_f = mempty ; mappend_f = mappend }+instance Ord t => Monoid ((EventG t :. f) a) where+  { mempty = O mempty; mappend = inO2 mappend }+instance Ord t => Monoid_f (EventG t :. f) where+  { mempty_f = mempty ; mappend_f = mappend }+instance (Ord t, Copair f) => Pair (EventG t :. f) where+  pair = copair++-- Standard instance for functors+instance Unpair (EventG t) where {pfst = fmap fst; psnd = fmap snd}+++{--------------------------------------------------------------------+    To be moved elsewhere+--------------------------------------------------------------------}++-- | Pass through @Just@ occurrences.+joinMaybes :: MonadPlus m => m (Maybe a) -> m a+joinMaybes = (>>= maybe mzero return)++-- | Pass through values satisfying @p@.+filterMP :: MonadPlus m => (a -> Bool) -> m a -> m a+filterMP p m = joinMaybes (liftM f m)+ where+   f a | p a        = Just a+       | otherwise  = Nothing++-- Alternatively:+-- filterMP p m = m >>= guarded p+--  where+--    guarded p x = guard (p x) >> return x++{--------------------------------------------------------------------+    Tests+--------------------------------------------------------------------}++-- TODO: Define more types like ApTy, use in batch below.  Move to checkers.+type ApTy f a b = f (a -> b) -> f a -> f b++batch :: TestBatch+batch = ( "Reactive.PrimReactive"+        , concatMap unbatch+          [ ("monotonicity",+              [ monotonicity2 "<*>"           +                 ((<*>) :: ApTy (EventG NumT) T T)+--                 ::    EventG NumT (T -> T)+--                    -> EventG NumT T+--                    -> EventG NumT T+              , monotonicity2 "adjustE"       (adjustE+                ::    Time NumT+                   -> EventG NumT NumT+                   -> EventG NumT NumT)+              , monotonicity  "join"          (join+                ::    EventG NumT (EventG NumT T)+                   -> EventG NumT T)+              , monotonicity  "withTimeGE"    (withTimeGE+                ::    EventG NumT T+                   -> EventG NumT (T, Time NumT))+              , monotonicity  "once"          (once+                ::    EventG NumT T+                   -> EventG NumT T)+              , monotonicity2 "accumE"        (accumE+                ::    T+                   -> EventG NumT (T -> T)+                   -> EventG NumT T)+              , monotonicity2 "mappend"       (mappend+                ::    EventG NumT T+                   -> EventG NumT T+                   -> EventG NumT T)+              , monotonicity2 "mplus"         (mplus+                ::    EventG NumT T+                   -> EventG NumT T+                   -> EventG NumT T)+              , monotonicity2 "<|>"           ((<|>)+                ::    EventG NumT T+                   -> EventG NumT T+                   -> EventG NumT T)+              , monotonicity2 "fmap"          (fmap+                ::    (T -> T)+                   -> EventG NumT T+                   -> EventG NumT T)+--              ,monotonicity2 "flip (>>=)"    (flip (>>=))+--              ,monotonicity2 (flip snapshot) "flip snapshot"+              ])+          , ("order preservation",+              [ simulEventOrder  "once"       (once+                ::    EventG NumT NumT+                   -> EventG NumT NumT)+              ])+          -- monad associativity fails+          -- , monad  (undefined :: EventG NumT (NumT,T,NumT))+          , monad  (undefined :: ReactiveG NumT (NumT,T,NumT))+          , monoid (undefined :: EventG NumT T)+          , monoid (undefined :: ReactiveG NumT [T])+--           , ("occurance count",+--              [("joinE", joinEOccuranceCount)]+--             )+          ]+        )++-- joinEOccuranceCount :: Property+-- joinEOccuranceCount =+--   forAll (finiteEvent $ finiteEvent arbitrary+--            :: Gen (EventG NumT (EventG NumT T)))+--          ((==) <$> (sum . map (length . toListE_) . toListE_)+--                <*> (length . toListE_ . joinE))++{-+toListE :: EventG t a -> [FutureG t a]+toListE (Event (Future (Max MaxBound, _             ))) = []+toListE (Event (Future (t0          , v `Stepper` e'))) = Future (t0,v) : toListE e'++toListE_ :: EventG t a -> [a]+toListE_ = map futVal . toListE+-}++monotonicity :: (Show a, Arbitrary a, Arbitrary t+                ,Num t, Ord t, Ord t')+             => String -> (EventG t a -> EventG t' a')+             -> (String,Property)+monotonicity n f = (n, property $ monotoneTest f)++monotonicity2 :: (Show a, Show b, Arbitrary a, Arbitrary b, Arbitrary t+                 ,Num t, Ord t, Ord t')+              => String -> (b -> EventG t a -> EventG t' a')+              -> (String,Property)+monotonicity2 n f = (n, property $ monotoneTest2 f)++monotoneTest :: (Ord t') => (EventG t a -> EventG t' a')+                         -> EventG t a+                         -> Bool+monotoneTest f e = unsafePerformIO (       (evaluate (isMonotoneE . f $ e))+                                    `race` slowTrue)++monotoneTest2 :: (Show a, Show b, Arbitrary a, Arbitrary b, Arbitrary t+                 ,Num t, Ord t, Ord t')+              => (b -> EventG t a -> EventG t' a')+              -> (b ,  EventG t a) -> Bool+monotoneTest2 f (x,e) =+  unsafePerformIO (       (evaluate (isMonotoneE (x `f` e)))+                   `race` slowTrue)++slowTrue :: IO Bool+slowTrue = do threadDelay 10+              return True++-- TODO: Replace this stuff with a use of delay from Data.Later in checkers.+++isMonotoneE :: (Ord t) => EventG t a -> Bool+isMonotoneE = liftA2 (||) ((==(Max MaxBound)) . futTime . eFuture)+                          ((uncurry isMonotoneR') . unFuture . eFuture)++isMonotoneE' :: (Ord t) => (Time t) -> EventG t a -> Bool+isMonotoneE' t =+  liftA2 (||) ((==(Max MaxBound)) . futTime . eFuture)+              ((\(t',r) -> t <= t' && isMonotoneR' t' r) . unFuture . eFuture)++isMonotoneR :: (Ord t) => ReactiveG t a -> Bool+isMonotoneR (_ `Stepper` e) = isMonotoneE e++isMonotoneR' :: (Ord t) => (Time t) -> ReactiveG t a -> Bool+isMonotoneR' t (_ `Stepper` e) = isMonotoneE' t e++simulEventOrder :: (Arbitrary t, Num t, Ord t+                   ,Arbitrary t', Num t', Ord t'+                   ,Num t'', Ord t'', Num t''', Ord t''')+                => String -> (EventG t t' -> EventG t'' t''')+                -> (String, Property)+simulEventOrder n f =+  (n,forAll genEvent (isStillOrderedE . f))+  where+    genEvent :: (Arbitrary t1, Num t1, Ord t1, Arbitrary t2, Num t2, Ord t2)+             => Gen (EventG t1 t2)+    genEvent = liftA futuresE (liftA2 (zipWith future) nondecreasing+                                                          increasing)+    isStillOrderedE :: (Num t1, Ord t1, Num t2, Ord t2) => EventG t1 t2 -> Bool+    isStillOrderedE =+      liftA2 (||) ((==(Max MaxBound)) . futTime . eFuture)+                  (isStillOrderedR . futVal . eFuture)+    +    isStillOrderedR (a `Stepper` e) =+      isStillOrderedE' a e+    +    isStillOrderedE' a =+      liftA2 (||) ((==(Max MaxBound)) . futTime . eFuture)+                  (isStillOrderedR' a . futVal . eFuture)+    +    isStillOrderedR' a (b `Stepper` e) =+      a < b && isStillOrderedE' b e++-- An event to test with that is infinite+infE :: EventG NumT NumT+infE = futuresE (zipWith future [1..] [1..]) 
+ src/FRP/Reactive/Reactive.hs view
@@ -0,0 +1,292 @@+{-# LANGUAGE TypeSynonymInstances, ScopedTypeVariables, TypeOperators, FlexibleInstances #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  FRP.Reactive.Reactive+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Simple reactive values.  Adds some extra functionality on top of+-- "FRP.Reactive.PrimReactive"+----------------------------------------------------------------------++module FRP.Reactive.Reactive+  (+    module FRP.Reactive.PrimReactive+  , TimeT, ITime, Future+  , traceF+    -- * Event+  , Event+  , withTimeE+  , atTime, atTimes, listE+  , {-mbsEvent,-} pairE, scanlE, monoidE+  , withPrevE, withPrevEWith+  , stateE, stateE_, countE, countE_, diffE+    -- * Reactive values+  , Reactive+  , Source+  , snapshot_, snapshot, whenE+  , scanlR, monoidR, eitherE, maybeR, flipFlop, countR+  , splitE, switchE+  , integral, sumR+    -- * Re-export+  , exact+    -- * Tests+  , batch+  ) where++import Control.Applicative+import Control.Arrow (first,second)+import Control.Monad+import Data.Monoid+import Debug.Trace (trace)++-- import Test.QuickCheck+import Test.QuickCheck.Checkers+import Test.QuickCheck.Classes ()++-- vector-space+import Data.VectorSpace++-- TypeCompose+import Data.Pair (pairEdit)++import Data.Max+import Data.AddBounds+import FRP.Reactive.Future hiding (batch)+import FRP.Reactive.PrimReactive hiding (batch)+import FRP.Reactive.Improving++-- | The type of finite time values.+type TimeT = Double++-- | Improving doubles, as used for time values in 'Event', 'Reactive',+-- and 'ReactiveB'.+type ITime = Improving TimeT++-- | Type of future values.  Specializes 'FutureG'.+type Future = FutureG ITime++-- -- | Sink, e.g., for an event handler+-- type Sink a = SinkG Time a+++-- | Trace the elements of a functor type.+traceF :: Functor f => (a -> String) -> f a -> f a+traceF shw = fmap (\ a -> trace (shw a) a)++-- traceShowF :: (Functor f,Show a) => f a -> f a+-- traceShowF = traceF show+++{--------------------------------------------------------------------+    Events+--------------------------------------------------------------------}++-- | Events, specialized to improving doubles for time+type Event = EventG ITime++-- | Access occurrence times in an event.  See 'withTimeGE' for more+-- general notions of time.+withTimeE :: Event a -> Event (a, TimeT)+withTimeE e = second (exact.timeT) <$> withTimeGE e++timeT :: Ord t => Time t -> t+timeT (Max (NoBound t)) = t+timeT _                 = error "timeT: non-finite time"++-- | Single-occurrence event at given time.  See 'atTimes' and 'atTimeG'.+atTime ::  TimeT -> Event ()+atTime = atTimeG . exactly++-- | Event occuring at given times.  See also 'atTime' and 'atTimeG'.+atTimes ::  [TimeT] -> Event ()+atTimes = atTimesG . fmap exactly++-- | Convert a temporally monotonic list of timed values to an event.  See also+-- the generalization 'listEG'+listE :: [(TimeT,a)] -> Event a+listE = listEG . fmap (first exactly)++-- | Generate a pair-valued event, given a pair of initial values and a+-- pair of events.  See also 'pair' on 'Reactive'.+pairE :: Ord t => (c,d) -> (EventG t c, EventG t d) -> EventG t (c,d)+pairE cd cde = cd `accumE` pairEdit cde++-- | Like 'scanl' for events.+scanlE :: Ord t => (a -> b -> a) -> a -> EventG t b -> EventG t a+scanlE f a e = a `accumE` (flip f <$> e)++-- | Accumulate values from a monoid-typed event.  Specialization of+-- 'scanlE', using 'mappend' and 'mempty'.+monoidE :: (Ord t, Monoid o) => EventG t o -> EventG t o+monoidE = scanlE mappend mempty++-- | Pair each event value with the previous one.  The second result is+-- the old one.  Nothing will come out for the first occurrence of @e@,+-- but if you have an initial value @a@, you can do @withPrevE (pure a+-- `mappend` e)@.+withPrevE :: Ord t => EventG t a -> EventG t (a,a)+withPrevE e = (joinMaybes . fmap combineMaybes) $+              (Nothing,Nothing) `accumE` fmap (shift.Just) e+ where+   -- Shift newer value into (old,new) pair if present.+   shift :: u -> (u,u) -> (u,u)+   shift new (old,_) = (new,old)+   combineMaybes :: (Maybe u, Maybe v) -> Maybe (u,v)+   combineMaybes = uncurry (liftA2 (,))++-- | Same as 'withPrevE', but allow a function to combine the values.+-- Provided for convenience.+withPrevEWith :: Ord t => (a -> a -> b) -> EventG t a -> EventG t b+withPrevEWith f e =  fmap (uncurry f) (withPrevE e)+++-- | State machine, given initial value and transition function.  Carries+-- along event data.  See also 'stateE_'.  TODO: better name.+stateE :: Ord t => s -> (s -> s) -> EventG t b -> EventG t (b,s)+stateE s0 f = scanlE h (b0,s0)+ where+   b0        = error "stateE: no initial value"+   h (_,s) b = (b, f s)++-- | State machine, given initial value and transition function.  See also+-- 'stateE'.+stateE_ :: Ord t => s -> (s -> s) -> EventG t b -> EventG t s+stateE_ = (fmap.fmap.fmap.fmap) snd stateE++-- stateE_ s0 f e = snd <$> stateE s0 f e+++-- | Count occurrences of an event, remembering the occurrence values.+-- See also 'countE_'.+countE :: (Ord t, Num n) => EventG t b -> EventG t (b,n)+countE = stateE 0 (+1)++-- | Count occurrences of an event, forgetting the occurrence values.  See+-- also 'countE'.+countE_ :: (Ord t, Num n) => EventG t b -> EventG t n+countE_ = (fmap.fmap) snd countE++-- countE_ e = snd <$> countE e++-- | Difference of successive event occurrences.  See 'withPrevE' for a+-- trick to supply an initial previous value.+diffE :: (Ord t, Num n) => EventG t n -> EventG t n+diffE = withPrevEWith (flip subtract)++-- -- | Returns an event whose occurrence's value corresponds with the input+-- --   event's previous occurence's value.+-- delayE :: Event a -> Event a+-- delayE = withPrevEWith (flip const)++-- I suspect that delayE will only be used to hide implementation+-- problems, so I removed it.  - Conal++{--------------------------------------------------------------------+    Reactive extras (defined via primitives)+--------------------------------------------------------------------}++-- | Reactive values, specialized to improving doubles for time+type Reactive = ReactiveG ITime++-- | Compatibility synonym (for ease of transition from DataDriven)+type Source = Reactive+++-- | Snapshot a reactive value whenever an event occurs.+snapshot :: Ord t => EventG t a -> ReactiveG t b -> EventG t (a,b)+snapshot = snapshotWith (,)++-- | Like 'snapshot' but discarding event data (often @a@ is '()').+snapshot_ :: Ord t => EventG t a -> ReactiveG t b -> EventG t b+snapshot_ = snapshotWith (flip const)++-- Alternative implementations+-- e `snapshot_` src = snd <$> (e `snapshot` src)+-- snapshot_ = (fmap.fmap.fmap) snd snapshot++-- | Filter an event according to whether a reactive boolean is true.+whenE :: Ord t => EventG t a -> ReactiveG t Bool -> EventG t a+whenE e = joinMaybes . fmap h . snapshot e+ where+   h (a,True)  = Just a+   h (_,False) = Nothing++-- | Like 'scanl' for reactive values.  See also 'scanlE'.+scanlR :: Ord t => (a -> b -> a) -> a -> EventG t b -> ReactiveG t a+scanlR f a e = a `stepper` scanlE f a e++-- | Accumulate values from a monoid-valued event.  Specialization of+-- 'scanlE', using 'mappend' and 'mempty'.  See also 'monoidE'.+monoidR :: (Ord t, Monoid a) => EventG t a -> ReactiveG t a+monoidR = scanlR mappend mempty++-- Equivalently,+--   monoidR = stepper mempty . monoidE++-- | Combine two events into one.+eitherE :: Ord t => EventG t a -> EventG t b -> EventG t (Either a b)+eitherE ea eb = ((Left <$> ea) `mappend` (Right <$> eb))++-- | Start out blank ('Nothing'), latching onto each new @a@, and blanking+-- on each @b@.  If you just want to latch and not blank, then use+-- 'mempty' for @lose@.+maybeR :: Ord t => EventG t a -> EventG t b -> ReactiveG t (Maybe a)+maybeR get lose =+  Nothing `stepper` ((Just <$> get) `mappend` (Nothing <$ lose))++-- | Flip-flopping reactive value.  Turns true when @ea@ occurs and false+-- when @eb@ occurs.+flipFlop :: Ord t => EventG t a -> EventG t b -> ReactiveG t Bool+flipFlop ea eb =+  False `stepper` ((True <$ ea) `mappend` (False <$ eb))++-- TODO: redefine maybeR and flipFlop in terms of eitherE.++-- | Count occurrences of an event.  See also 'countE'.+countR :: (Ord t, Num n) => EventG t a -> ReactiveG t n+countR e = 0 `stepper` countE_ e++-- | Partition an event into segments.+splitE :: Ord t => EventG t a -> EventG t b -> EventG t (a, EventG t b)+ea `splitE` eb = h <$> (withRestE ea `snapshot` remainderR eb)+ where+   h ((a,ea'),eb') = (a, eb' `untilE` ea')++-- | Switch from one event to another, as they occur.  (Doesn't merge, as+-- 'join' does.)+switchE :: Event (Event a) -> Event a+switchE = join . fmap (uncurry untilE) . withRestE+++-- | Euler integral.+integral :: forall v t. (Num t, VectorSpace v t) =>+            t -> Event t -> Reactive v -> Reactive v+integral t0 newT r = sumR (snapshotWith (*^) deltaT r)+  where+    deltaT :: Event t+    deltaT = diffE (pure t0 `mappend` newT)++-- TODO: find out whether this integral works recursively.  If not, then+-- fix the implementation, rather than changing the semantics.  (No+-- "delayed integral".)++sumR :: VectorSpace v s => Event v -> Reactive v+sumR = scanlR (^+^) zeroV+++{----------------------------------------------------------+    Tests+----------------------------------------------------------}++batch :: TestBatch+batch = ( "FRP.Reactive.Reactive"+        , concatMap unbatch+            [ +            -- Write some tests!+            ]+        )
+ src/FRP/Reactive/SImproving.hs view
@@ -0,0 +1,173 @@+{-# OPTIONS -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  Data.SImproving+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- \"Improving values\" from Warren Burton's \"Encapsulating Nondeterminacy+-- in an Abstract Data Type with Deterministic Semantics\".+-- +-- This implementation is simple but not efficient, as it accumulates lots+-- of lower bounds.+----------------------------------------------------------------------++module Reactive.SImproving+  (+    Improving(..), exactly, exact, improveMbs+  -- * Misc speculation tools+  , spec, specNY, specYY, start+  ) where++import Data.Function (on)+-- import Debug.Trace++import Control.Parallel (par)++-- | Progressive information about a value (e.g., a time).  Represented as+-- a non-empty list of monotonically non-decreasing values.  The last one+-- is the actual value.  (The operations here ensure that the values are+-- strictly increasing, but they only rely on non-decreasing.)+newtype Improving a = Imp { unImp :: [a] } deriving Show++-- | Apply a unary function inside an 'Improving' representation.+inImp :: ([a] -> [b]) -> (Improving a -> Improving b)+inImp f = Imp . f . unImp++-- | Apply a unary function inside an 'Improving' representation.+inImp2 :: ([a] -> [b] -> [c]) -> (Improving a -> Improving b -> Improving c)+inImp2 f = inImp . f . unImp++-- | A known improving value (which doesn't really improve)+exactly :: Ord a => a -> Improving a+exactly = Imp . (:[])++-- | Extract an exact value from an improving value+exact :: Improving a -> a+exact = last . unImp++instance Eq a => Eq (Improving a) where+  (==) = (==) `on` exact++instance Ord a => Ord (Improving a) where+  Imp xs `compare` Imp ys = -- trace "Improving: compare" $+                            xs `compares` ys+  -- experimental.  probably eliminate.+  Imp xs <= Imp ys = xs `leq` ys+  min = inImp2 shortMerge+  max = inImp2 (specNY monotonicAppend)++-- This one wasn't in the Improving Values papers.  Here so that+-- 'compare', '(<=)', etc are defined on Improving.+compares :: Ord a => [a] -> [a] -> Ordering+compares [] _    = error "compares: emptied first argument"+compares _ []    = error "compares: emptied second argument"+compares [x] (y:_) | x < y = LT+compares (x:_) [y] | x > y = GT+compares [x] [y] = compare x y+-- we know x >= y and length ys >= 2+compares xs@[_] (_:ys') = compares xs ys'+-- we know x <= y and length xs >= 2+compares (_:xs') ys@[_] = compares xs' ys+-- neither list is down to last element.  progress where less is known.+compares xs@(x:xs') ys@(y:ys') | x == y    = compares xs' ys'+                               | x  < y    = compares xs' ys+                               | otherwise = compares xs  ys'++-- Hm!  The test I really want is (<=), which can get an answer based on+-- slightly less information than compares.++leq :: Ord a => [a] -> [a] -> Bool+leq [] _    = error "leq: emptied first argument"+leq _ []    = error "leq: emptied second argument"+leq [x] (y:_) | x <= y = True+leq (x:_) [y] | x >  y = False+leq [x] [y] = x <= y+-- we know x > y and length ys >= 2+leq xs@[_] (_:ys') = leq xs ys'+-- we know x <= y and length xs >= 2+leq (_:xs') ys@[_] = leq xs' ys+-- neither list is down to last element.  progress where less is known.+leq xs@(x:xs') ys@(y:ys') | x == y    = leq xs' ys'+                          | x  < y    = leq xs' ys+                          | otherwise = leq xs  ys'++-- leq didn't fix the bug I'm finding in phooey (src/Examples/Monad, t5)+-- when using SReactive instead of PrimReactive in Data/Reactive.+-- Probably remove leq later.+++shortMerge :: Ord a => [a] -> [a] -> [a]+shortMerge [] _ = []+shortMerge _ [] = []+shortMerge xs@(x:xs') ys@(y:ys')+  | x == y    = x : shortMerge xs' ys'+  | x <  y    = x : shortMerge xs' ys+  | otherwise = y : shortMerge xs  ys'++monotonicAppend :: Ord a => [a] -> [a] -> [a]+-- monotonicAppend [x]     ys = x : dropWhile (<= x) ys+-- monotonicAppend (x:xs') ys = x : monotonicAppend xs' ys+-- monotonicAppend []      _  = error "monotonicAppend: empty list"++-- From "Encapsulating nondeterminacy in an abstract data type with+-- deterministic semantics"+monotonicAppend xs ys = xs ++ dropWhile (<= last xs) ys+++-- TODO: consider trimming ys as we go, rather than later.  However, I+-- have a fuzzy understanding of why spec_max and not just max in the+-- papers.++-- | Interpret 'Nothing' values as lower bounds+improveMbs :: [(t, Maybe a)] -> [(Improving t, a)]+improveMbs = foldr f []+ where+   f (t,Just a ) qs = (Imp [t],a) : qs+   f (t,Nothing) ~((Imp ts', a) : qs') = (Imp (t:ts'), a) : qs'+   -- f (_,Nothing) [] = error "improveMbs: input ends in a Nothing"++-- The lazy pattern (~) above is essential for laziness.  It also+-- complicates giving an error message if the input ends in a Nothing.++-- improveMbs [] = []+-- improveMbs ((t,Just a ) : ps') = (Imp [{-tr True-} t],a) : improveMbs ps'+-- improveMbs ((t,Nothing) : ps') = (Imp ({-tr False-} t:ts'), a) : qs'+--  where+--     (Imp ts', a) : qs' = improveMbs ps'++-- tr :: (Show x, Show t) => x -> t -> t+-- tr x t = t+--          -- trace (show (t, x)) t++-- improveMbs = foldr f []+--  where+--    f (t,Just a ) qs = (Imp [t],a) : qs+--    f (t,Nothing) qs =+--      case qs of ((Imp ts', a) : qs') -> (Imp (t:ts'), a) : qs'+--                 [] -> error "improveMbs: input ends in a Nothing"++-- TODO: re-think the case of input ending in a Nothing.+++---- Misc++spec :: (a -> b) -> (a -> b)+spec f a = a `par` f a++specNY :: (a -> b -> c) -> (a -> b -> c)+specNY f a = spec (f a)++specYY :: (a -> b -> c) -> (a -> b -> c)+specYY f a = spec (spec f a)++start :: [a] -> [a]+start [] = []+start (x:xs) = specYY (:) x (start xs)++-- Hm. Does this specNY really do anything?  How far does 'par' evaluate?+-- Probably to WHNF, which wouldn't help much, would it?  And I don't+-- understand the point yet.  Read further in the paper.
+ src/FRP/Reactive/Sorted.hs view
@@ -0,0 +1,77 @@+{-# OPTIONS_GHC -Wall #-}++----------------------------------------------------------------------+-- |+-- Module      :  Data.Sorted+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Sorted lists: experimental (unused)+----------------------------------------------------------------------++module Reactive.Sorted where++import Data.Monoid+import Data.List (sort)+import Control.Applicative+import Control.Monad++newtype Sorted a = Sort { unSort :: [a] } -- non-decreasing values++-- | Apply a unary function within the event representation.+inSort :: ([a] -> [b]) -> (Sorted a -> Sorted b)+inSort f = Sort . f . unSort++-- | Apply a binary function within the event representation.+inSort2 :: ([a] -> [b] -> [c]) -> (Sorted a -> Sorted b -> Sorted c)+inSort2 f = inSort . f . unSort+++instance Ord a => Monoid (Sorted a) where+  mempty  = Sort []+  mappend = inSort2 merge++-- | Merge two ordered lists into an ordered list.+merge :: Ord a => [a] -> [a] -> [a]+[]         `merge` vs         = vs+us         `merge` []         = us+us@(u:us') `merge` vs@(v:vs') =+  (u `min` v) : if u <= v then us' `merge` vs else us `merge` vs'++-- Alternatively,+-- +--   us@(u:us') `merge` vs@(v:vs') =+--     if u <= v then+--       u : (us' `merge` vs )+--     else+--       v : (us  `merge` vs')+-- +-- The definition used instead is more productive.  It produces a cons+-- cell immediately and can even produce partial information about @u+-- `min` v@ before it's known which is smaller.++class FunctorOrd h where+  fmapO :: (Ord a, Ord b) => (a -> b) -> h a -> h b++class FunctorOrd h => ApplicativeOrd h where+  pureO  :: Ord a => a -> h a+  (<*?>) :: (Ord a, Ord b) => h (a -> b) -> h a -> h b++class MonadOrd h where+  returnO :: Ord a => a -> h a+  -- does joinO need Ord (h a) ?+  joinO :: Ord a => h (h a) -> h a++instance FunctorOrd Sorted where+  fmapO f = inSort (sort . fmap f)++instance ApplicativeOrd Sorted where+  pureO a = Sort (pure a)+  (<*?>)  = inSort2 $ (fmap.fmap) sort (<*>)++instance MonadOrd Sorted where+  returnO = pureO+  joinO = inSort $ sort . join . fmap unSort 
+ src/FRP/Reactive/VectorSpace.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}++{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}++module FRP.Reactive.VectorSpace( ) where++import FRP.Reactive.Behavior+import Control.Applicative++import Data.VectorSpace+import Data.AdditiveGroup++instance AdditiveGroup v => AdditiveGroup (Behavior v) where+  zeroV   = pure   zeroV+  (^+^)   = liftA2 (^+^)+  negateV = liftA   negateV++instance VectorSpace v s => VectorSpace (Behavior v) s where+  (*^) s = fmap (s *^)
+ src/Test/Reactive.hs view
@@ -0,0 +1,33 @@+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module      :  Test.TestReactive+-- Copyright   :  (c) Conal Elliott 2008+-- License     :  BSD3+-- +-- Maintainer  :  conal@conal.net+-- Stability   :  experimental+-- +-- Gather up QuickCheck tests for Reactive+----------------------------------------------------------------------++module Test.Reactive (batches,main) where++import Test.QuickCheck.Checkers++-- import qualified Data.Unamb++import qualified FRP.Reactive.Future+import qualified FRP.Reactive.PrimReactive+import qualified FRP.Reactive.Reactive+import qualified FRP.Reactive.Fun++batches :: [TestBatch]+batches = [ FRP.Reactive.Future.batch+          , FRP.Reactive.PrimReactive.batch+          , FRP.Reactive.Reactive.batch+          , FRP.Reactive.Fun.batch+          ]++main :: IO ()+main = mapM_ quickBatch batches