Permalink
Browse files

Added effectful combinators to the simple variant.

  • Loading branch information...
1 parent 35a265e commit fbc6b6e99b1b7a9d75a0e00b7fe6a2661b95cd9d @cobbpg committed Nov 11, 2011
Showing with 110 additions and 3 deletions.
  1. +3 −0 CHANGES
  2. +105 −1 FRP/Elerea/Simple.hs
  3. +2 −2 elerea.cabal
View
@@ -1,3 +1,6 @@
+2.4.0 - 111111
+* added effectful signals to assist library writers
+
2.3.0 - 110627
* reimplemented clocked variant in a correct and more efficient way
View
@@ -28,6 +28,12 @@ module FRP.Elerea.Simple
, transfer2
, transfer3
, transfer4
+ -- * Signals with side effects
+ , effectful
+ , effectful1
+ , effectful2
+ , effectful3
+ , effectful4
-- * Random sources
, noise
, getRandom
@@ -251,6 +257,10 @@ generator (S s) = SG $ \pool -> do
addSignal (const sample) (const (() <$ sample)) ref pool
+-- | Auxiliary function.
+memoise :: IORef (Phase a) -> a -> IO a
+memoise ref x = writeIORef ref (Updated undefined x) >> return x
+
-- | Memoising combinator. It can be used to cache results of
-- applicative combinators in case they are used in several places.
-- It is observationally equivalent to 'return' in the 'SignalGen'
@@ -269,7 +279,7 @@ memo :: Signal a -- ^ the signal to cache
memo (S s) = SG $ \pool -> do
ref <- newIORef (Ready undefined)
- let sample = s >>= \x -> writeIORef ref (Updated undefined x) >> return x
+ let sample = s >>= memoise ref
addSignal (const sample) (const (() <$ sample)) ref pool
@@ -475,6 +485,100 @@ transfer4 x0 f s1 s2 s3 s4 = mfix $ \sig -> do
sig' <- delay x0 sig
memo (liftM5 f s1 s2 s3 s4 sig')
+-- | A signal that executes a given IO action once at every sampling.
+-- The IO action is constructed by an initialiser.
+--
+-- In essence, this combinator provides cooperative multitasking
+-- capabilities, and its primary purpose is to assist library writers
+-- in wrapping effectful APIs as conceptually pure signals. If there
+-- are several effectful signals in the system, their order of
+-- execution is undefined and should not be relied on.
+--
+-- Example:
+--
+-- > do
+-- > act <- start $ effectful $ do
+-- > ref <- newIORef 0
+-- > return $ do
+-- > x <- readIORef ref
+-- > putStrLn $ "Count: " ++ show x
+-- > writeIORef ref $! x+1
+-- > return ()
+-- > replicateM_ 5 act
+--
+-- Output:
+--
+-- > Count: 0
+-- > Count: 1
+-- > Count: 2
+-- > Count: 3
+-- > Count: 4
+effectful :: IO (IO a) -- ^ initialiser that yields the action to be executed repeatedly
+ -> SignalGen (Signal a)
+effectful gen = SG $ \pool -> do
+ ref <- newIORef (Ready undefined)
+ act <- gen
+
+ let sample = act >>= memoise ref
+
+ addSignal (const sample) (const (() <$ sample)) ref pool
+
+-- | A signal that executes a parametric IO action once at every
+-- sampling. The IO action is constructed by an initialiser, and the
+-- parameter is supplied by another signal at every sampling step.
+effectful1 :: IO (t -> IO a) -- ^ initialiser that yields the action to be executed repeatedly
+ -> Signal t -- ^ parameter signal
+ -> SignalGen (Signal a)
+effectful1 gen (S s) = SG $ \pool -> do
+ ref <- newIORef (Ready undefined)
+ act <- gen
+
+ let sample = s >>= act >>= memoise ref
+
+ addSignal (const sample) (const (() <$ sample)) ref pool
+
+-- | Like 'effectful1', but with two parameter signals.
+effectful2 :: IO (t1 -> t2 -> IO a) -- ^ initialiser that yields the action to be executed repeatedly
+ -> Signal t1 -- ^ parameter signal 1
+ -> Signal t2 -- ^ parameter signal 2
+ -> SignalGen (Signal a)
+effectful2 gen (S s1) (S s2) = SG $ \pool -> do
+ ref <- newIORef (Ready undefined)
+ act <- gen
+
+ let sample = join (liftM2 act s1 s2) >>= memoise ref
+
+ addSignal (const sample) (const (() <$ sample)) ref pool
+
+-- | Like 'effectful1', but with three parameter signals.
+effectful3 :: IO (t1 -> t2 -> t3 -> IO a) -- ^ initialiser that yields the action to be executed repeatedly
+ -> Signal t1 -- ^ parameter signal 1
+ -> Signal t2 -- ^ parameter signal 2
+ -> Signal t3 -- ^ parameter signal 3
+ -> SignalGen (Signal a)
+effectful3 gen (S s1) (S s2) (S s3) = SG $ \pool -> do
+ ref <- newIORef (Ready undefined)
+ act <- gen
+
+ let sample = join (liftM3 act s1 s2 s3) >>= memoise ref
+
+ addSignal (const sample) (const (() <$ sample)) ref pool
+
+-- | Like 'effectful1', but with four parameter signals.
+effectful4 :: IO (t1 -> t2 -> t3 -> t4 -> IO a) -- ^ initialiser that yields the action to be executed repeatedly
+ -> Signal t1 -- ^ parameter signal 1
+ -> Signal t2 -- ^ parameter signal 2
+ -> Signal t3 -- ^ parameter signal 3
+ -> Signal t4 -- ^ parameter signal 4
+ -> SignalGen (Signal a)
+effectful4 gen (S s1) (S s2) (S s3) (S s4) = SG $ \pool -> do
+ ref <- newIORef (Ready undefined)
+ act <- gen
+
+ let sample = join (liftM4 act s1 s2 s3 s4) >>= memoise ref
+
+ addSignal (const sample) (const (() <$ sample)) ref pool
+
-- | A random signal.
--
-- Example:
View
@@ -1,5 +1,5 @@
Name: elerea
-Version: 2.3.0
+Version: 2.4.0
Cabal-Version: >= 1.2
Synopsis: A minimalistic FRP library
Category: reactivity, FRP
@@ -38,7 +38,7 @@ Description:
(<http://sgate.emt.bme.hu/documents/patai/publications/PataiWFLP2010.pdf>).
Author: Patai Gergely
-Maintainer: Patai Gergely (patai@iit.bme.hu)
+Maintainer: Patai Gergely (patai.gergely@gmail.com)
Copyright: (c) 2009-2011, Patai Gergely
License: BSD3
License-File: LICENSE

0 comments on commit fbc6b6e

Please sign in to comment.