reactive-banana-1.2.1.0: Library for functional reactive programming (FRP).

Safe HaskellNone
LanguageHaskell98

Reactive.Banana.Prim

Contents

Synopsis

Synopsis

This is an internal module, useful if you want to implemented your own FRP library. If you just want to use FRP in your project, have a look at Reactive.Banana instead.

Evaluation

type Step = EvalNetwork (IO ()) Source #

data Network Source #

A Network represents the state of a pulse/latch network,

Build FRP networks

type Build = ReaderWriterIOT BuildR BuildW IO Source #

buildLaterReadNow :: Build a -> Build a Source #

Pretend to return a value right now, but do not actually calculate it until later.

NOTE: Accessing the value before it's written leads to an error.

FIXME: Is there a way to have the value calculate on demand?

compile :: BuildIO a -> Network -> IO (a, Network) Source #

Change a Network of pulses and latches by executing a BuildIO action.

Caching

Testing

interpret :: (Pulse a -> BuildIO (Pulse b)) -> [Maybe a] -> IO [Maybe b] Source #

Simple interpreter for pulse/latch networks.

Mainly useful for testing functionality

Note: The result is not computed lazily, for similar reasons that the sequence function does not compute its result lazily.

mapAccumM :: Monad m => (a -> s -> m (b, s)) -> s -> [a] -> m [b] Source #

mapAccum for a monad.

mapAccumM_ :: Monad m => (a -> s -> m (b, s)) -> s -> [a] -> m () Source #

Strict mapAccum for a monad. Discards results.

runSpaceProfile :: Show b => (Pulse a -> BuildIO (Pulse b)) -> [a] -> IO () Source #

Execute an FRP network with a sequence of inputs. Make sure that outputs are evaluated, but don't display their values.

Mainly useful for testing whether there are space leaks.

IO

newInput :: forall a. Build (Pulse a, a -> Step) Source #

Create a new pulse in the network and a function to trigger it.

Together with addHandler, this function can be used to operate with pulses as with standard callback-based events.

addHandler :: Pulse (Future a) -> (a -> IO ()) -> Build () Source #

Register a handler to be executed whenever a pulse occurs.

The pulse may refer to future latch values.

readLatch :: Latch a -> Build a Source #

Read the value of a Latch at a particular moment in time.

Pulse

type Pulse a = Ref (Pulse' a) Source #

neverP :: Build (Pulse a) Source #

Pulse that never fires.

mapP :: (a -> b) -> Pulse a -> Build (Pulse b) Source #

type Future = IO Source #

tagFuture :: Latch a -> Pulse b -> Build (Pulse (Future a)) Source #

Tag a Pulse with future values of a Latch.

This is in contrast to applyP which applies the current value of a Latch to a pulse.

unsafeMapIOP :: forall a b. (a -> IO b) -> Pulse a -> Build (Pulse b) Source #

unionWithP :: forall a. (a -> a -> a) -> Pulse a -> Pulse a -> Build (Pulse a) Source #

Latch

type Latch a = Ref (Latch' a) Source #

pureL :: a -> Latch a Source #

mapL :: (a -> b) -> Latch a -> Latch b Source #

applyL :: Latch (a -> b) -> Latch a -> Latch b Source #

accumL :: a -> Pulse (a -> a) -> Build (Latch a, Pulse a) Source #

applyP :: Latch (a -> b) -> Pulse a -> Build (Pulse b) Source #

Dynamic event switching

switchL :: Latch a -> Pulse (Latch a) -> Build (Latch a) Source #

executeP :: forall a b. Pulse (b -> Build a) -> b -> Build (Pulse a) Source #

Notes

The Build monad is an instance of MonadFix and supports value recursion. However, it is built on top of the IO monad, so the recursion is somewhat limited.

The main rule for value recursion in the IO monad is that the action to be performed must be known in advance. For instance, the following snippet will not work, because putStrLn cannot complete its action without inspecting x, which is not defined until later.

  mdo
      putStrLn x
      let x = "Hello recursion"

On the other hand, whenever the sequence of IO actions can be known before inspecting any later arguments, the recursion works. For instance the snippet

  mdo
      p1 <- mapP p2
      p2 <- neverP
      return p1

works because mapP does not inspect its argument. In other words, a call p1 <- mapP undefined would perform the same sequence of IO actions. (Internally, it essentially calls newIORef.)

With this issue in mind, almost all operations that build Latch and Pulse values have been carefully implemented to not inspect their arguments. In conjunction with the Cached mechanism for observable sharing, this allows us to build combinators that can be used recursively. One notable exception is the readLatch function, which must inspect its argument in order to be able to read its value.