Headline

Reflex is a Script:Functional Reactive Programming library in Haskell

Reflex

http://hackage.haskell.org/package/reflex: Reflex is a haskell library for functional reactive programming in haskell.

Reflex provides the abstractions described in Script:Functional Reactive Programming (behaviors and events), functions to work with them, and an additional abstraction called Dynamics. For our intents and purposes, we’ll consider dynamics to be the same as behaviors, since discussing the details of it would be out of the scope of this tutorial(2).

(2) In a very short note: Reflex is implemented with push-pull evaluation, and thus often requires explicit push notifications to update things like the DOM. Event push notifications when they occur, but Behaviors don’t push anything. A Dynamic is a combination of an Event and Behavior, meaning it’s a continuous time-varying value that pushes notification whenever it changes, and fills the spaces where we need a behaviour and to know when the value changes.

Building UIs with Reflex-Dom

To build a UI we’ll use reflex-dom which is a haskell library that provides a monadic interface for building a DOM-based UI with functional reactive programming abstractions from reflex.

A DOM user interface is defined with element tags. A webpage can have many elements such as divisions and text paragraphs.

<div>
    <p>This is a paragraph.</p> 
    <button>Button below the paragraph</button>
</div>

<div>
    <p>This is the second division.</p>
    <input></input>
</div>

Would define a webpage with two divisions (<div>). The first division has a paragraph (<p>) and a button (<button>), the second, a paragraph and and a text-box for input (<input>).

Reflex-dom defines the Widget monad(3) and multiple functions with which we can create our UI. However, some of these functions do more than build the UI: they return and accept events and behaviors! Let’s look at a few.

We can put text on the UI, without creating a new element. Notice how the returned Widget is a computation that builds the UI and the resulting value of the said computation has type ().

text :: Text -> Widget ()

The most basic element-creating function is el. It takes the name of the element as a string, a Widget computation, and returns another Widget computation. This still does nothing regarding FRP.

el :: Text -> Widget a -> Widget a

ex1 = el "p" (text "This is a paragraph")

would correspond to

<p>This is a paragraph</p>

ex2 = el "div" (el "p" (text "Other paragraph"))
would correspond to
<div>
  <p>This is a paragraph</p>
</div>

ex3 = el "div" $ do
        el "p" (text "First paragraph")
        el "p" (text "Second paragraph")
would correspond to
<div>
    <p>First paragraph</p>
    <p>Second paragraph</p>
<div>

A button can be clicked at some points in time. We can model a button click with an event! There is a convenient function that returns a Widget computation that creates a button and returns an event that occurs when the button is clicked. We’ll see ahead an example in which we this event.

button :: Text -> Widget (Event t ()) 

ex4 = el "div" $ do
        el "p" (text "Before button")
        clickEvt <- button "Click me!"
        el "p" (text "After button")

would correspond to

<div>
    <p>Before button</p>
    <button>Click me!</button>
    <p>After button</p>
<div>

An input text box has a continuous time-varying value: What the user has input in the box. We can model the value inside the input box with a behavior! As with the button, we already have a function that returns a Widget computation that creates an input box and which returns a behavior of the value in the text box.

input :: Widget (Dynamic Text)

ex5 = el "div" $ do
        inputBehavior <- input
        return ()

would correspond to

<div>
    <input></input>
<div>

Lastly, we have a function for displaying a behavior of a string. Whatever the value of the string is at the current time, is what’s displayed on the string. Non-surprisingly, this function is called dynText.

dynText :: Dynamic Text -> Widget ()

ex6 = el "div" $ do
        inputBehavior <- input
        el "p" (dynText inputBehavior)

what would this look like?

<div>
    <input></input>
    <p>???</p>
</div>
not so simple, would require javascript

Next we’ll look at some UI-independent combinators on events and behaviors, and an example of how we can use them in building a more complex UI.

(3) In fact, Widget x is the monad (Widget :: * -> * -> *), where x is a type parameter to guarantee contexts don’t get mixed.

Reflex Combinators

In this section we describe some reflex combinators for manipulating events and behaviors. Remember that for our purposes we’ll consider dynamics to be the same as behaviors but with a different name.

We can create a behavior which is constant over all time, given the constant value.

constDyn :: a -> Dynamic a

d1 = constDyn 2 -- is 2 at all points in time

We can create a behavior that changes to the value of an event every time said event occurs, provided an initial value for the behavior to have before the first occurrence of the event is given.

holdDyn :: a -> Event a -> Widget (Dynamic a)

d2 = holdDyn 'A' keyPress -- until the first event occurrence is 'A', and then it's the value of the event occurrence
    where keyPress :: Event Char

We can create a behavior that changes everytime an event occurs, provided the function to fold the event value with the current value, and an initial value. This is somewhat similar to a list fold, but it’s a actually a fold across time.

foldDyn :: (a -> b -> b) -> b -> Event a -> m (Dynamic b)

-- Note: foldr :: (a -> b -> b) -> b -> [a] -> b

As an example, we can now define an application that displays a button and the number of times said button has been clicked (remember how behaviors instance Functor)

app = el "div" $ do
        clickEvt <- button "Click me!"
        clickAmount <- foldDyn (\_ acc -> acc+1) 0 clickEvt
        el "p" (dynText (displayAmt <$> clickAmount))
    where
        displayAmt x = "Clicked " <> showT x <> " times."

-- Note how length for normal functions can be defined as
length ls = foldr (\_ acc -> acc+1) 0 ls

This might be a bit confusing so we’ll walk through each value.

  • clickEvt has type Event (), representing the occurrences of a button click through time
  • clickAmount has type Dynamic Int. We start with the value 0, and when the Event () occurs, we apply the lambda of type () -> Int -> Int which ignores the first argument and adds +1 to the existing counter.
  • displayAmt <$> clickAmount has type Dynamic Text, meaning we can display it with dynText. Note that displayAmt :: Int -> Text, and we fmap it over Dynamic Int (hence why we get Dynamic Text).
If you wrote something similar to this code, you’d see an application with a button and a text paragraph with a counter. Every time you click the button the counter will increase.

Lastly, we have a function that allows us to sample the value of a behavior every time an event occurs.

tagPromptlyDyn :: Dynamic a -> Event b -> Event a

-- e1 will occur with the value of the input box every time the button is clicked
e1 :: Event Text
e1 = do
    click <- button "Sample input box value"
    valBehv <- input
    return (tagPromptlyDyn valBehv click)

In Contribution:haskellReflex you can see this full application for a complete example of using Reflex.


romes edited this article at Wed, 21 Sep 2022 10:51:50 +0200
Compare revisions Compare revisions

User contributions

    This user never has never made submissions.

    User edits

    Syntax for editing wiki

    For you are available next options:

    will make text bold.

    will make text italic.

    will make text underlined.

    will make text striked.

    will allow you to paste code headline into the page.

    will allow you to link into the page.

    will allow you to paste code with syntax highlight into the page. You will need to define used programming language.

    will allow you to paste image into the page.

    is list with bullets.

    is list with numbers.

    will allow your to insert slideshare presentation into the page. You need to copy link to presentation and insert it as parameter in this tag.

    will allow your to insert youtube video into the page. You need to copy link to youtube page with video and insert it as parameter in this tag.

    will allow your to insert code snippets from @worker.

    Syntax for editing wiki

    For you are available next options:

    will make text bold.

    will make text italic.

    will make text underlined.

    will make text striked.

    will allow you to paste code headline into the page.

    will allow you to link into the page.

    will allow you to paste code with syntax highlight into the page. You will need to define used programming language.

    will allow you to paste image into the page.

    is list with bullets.

    is list with numbers.

    will allow your to insert slideshare presentation into the page. You need to copy link to presentation and insert it as parameter in this tag.

    will allow your to insert youtube video into the page. You need to copy link to youtube page with video and insert it as parameter in this tag.

    will allow your to insert code snippets from @worker.