Switching

Posted on September 29, 2017

Previously we looked at how to use reflex-dom to put DOM elements on a page. Now we’re going to look at how to modify our FRP network in response to user input.

What is switching?

Everything we’ve done so far has involved building up an FRP network that is static. The graph of dependencies between Events, Behaviors and Dynamics is set up in a particular configuration and it stays in that configuration for the lifetime of the application. The same is true of our DOM elements - once we have laid them out on the page, they are there forever.

Sometimes we will want to modify the FRP network or the DOM in response to Events, and that is what the various switching functions do.

There are two kinds of switching functions available to us:

  • functions that modify the FRP network in response to input
  • functions that modify the DOM in response to input

We’ll cover these one at a time.

Higher-order FRP

When we want to modify an FRP network, we do it using higher-order FRP.

We’ve probably seen higher-order functions, which is where function which take functions as arguments, like:

or

Higher-order FRP involves an FRP type - one of Event, Behavior or Dynamic - which contains another FRP type.

Some examples turn up due to the fact that Behavior and Dynamic have Monad instances, giving rise to:

and:

There are other functions provided by reflex that enable higher-order FRP, including:

It can be handy to think of a railroad switch while you’re getting use to these methods.

We’ll focus on switch for a while to motivate higher-order FRP:

We can view a Behavior t (Event t a) as an Event t a which is varying over time. There could be multiple sources of these Events, and the Behavior is being used to track which Event should be used at any given moment in time. Since Behaviors have values at all points in time, that means there is always an Event which is selected. The switch function is giving us access to the Event that is selected by the Behavior.

This time, we’re going to look at the example before we look at the code:

Have a click around until you’re comfortable that you know what is going on.

We’re going to zoom in on the function that creates the Event that is used as the input to the counter on the left-hand side.

It takes in an Event for the “Add” button and both “Select” buttons.

We’re going to create a Behavior t (Event t ()) so that we can use switch to get the Event that we want out of this:

We build up our Behavior using hold:

which you might recall has this type signature:

Since we’re calling switch like we get an Event t () out of it, this leads to a ~ Event t () in the above, which translates to:

for this particular use of hold.

We’ll make a note of that:

The “Add” button starts off being routed to the left input, so we’ll use that as our initial value for our Behavior:

The Behavior is going to be changed whenever either of the “Select” buttons is pressed. We use leftmost and the Events coming from the “Select” buttons to start building up the other input to hold. This leaves us with two things to fill in, but we know their types:

We also know that we want the input from the “Add” button to flow through to the output when the output on the left-hand side is selected, and that we want no input to flow through to the output when the output on the right-hand side is selected. We have those things at hand:

and we are done.

Switching the “Add” clicks towards the output on the right-hand side is very similar:

We can do this a little more directly using switchHold:

The first argument is the initial Event to use as the output. The second argument consists of an outer Event and an inner Event. The outer Event fires to indicate that the output should switch. It switches to the value of the inner Event until such time that the outer Event fires again.

This results in:

and

The resulting widget still behaves the same way:

If you’ve been paying close attention, you might be wondering if we could have just used gate or ffilter to do something like this. For this example, you certainly could.

The benefits of modifying the FRP network start to come into play once pieces of your network start to consume significant amounts of processing time or memory. In those cases, you can use switching to only add those pieces to your FRP network when you need them and to remove them once you no longer need them.

If some piece of the network becomes entirely disconnected from the rest of the network then it is eligible for garbage collection, and so you’ll be able to reclaim the memory it was using and rest easy knowing that it isn’t hanging around and using up your processing time.

Dynamic modifications to the DOM

Imagine that we have three widgets that all return an Event t Text.

We have a text widget which fires its Event when the user types:
a button widget which fires its Event when the user clicks the buttton:
and a tick widget with fires its Event every second and doesn’t care about the user at all:

We’d like to switch between displaying these widgets and collect the Event t Text from whichever widget is being displayed.

Faking it by hiding elements

Imagine that we are very keen on premature optimization, and we know that modifying the structure of the DOM is slow but modifying attributes on the DOM is fast.

We could solve this problem by hiding elements and gating their outputs based on whether or not they are displayed.

We start by putting a button on the page:

and we track how many times it has been toggled:

This is using a handy helper function from reflex in passing:

to toggle a Bool when an Event fires.

We also set up a Dynamic with the negation of that Bool, because it will be handy in a moment:

We write a helper function to toggle the “hidden” attribute based on a Bool:

which we use to create a pair of Dynamic attribute maps:

Now we can wrap our widgets in divs that will be hidden or shown based on how many times the button has been pressed:

We need to gate the outputs so that only Events from the currently displayed widget flow through:

That output gets turned into a Dynamic so that we can display it, which we clear whenever the “Switch” button is pressed:

after which we put it on the page:

Here it is in one piece:

If we click around with this:

we’ll see that we’re definitely not adding or removing elements from the DOM, because the state of the text input is being maintained across clicks of the “Switch” button.

This is even more pronounced if we use the timer widget in place of the button widget:

Switching out elements

If want to have freshly laid out widgets every time we click the “Switch” button, we need some new functions.

The first of these is widgetHold:

The first argument is the initial widget to lay out on the page. The second argument is an Event with the next widget to lay out on the page as its value. The values that these widgets return are collected into a Dynamic. We’ll see why you would want this in a moment.

The MonadAdjust typeclass is present here so that we can replace pieces of the FRP network.

You might wonder how we managed without this for the switching functions at the beginning of this post. If we think of an FRP network as a graph, the earlier switch function were moving edges around between nodes. We could completely remove a piece of the graph - with help from the garbage collector - if nothing is connected to it and if we know that nothing will ever be connected to it again, but that is the only way we could effect the nodes via switching. For all other cases where we want to add or remove nodes, we need the MonadAdjust typeclass to place and connect new nodes when certain Events fire.

In the same way that we have never for when we need an Event which doesn’t fire, we have blank for when we need a widget that doesn’t display on the page. Sometimes that is useful as an initial value for widgetHold, but not always.

With all of that out of the way, let us have a look at how we might use widgetHold.

We start with the same button and toggling Dynamic that we used for hideWidget:

To use widgetHold we’re going to need Events that trigger when we want to change widgets, so we set some up:

Now that we have the pieces in place, we use widgetHold to put textWidget on the page, and to switch between the two widgets depending on how many times the “Switch” button has been pressed:

This gives us a Dynamic t (Event t Text), and we want an Event t Text.

There is a function with this signature:

In our case, we use it to pull out an Event t Text:

At that point we have what we need to display the output on the page as before:

All together it looks like:

We can see that we are getting freshly laid out DOM elements every time we press “Switch” by playing with this:
and with this:

These are small examples, but the idea gets more useful as you do more adventurous things.

If we don’t know what we want to use as an initial value for widgetHold, we can use dyn:

although widgetHold is probably a better bet if you have a choice between the two.

To use it, we would start with something that should look very familiar:

We’ll build a Dynamic of widgets that return Event t Text:

and we’ll use dyn to collect the outptuts into an Event t (Event t Text):

We can collapse these to an Event t Text using switchHold, using never as the inital Event:

and then we proceed as normal:

All in one place it looks like this:

Using Workflow

We can use a handy piece of functionality to clean this up a little. It might look scary at first glance, but we’ll get used to it pretty quickly.

A Workflow is a newtype used to build a graph of widgets that the user will transition through. It’s the kind of thing you would reach for if you were building a “wizard” in a UI, but it is much more flexible than that.

The newtype wraps a widget that returns a pair, containing the result we are interested in and an Event which will fire with the next piece of the Workflow we want to visit:

Once we have that assembled, we can run it with the workflow function:

and it will give us a Dynamic that collects the changing result values as the user interacts with the workflow.

An example will help.

We set up a “Switch button”:

and then we start creating pieces of the workflow.

The first piece will lay out the textWidget on the page, and will transition to the second piece of the workflow when “Switch” is pressed:

The second piece will lay out the tickWidget on the page, and will transition to the first piece of the workflow when “Switch” is pressed:

We don’t have to worry about rigging up a toggle Event and keeping everything synchronized, we just set up the graph for the user to navigate.

We start the user on the first piece of the workflow:

which gives us a Dynamic t (Event t Text), and we know what to do with that:

All in one place it looks like:

This gives us the same behavior as we had previously:

We can use this to switch between our various widgets in a cycle:

or we can give each widget it’s own “Next” and “Back” buttons to arrange them more like a traditional wizard:

Playing along at home

If you want to test out your understanding of how switching works, there are exercises coming soon. These exercises build up incrementally as the series progresses, so it would probably best to start the exercises beginning at the start of the series.

Next up

In the next post we’ll look at how we break things up into components in reflex, and the various design tradeoffs that are involved with that.

We’re preparing educational materials about the reflex library, and using it to see what exciting things we can do with FRP.

> Dave Laing

Dave is a programmer working at the Queensland Functional Programming Lab.