Flare
Flare is a special-purpose UI library for Purescript. The idea is to define the user interface and the logic of the program at the same time. On the one hand this approach somewhat limits the number of use cases, but on the other hand this allows for a remarkable expressiveness.
A first example
Imagine you want to build a really simple web application that calculates the product a × b for two numbers a and b which can be entered by the user. Using Flare, we define the user interface and the program logic in a single expression:
26 lift2 (*) (int "a" 6) (int "b" 7)
This code generates the following reactive web-interface1:
How does this work? Flare defines the function int
that takes two arguments: a label and an initial value2. An expression
like int "a" 6
serves two purposes. It creates the input field with the label "a" and initial value 6,
and at the same time it serves as a placeholder for its current value in
the surrounding expression. If the user changes one of the input fields, the whole
expression is re-evaluated.
Pure functions
In functional programming languages, programs are built from small,
composable pieces that can be indivually tested. Suppose we have
a pure function greet
, that takes a string
(a name) and returns a string (a greeting with a fancy version of
the name):
13 greet :: String -> String 14 greet name = "Hello, " <> fancy name <> "!" 15 where fancy = toUpper >>> split "" >>> joinWith "."
To turn this into a web interface where users can enter their name,
we first need an appropriate Flare component that contains
a string. The function string
creates
a simple text field from a given label and default value. Then, all we have to
do is to apply our function to the value inside the Flare component. We
can do so by mapping the function greet
over the
component:
29 map greet (string "Your name:" "Nemo")
The result is the following small web application:
The usage of map
here is similar to mapping functions over lists, except
that the function is not applied to every element of a list, but rather to the
stream of values inside the text field (technically, this means that a Flare component is a functor).
Multiple arguments
If our pure function takes more than one argument, we can not use map
to apply it to the values inside several Flare components. This is where applicative functors come into play: they allow us to lift functions of several
arguments into the realm of the functor (the Flare component). As an example, consider the following
function that takes two parameters, hue and radius, and returns a representation
of a small colored circle:
17 coloredCircle :: Number -> Number -> Drawing 18 coloredCircle hue radius = 19 filled (fillColor (hsl hue 0.8 0.4)) (circle 50.0 50.0 radius)
Note that this is a pure function without any side effects. Again, we can easily turn this into a web interface by creating two Flare
components of the appropriate type for the two parameters. Here, we will use
numberSlider label min max step default
. Finally, the
higher order function lift2
takes our two-argument function
and applies it to the current values inside the two components:
32 lift2 coloredCircle (numberSlider "Hue" 0.0 360.0 1.0 140.0) 33 (numberSlider "Radius" 2.0 45.0 0.1 25.0)
This results in the following interface:
Integration with other signals
The Flare library is built on top of Purescripts Signal library which is inspired by the corresponding module in Elm. In fact, a Flare is just a Signal with a collection of input fields. In this section, we demonstrate how Flare works together with other types of Signals. As a showcase, we want to integrate time to create a small animation:
Again, we start with a pure function. It takes three parameters, the number of leaves, a boolean flag which enables or disables the shadow, and a rotation angle:
28 plot :: Int -> Boolean -> Number -> Drawing plot nLeaves shadow phi0 = ...
Similar to before, we create a slider for the first argument. Note
that we have replaced numberSlider
by intSlider
(type safety first).
For the second argument, we use the function boolean
which creates a checkbox that returns either true
or false
.
Finally, for the third argument we want to have a time-dependent rotation angle. We will
use the animationFrame
Signal which yields the
current time on every redraw of the frame in the browser.
We can use the Signal in our expression for the Flare UI, by using
the function lift
:
41 lift3 plot (intSlider "Leaves" 2 10 6) 42 (boolean "Shadow" false) 43 (lift animationFrame)
With three lines of code, we have turned our static plot
function into an interactive animation.
Stateful interfaces
So far, our examples did not include any notion of state.
The output was simply given as a function of the current input values.
To incorporate state, Flare uses the function foldp
that
works analogous to the corresponding function for signals. The p
in foldp
stands for past and indicates that
the output can now depend on past values of the input fields:
51 foldp (+) 0 (button "Increment" 0 1)
Here, button
is a simple Flare component that returns the first value 0
in its default state and the second value 1
when it is pressed. This allows us
to build a simple counter:
Notice how foldp
uses the internal accumulator to sum the time-varying stream of zeros and ones.
HTML output
We have seen simple text outputs and canvas graphics, but often we will have HTML as a target output. Flare provides an interface to Purescripts Smolder library that offers a nice way to create HTML markup:
82 table :: Int -> Int -> H.Markup 83 table h w = H.table $ foldMap row (0 .. h) 84 where row i = H.tr $ foldMap (cell i) (0 .. w) 85 cell i j = H.td (H.text (show i <> "," <> show j))
We turn this pure function into a Flare UI in the usual way...
60 lift2 table (intSlider "Height" 0 9 5) (intSlider "Width" 0 9 5)
... to create our last example:
Read on
Additional reading material:
- More Flare examples
- Flare on Github
- Quick start project: temperature conversion (live demo)
- Flare documentation on Pursuit
- The full source code for this page
- typed-spreadsheet: The Haskell library that inspired Flare
- The Purescript programming language
- Purescript Signal library
- Purescript Drawing library
- Purescript Smolder library
- Applicative functors
- Factorization diagrams (uses Flare)
- My personal website
Footnotes
1 The layout can be easily changed via CSS. We
have also left out some "boilerplate" code that is always the same throughout this article:
a few lines of Purescript and two lines of HTML.
2 In Purescript (or Haskell), function application f(x, y) is simply written as f x y
.