Chapter 9. Advanced FRP and Best Practices

In the previous chapter, we introduced a 360-degree description of FRP. We discussed the main features related to this type of programming, passing from a theoretical introduction to the concrete examples of the scenarios. We also described how the Event module of the F# language exposes functionalities to manage the Event type delegate in the .NET Framework and how this module amplifies its capabilities, adding also a set of functionalities to manage events, such as collection (the IObservable interface). Finally, we conclude the chapter showing the main differences between push-based and pull-based scenarios.

FRP is not a simple subject to understand; in fact, it is frequently considered very difficult to assimilate. This problem depends on the complexity of the concepts; anyway, once you understand their meaning, it is not so straightforward to put them into practice. In other words, the only relatively simple aspect is the presence of objects that can create a more complete description of FRP.

This chapter aims to examine in depth the previous introduction of FRP, implementing the description with other information in order to have a general and complete overview of FRP.

In particular, we will discuss the following:

  • Differences between the discrete and continuous components with related examples of scenarios
  • Concepts of time flow and dynamic change, in detail, through a synchronous data flow
  • Interoperability between the F# observable and FRP from a comparison to an example in the .NET multiparadigm

Discrete and continuous components

Discrete and continuous components are fundamental to better understand FRP. From the first introduction, one of the main axes is exactly the distinction between discrete and continuous. In the previous chapter, we analyzed many aspects related to these two components, but we never mentioned them. If we had so, then the argument could have been too complicated and theoretical.

By definition, discrete and continuous components can be described as the two main characteristics which FRP provides. In other words, they are the basics to apply time flow in our application or to improve our scenario of use.

We previously discussed the execution of the following function:

List.map (fun x -> x ** x) 

While doing so, we introduced the concept of time flow to underline that every execution of the mathematical function power occurs step by step.

In the example, it seems that this scenario is continuous, because for every input value of the list, a new output value is generated.

Actually, with a more in-depth analysis, List.map could be seen as a hybrid scenario. In fact, the exponentiation of every single value shall be made according to the order of the elements, which is a prerogative of discrete scenarios.

As a consequence, we will obtain combinators made up of behavior and Event.

Tip

Most of the concrete applications of FRP adopt a hybrid scenario. This is because, given a time flow, it is highly probable using both the time-varying and time-ordered implementations.

In the next sections, we will discuss in more detail about discrete components, while continuous components will be treated later.

Discrete components

The Event module is the basis of discrete components. They are the milestones of FRP in an event-driven context.

In the previous chapter, we introduced the concept of signals.

Note

Events are a collection of particular signals that can be separated from one another as discrete occurrences.

In other words, each event is able to modify a reactive system independently from other signals. We will consider the following graphic:

Discrete components

Each orange arrow shows the occurrence of an event. We can easily understand that it is impossible to determine when an occurrence happens between a signal and the next one.

In a discrete component, events are considered as a set and they are called Event Stream. This event flow is nothing but the scenario called push-based in the previous chapter:

open System.Windows.Forms 
let form = new Form(Text = "F# Windows Form", 
                    Visible = true, 
                    TopMost = true) 
form.MouseMove 
    |> Event.filter ( fun evArgs ->  
        evArgs.X > 100 && evArgs.Y > 100 &&          evArgs.X < 255 && evArgs.Y < 255)  
    |> Event.add ( fun evArgs -> 
        form.BackColor <- System.Drawing.Color.FromArgb( 
            evArgs.X, evArgs.Y, evArgs.X ^^^ evArgs.Y)) 

It is interesting to notice that we can decide how to act once the occurrence happened. We can't stop the event, but only filter or ignore it.

Note

This leads us to question ourselves that a discrete implementation is always possible in counting the number of occurrences, whether the count is finite or infinite.

For example, the graphical representation of the function y=x 2 is discrete if the axis domain is composed only of integers. As a result, we will obtain a set of separated points, but in a countable range.

In the next example, the Charting library using NuGet is used to represent data. Notice that it is necessary to install the library if you want to run the code and pay attention to the current version of the library.

We will start with the following F# code:

#r @"../packages/FSharp.Charting.0.90.14/lib/net40/FSharp.Charting.dll" 
#load @"..packagesFSharp.Charting.0.90.14FSharp.Charting.fsx" 
 
open System 
open System.Drawing 
open System.Windows.Forms 
open FSharp.Charting 
 
let list = [for x in -10 .. 10 -> (x, x * x)] 
Chart.Point(list,Name="Integer") 

We will obtain this graphic:

Discrete components

To sum up, a discrete component has the following features:

  • Event-driven (or data-driven) context
  • Push-based scenario
  • Events are streams of event occurrences
  • Time domain is of the type discrete
  • We can always count the number of signals

The discrete event example with the discriminated union

It is very important to remember that, when we discuss about events in F#, we are dealing with the type Event of the module in the assembly Fsharp.Core.dll with the namespace Microsoft.FSharp.Control, not with DelegateEvent of the .NET framework.

In fact, this particular type of events offers many more functionalities, as previously seen.

When we presented an example of code related to the events, we confined ourselves to filter the stream of occurrences by using the position of the mouse. However, thanks to F#, now it is possible to apply more powerful flow controls:

open System 
open System.Windows.Forms 
 
let form = new Form(Text = "F# Windows Form", 
                    Visible = true, 
                    TopMost = true) 
form.MouseMove 
    |> Event.choose(fun evArgs -> 
        match evArgs.Button with 
            | MouseButtons.None -> None 
            | _ -> Some( evArgs.X, evArgs.Y)) 
 
    |> Event.filter (fun (x, y) ->  
        x > 0 && y > 0 && x < 255 && y < 255)  
 
    |> Event.add (fun (x, y) -> 
        form.BackColor <- System.Drawing.Color.FromArgb( 
            x, y, x ^^^ y)) 

The Event.choose function is similar to Event.filter; however, it creates a selection of messages from the original event, so it creates a new message.

Notice that through Pattern Matching and Built-In Discriminate union (option-choice), we can pass values with different types using Some and we can also stop the execution using None when MouseButtons is in the status none.

Continuous components

Continuous components differ from discrete ones in many aspects. Anyway, together, they represent one of the main axes of FRP.

In this specific case, signals are called behavior and they change continuously over time. In other words, a continuous component is composed of a constant value (signals) that can become any value during the time flow in a set of definite or indefinite values.

The following graphic shows how the time flow of a behavior works over time:

Continuous components

Unlike a discrete scenario, there are no events in a continuous component, but a continuous change flow. Therefore, if a signal changes continuously, the calculation performed at each modification will be greater, since it will occur many more times.

When working with continuous components, we can find a latency state and the scenario will be different because it will be pull-based. Regardless of how many times signals change, every new state of these signals will be elaborated, almost exclusively, in an asynchronous context.

In a continuous implementation, it is not possible to count the number of occurences (changes), but every change will certainly be elaborated.

We can see the mathematical example and the related F# code again.

If we consider the function y= x2 and we move the axis domain from integer to real, we will obtain a continuous line, not a set of different points. This will happen because the scenario will move from discrete to continuous, since any value will be included in the set of real numbers.

As a consequence, the representative F# code will be as follows:

#r @"../packages/FSharp.Charting.0.90.14/lib/net40/FSharp.Charting.dll" 
#load @"..packagesFSharp.Charting.0.90.14FSharp.Charting.fsx" 
 
open System 
open System.Drawing 
open System.Windows.Forms 
open FSharp.Charting 
 
let list = [for x in -10.0 .. 10.0 -> (x, x ** 2.0)] 
Chart.Line(list,Name="Float") 

We will get the following graphical output:

Continuous components

In conclusion, a continuous component has the following features:

  • Demand-driven context
  • As a consequence, the scenario will be pull-based
  • Behaviors are indefinite occurrences of the change of scenario
  • Time domain is of the type continuous
  • There is a reactive system
  • Often, we cannot count the number of occurred changes, but we know that they all are certainly processed

Changing continuous value and event stream

In F#, if we want to represent a discrete scenario, we have the module Event at our disposal, which expresses the Event Stream concept in detail.

However, in a continuous component, we do not find the type Event; in fact, it is more correct to talk about values and constants.

A good example of behavior is any object Time that continuously changes values over time. This modification can be negative or positive, but it certainly has different values in two different moments of the time flow.

It is not easy to find other cases in which it is simple to imagine an object that changes over time. Actually, in the example of the code used in the previous chapter, there is a kind of value that frequently changes:

form.MouseMove 
    |> Event.filter( fun evArgs ->  
        evArgs.X > 0 && evArgs.Y > 0 &&  
        evArgs.X < 255 && evArgs.Y < 255)  

We have already defined a value that changes over time. The example shows how the position of the mouse (struct point) updates at every movement of the mouse with the value of the position of the absolute coordinates in the application window.

This is another good example of behavior. So, why did we previously consider it as an example of Event Stream?

In FRP, Event and behavior, such as discrete and continuous, are conceptually similar and together they represent one of the main fundamentals.

By definition, we can state that:

Event a ≈ Behavior (Maybe a) => Signals

Symbol means circa or approximately the same.

Now that we understand the main object of FRP, we can go further and analyze the common use scenario that includes both the concepts discussed up to this point.

Hybrid system

The use of the signals is almost always essential in cases in which the system has to respond in realtime. Usually, these scenarios have as their objective the need to respond to incentives and they have to occur in a definite period of time. As a consequence, an essential aspect that comes to light is the fact that, in a reactive system, it's very important to consider all the costs related to execution time and used memory. Imagine how much it could cost to not analyze these aspects in depth. It might result in memory leak or infinite cycles.

When referring to hybrid system or simply the Real Time FRP (RT-FRP) in FRP, a reactive system is made up mainly of two parts:

  • The Reactive part, which contains everything that has to be technically developed, such as FRP
  • A base language part, developed in any language and paradigm of programming on which it is always possible to stop the execution and use definite resources

Scenarios where it is possible to apply RT-FRP and create a hybrid system are numerous, for example, animations and Graphical User Interface (GUI) seen in the code in the previous chapter.

Otherwise, it is very interesting, considering the application in robotics where responding to incentives in a reactive way is fundamental and it is important to accurately manage all data and the memory used.

Moreover, it is also important to notice that the evolution of technology in the last few years, such as the Cloud and Internet of Things (IoT), opens the way for new possibilities concerning the application of FRP and also the reactive system, or more aptly called the hybrid system, in particular. In fact, thanks to FRP and F#, it is possible to simplify the development of technologies by using the lambda architecture, composite functions, and actors.

In the following section, we will explain in detail how to interact with and integrate RT-FRP with some examples.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.76.164