Skip to content

Instantly share code, notes, and snippets.

@bennofs
Created October 28, 2015 17:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bennofs/a03445850410ef602963 to your computer and use it in GitHub Desktop.
Save bennofs/a03445850410ef602963 to your computer and use it in GitHub Desktop.
reflex notes

Module overview

Reflex.Class: generic definition of FRP, operations on Events / Behaviors

provides functions for working with events and behaviors

should be imported by users of an FRP-based framework

does not deal with connecting to the outside world, for example creating events from external sources

Reflex.Dynamic: discrete behaviors

Provides the Dynamic data type, a combination of Behavior and Event

Behavior always contains the current value, Event is fired whenever current value changes

-> Behavior is only updated at the end of the current frame, after event propagation

Reflex.Host.Class: generic framework implementation support

use this module if you want to write your own FRP framework

allows to create new events from outside sources

allows to perform actions when events occur

Reflex.Spider: one concrete implementation of the reflex FRP specification

Reflex.Spider.Internal contains concrete implementation

Should not need to be imported for normal usage, since Internal

Exports the Spider type, which represents this FRP implementation (-> instance of Reflex)

SpiderHost is the monad used for implementing frameworks that use this implementation (-> instance of ReflexHost)

Working with Events and Behaviors

Trivial values: `never` (never occurs), `constant a` (always has value a, never changes)

Events and Behaviors are Functors -> use fmap to change their value

Mapping events with access to behaviors (“reading” from events): push

This function also allows to sample behaviors

can connect behaviors and events: for example, tag can be implemented with push

can make behaviors that depend on event values: combine push with switch and hold

Example: make a behavior that always contains the last event value together with the current value of the behavior

switch $ flip push event $ \eventValue $ do return $ Just $ fmap (\behaviorValue -> (eventValue, behaviorValue)) behavior

Example: filter an integer event so it only occurs when the integer is even

push (\eventValue -> return $ if even eventValue then Just eventValue else Nothing)

Sampling behaviors to form new behaviors: pull

cannot get the state of events, only read from behaviors

easy to join nested behaviors: pull $ sample behavior >>= sample

Changing behaviors depending on events: switch

Joining events: coincidence

rarely used, but needed for completeness

joins nested events, such that an event occurs when both inner and outer events occur at the same time

Efficiently combining multiple events into a single one: merge

efficiently combines multiple events into a single one

supports input events with different value types

input: collections of events, indexed by a key, where the type of the event is determined by the key

output: event that fires whenever one or more input events fired, with their values.

Efficiently deliviering an event to many consumers: fan

should be partially applied!

for performance, since assignment of events to consumers happens once and not separately for each consumer

Implementing FRP-based frameworks

Networks should be build in a type with an instance for MonadReflexHost

To use the Spider implemenation, import Reflex.Spider and use runSpiderHost

create events with newEventWithTrigger.

subscribe for events using `subscribeEvent`

use fireEventsAndRead to notify the network whenever one or more input events changed

you can read the new output events here, which you should use to update the outside world

fireEventsAndRead should be called in the event loop, whenever one input event changed

The Spider Implementation

The FRP network forms a directed graph: nodes are FRP objects (events, behaviors, etc) and edges represent dependencies

To run the frame, spider does a breadth-first traversal of the graph

Each entry in the network has an associated height:

Entries with a lower height may never depend on entries with a greater height. This means that when entries of a given height are evaluated, all entries of previous heights have already been processed.

Notes on weak references

If possible, place on IORef since IORefs aren’t optimized away

If placing a weak reference on a constructed haskell type:

make sure that the value is in WHNF before adding the weak ref, since forcing sometimes makes the weak ref invalid

hide the constructor from GHC via a NOINLINE function, or GHC will optimize the constructor away and directly use the parts

to keep the value itself alive, do not store it in unpacked fields (since that again will eliminate the constructor)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment