Reflex FRP

repository·develop·Indexed 22 days ago

https://github.com/reflex-frp/reflex

A high-performance, deterministic Functional Reactive Programming (FRP) engine and interface for Haskell. It allows developers to build complex interactive systems using pure functions, composable events, and time-varying values (Behaviors and Dynamics) instead of callbacks. The library provides primitives for transforming and combining events, managing dynamic collections, performing side-effects via PerformEvent, and handling dynamic sub-networks.

Tokens
3.4K
Snippets
10
Records
14
Agent score
28%

What's inside reflex

  1. Overview of Reflex FRP

    develop
    Reflex is a fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and engine. It allows you to build interactive programs without using callbacks or side-effects by using composable events and time-varying values to describe interactive systems as pure functions.
  2. Flatten Nested FRP Types

    develop

    Flattening functions remove outer wrappers like Event (Event a) or Dynamic (Dynamic a) to create a single, simpler stream.

    Event Flattening

    • switch: Flattens Behavior (Event a) to Event a. Uses the old event during switchover.
    • switchDyn: Flattens Dynamic (Event a) to Event a. Uses the new event immediately.
    • coincidence: Flattens Event (Event a) to an event that fires only when both the outer and inner events fire.

    Dynamic Flattening

    • join: Flattens Dynamic (Dynamic a) to Dynamic a. The output updates whenever the inner OR outer dynamic updates.
    • joinDynThroughMap: Flattens Dynamic (Map k (Dynamic a)) to Dynamic (Map k a).

    Behavior Flattening

    • join: Flattens Behavior (Behavior a) to Behavior a (standard monadic join).
  3. Understand Reflex Monadic Contexts and Annotations

    develop

    Reflex functions often operate in a monadic context m a. The specific monad m is determined by your FRP host (e.g., reflex-dom). To help developers understand what capabilities a function requires, the documentation uses specific annotations:

    • [ ] (Pure): Operates on Events, Behaviors, or Dynamics without regard to "current time".
    • [S] (MonadSample): Requires a monad supporting MonadSample. Used for functions that produce a result "as of now".
    • [H] (MonadHold): Requires a monad supporting MonadHold. Since MonadHold depends on MonadSample, any [S] function also runs in an [H] context.
    • [B] (PostBuild): Requires PostBuild to fire an event when the network is set up.
    • [A] (Adjustable): Requires Adjustable to add or remove pieces of the network via Events.
    • [T] (TriggerEvent): Requires TriggerEvent to create new externally-fired Events.
    • [P] (PerformEvent): Requires PerformEvent to trigger IO actions via Events.
  4. How to hack on the Reflex source code

    develop

    If you are working within a Reflex Platform checkout and want to modify the Reflex source code, follow these steps:

    1. Checkout the reflex source code into your local overlay directory:
      ./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex
    2. Point that checkout at your fork to make changes.
    3. Use the ./try-reflex or ./scripts/work-on scripts to start a shell environment where you can test your changes.
    ./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex
  5. Explore Reflex ecosystems and resources

    develop

    Reflex is part of a larger ecosystem for building interactive applications:

    • Reflex-DOM: A framework built on Reflex for developing web pages and highly-interactive single-page apps.
    • Obelisk: A 'batteries included' framework built on Reflex and Reflex-DOM for functional reactive web and mobile applications.
    • Reflex Platform: The recommended way to get started with Reflex development.

    For tutorials, documentation, and examples, visit the official website.

  6. Initialize the FRP Network with PostBuild

    develop

    To perform actions immediately after the FRP network has been successfully constructed and started, use the getPostBuild function. This returns a one-shot Event that fires once.

    Requires the PostBuild typeclass [B].

    -- [B] getPostBuild :: m (Event ())
    
    -- Usage pattern:
    -- e <- getPostBuild
    -- performEvent_ (print "Network is up!" <$ e)
  7. Manage dynamic collections with list functions

    develop

    Reflex provides several functions to transform a Dynamic collection (like a Map or a List) into a collection of dynamically-changing widgets or values. These functions handle the lifecycle of the elements as the underlying collection changes.

    • listWithKey: Turns a Dynamic (Map k v) into a Dynamic (Map k a) by applying a constructor function to each key and its corresponding Dynamic v.
    • list: Similar to listWithKey, but the constructor function does not receive the key.
    • simpleList: A simplified version for Dynamic [v], where elements are processed without keys.
    • listViewWithKey: Specialized for widgets that return an Event a. It returns an Event (Map k a).
    • selectViewListWithKey_: Creates a set of widgets where one is selected based on a Dynamic k. It returns an Event k representing the current selection.
    • listWithKeyShallowDiff: Uses an initial Map and an Event (Map k (Maybe v)) to perform shallow updates rather than full re-renders.
    -- Turn a Dynamic key/value map into a set of dynamically-changing widgets.
    listWithKey :: Ord k => Dynamic (Map k v) -> (k -> Dynamic v -> m a) -> m (Dynamic (Map k a))
    
    -- Even simpler version where there are no keys and we just use a list.
    simpleList :: Dynamic [v] -> (Dynamic v -> m a) -> m (Dynamic [a])
  8. Perform side-effects (I/O) in an FRP network

    develop

    To connect your FRP network to the real world, use the performEvent family of functions. These allow you to execute side-effecting actions in response to events.

    • performEvent: Runs side-effecting actions in an Event. The returned Event contains the results of those actions. Requires a PerformEvent t m constraint.
    • performEvent_: Runs side-effects but does not return an Event containing results.
    • performEventAsync: Used for asynchronous actions. You provide a callback to the action, which the action uses to send its return value back into the network.
    -- Run side-effecting actions in Event when it occurs; returned Event contains results.
    performEvent :: Event (Performable m a) -> m (Event a)
    
    -- Just run side-effects; no return Event
    performEvent_ :: Event (Performable m ()) -> m ()
    
    -- Actions run asynchronously; actions are given a callback to send return values
    performEventAsync :: Event ((a -> IO ()) -> Performable m ()) -> m (Event a)
  9. Create and Transform Behaviors

    develop

    Behaviors represent continuous values over time. They always have a current value.

    Creation

    • constant: A behavior that never changes.
    • current: Extract the Behavior part of a Dynamic.
    • hold: Create a Behavior that updates its value whenever a specific Event fires. Requires MonadHold [H].

    Transformation

    • fmap, ffor, <*>: Standard applicative/functor transformations.
    • sample: Retrieve the current value of a Behavior in a monadic context. Requires MonadSample [S].
    • pull: Create a behavior from a monadic action. Requires MonadSample [S].
    -- [H] hold :: a -> Event a -> m (Behavior a)
    -- [S] sample :: Behavior a -> m a
  10. Work with time and delays

    develop

    Reflex provides primitives for handling time-based events:

    • tickLossy: Creates an Event that fires at a given interval based on a starting UTCTime. It is 'lossy', meaning if the system cannot keep up with the interval, some ticks may be skipped.
    • delay: Delays the occurrences of an existing Event by a specified NominalDiffTime (seconds).
    -- Create Event at given interval with given basis time.
    tickLossy :: NominalDiffTime -> UTCTime -> m (Event t TickInfo)
    
    -- Delay an Event's occurrences by a given amount in seconds.
    delay :: NominalDiffTime -> Event t a -> m (Event t a)
  11. Manage dynamic networks and sub-networks

    develop

    When building complex applications, you may need to create, destroy, or switch between entire sub-networks dynamically.

    • runWithReplace: Takes an initial value and an Event of replacement actions. It returns the current value and an Event of the new values.
    • networkView: Given a Dynamic of network-creating actions, it creates a network that is recreated whenever the Dynamic updates. This is useful for switching between different UI views or logic sets.
    • networkHold: Given an initial network and an Event of new network-creating actions, it returns a Dynamic representing the current network. The network is recreated whenever the Event fires.
    • untilReady: Renders a placeholder network (m a) to be shown while a primary network (m b) is being built. It returns the placeholder result and an Event indicating when the primary network is ready.
    -- Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
    networkView :: Dynamic (m a) -> m (Event a)
    
    -- Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the Event fires.
    networkHold :: m a -> Event (m a) -> m (Dynamic a)
    
    -- Render a placeholder network to be shown while another network is not yet done building
    untilReady :: m a -> m b -> m (a, Event b)
  12. Transform and Combine Events

    develop

    Events represent discrete occurrences in time. You can transform them using standard functional patterns or combine them with other Events and Behaviors.

    Transformation

    • fmap, ffor, <$: Standard mapping functions.
    • fmapMaybe, fforMaybe: Map and filter out Nothing values.
    • ffilter: Filter events based on a predicate.
    • updated: Extract an Event from a Dynamic (fires whenever the Dynamic changes).

    Sampling Behaviors and Dynamics

    Use these to capture the state of a Behavior or Dynamic at the moment an Event fires:

    • gate: Only allows an Event to pass if a Behavior Bool is True.
    • tag: Attach the current value of a Behavior to an Event.
    • attach: Combine an Event with the current value of a Behavior into a tuple (a, b).
    • attachPromptlyDyn: Similar to attach, but uses a Dynamic and uses the most recent value immediately.

    Combining Multiple Events

    • leftmost: Returns the first event from a list that fires.
    • mergeWith: Combines multiple events of the same type using a combining function.
    • align: Combines two events into a These a b structure (handling simultaneous firings).
    • difference: Returns an event that fires when the first event fires, but only if the second does not.
    -- Example: Tagging an event with a behavior value
    -- [ ] tag :: Behavior a -> Event b -> Event a
    
    -- Example: Filtering an event
    -- [ ] ffilter :: (a -> Bool) -> Event a -> Event a