Mental Reactive Programming Models for Supervisors


This article is intended for a wide range of readers who would like to know what Reactive Programming is. The purpose of this article is to formulate in you the basic Mental Models of Reactive Programming (MM RP) without going into technical details.

Disclaimer
( ) — , . , .
, : , . , , .
.

But first, let’s explain what the mental models and superiors mentioned in the heading of the article have to do with it ...

About Mental Models
, , , . , .
, , (. [1], [2])
? , , . (), , , . , , «», «» « » .
, , (), , - ().

And here are the bosses ...
. «» «» , : . ( , «» , ).
, «» , , , , , . , . «» «». , , , .
, , , .., () — , , .
, .


Why does Reactive Programming need your project?


Many people unfamiliar with RP are initially skeptical of him, suspecting that this is just another empty fashion, covered in a couple of beautiful words. Especially when they learn that you can evaluate RP only by trying it. And to try it is expensive, because of the high entry threshold. We lived and lived with OOP, what is missing from it?
Let me introduce my point of view on this subject.
At the dawn of Programming, when most programs were written directly in assembly language, the main working concept (an element of the Mental Model) of programmers was an instruction, or a language command. Some (primitive) data is fed to the input of a command or instruction. The instruction processes and issues some output data. The appearance of the first procedural programming languages ​​such as Fortran did not change the essence of the matter. Just the data and the operations performed (as a sequence of elementary commands) have become more complicated.
Over time, it became clear that this concept is not very consistent with the realities of the world. There can be a lot of data, they can be difficult to structure. Both the data and the functionality around them would be nice to split into parts, to develop and maintain separately, and use together.
OOP solved these problems in many ways. The unit of the Mental Model of a typical OOP programmer is an object with data hidden (encapsulated) in it and an access interface to this data as a set of functions.
OOP has played a huge role in the automation and computerization of many manufacturing and other processes. And along with this, his weaknesses were exposed.
Unfortunately, in OOP there is no concept of a process as such.
They tried to improve the situation in different ways, concentrating on various aspects.
Thus, Event-Driven Programming [3], Dataflow-programming [4], Stream processing [5] and several other paradigms were born.
I would venture to arouse a stream of criticism of the adherents and experts on these paradigms, trying to convey in simple words their general essence.
One way or another, these paradigms operate with information flows. At the same time, Event-Driven Programming, as the name suggests, focuses on the process of the emergence of information flow elements, Dataflow programming - on flow control (splitting, merging, transformation of flows) and Stream processing on the optimal use of resources in processing flows.
Reactive Programming is about the same thing, but with a focus on the specific elementary operations of creating, managing and using threads. Those. RP describes how your system reacts (English react) to elements of the information flow. In this sense, it would be more correct in Russian to use the term “Reagent Programming” (from the word “react”) or “Reaction Programming” (from the word “reaction to something”) if it weren’t for the ear to cut, and the second did not cause incorrect associations.
I would venture to express another seditious thought. What we today call in English Reactive Programming (Reactive Programming). called so for historical reasons and inclined in favor of this term the majority opinion.
This paradigm could have been called differently. Therefore, do not focus on its current name, but try to understand its essence.
And although I will talk about RP at a fairly abstract level, I will cite the RxJS library APIs as concrete examples.
The acronym RxJS stands for Reactive Extension for JavaScript, a JavaScript extension for Reactive Programming features. Similar extensions exist for many other programming languages, as can be seen in the picture below, taken from [6].
Reactive programing extensions

Why do Mental Models of RP need your project


Big projects are not done alone. You can often read or hear that project participants must speak the same language. My experience shows that this is hardly necessary and possible. But what is needed is for the most basic concepts of the project to be stated and understood by the project participants as equally as possible. In terms of Mental Models (MM), we can say that upper-level MMs should be as similar as possible.
But how can they be similar if analysts think in terms of Workflow and Use Cases, architects in patterns, developers in functions and data structures, and testers in test scenarios?
I do not urge all these specialists to start thinking at the same time with the categories of Reactive Programming, but I can promise them that acquaintance with these categories will simplify and increase the effectiveness of their professional communication with colleagues. This should happen because, on the one hand, MM RPs have the power necessary to describe complex Workflows, and on the other hand, MM RPs can be directly converted to code in many programming languages.

Surprises, dangers, or that in RP is not the way we are all used to


But before we get into the description of what the Mental Models of Reactive Programming consist of, based on our own experience, I would like to warn the reader about what is not in them. Moreover, not just not, but the very expectation of simple and understandable OOP behavior in the world leads to sad consequences.
I’m doing this not to intimidate, but rather to intrigue the reader.

Difference 1: Instead of a cursor model, a computational graph


I’ll venture to suggest that many readers, when thinking over the next task to be realized, have a mental model in their head, which I call the cursor model. It assumes that a step-by-step algorithm in the form of a linear list of instructions will be invented to solve the problem. The execution of the algorithm is reduced to the step-by-step execution of the instructions one after another. You can imagine something like a pointer to the currently executing instruction. After the instruction is executed, the pointer (cursor) moves to the next instruction in the list and it starts to execute.
Within this model, a sequence of commands written in a conditional object-oriented programming language
1. 1 = 2
2. 2 = 3 
3. 3 = 1 + 2
4.  1, 2, 3
5. 1 = 4
6.  1, 2, 3

will give the result
2 3 5
4 3 5

Our cursor mental model perfectly predicts and explains such a result. After processing the third line, the value X3 is set and the new value for X1 specified in line 5 cannot change it.
In the world of RP, depending on the interpretation of the “+” operation, the result will most likely be this
2 3 5
4 3 7

In this world, most operations connect input parameters to each other, thereby creating computational graphs through which calculations are “pushed” when one or more parameters are changed.

Difference 2: Asynchronous operations


In the framework of the cursor mental model of calculations, the next operation cannot begin earlier than the previous one.
Consider the following example. Suppose that the function f1 calculates the base salary by the value of the user identifier userId, and the function f2 calculates the bonus based on userId and the value of the salary.
Then the calculation of the full salary may look like this
1. X = f1(userId)
2. Y = f2(userId, X)
 X, Y

Suppose a staff member has a base salary of 10,000. and a bonus of 1000 units.
Our cursor mental model tells you what to print.
10000 1000 

Alas, in the world of asynchronous RP, the result may, depending on the duration of operations, be
0 0 
10000 0 
0 1000 
10000 1000 

(I do not consider exceptions yet).
The thing is that in the asynchronous-reactive world, the next operation does not wait for the end of the previous, if it is previous) asynchronous.
To illustrate this, let's look at some important details using the realistic example shown in the figure below.
The picture shows the execution time of four instructions L1, L2, L3 and L4 that are independent of each other (their numbers are important to us, not the spelling) in synchronous (upper part of the picture) and asynchronous (lower part of the picture) modes.

As we see, in the first case, each subsequent instruction "waits" for the end of the previous one. In the asynchronous case, all instructions begin to be executed simultaneously. Due to the parallel execution and use of resources, most instructions run in asynchronous mode longer than in synchronous mode. However, together they will bequeath their work earlier.
The order of completion of the instructions in both modes is also very different. In synchronous it:
L1, L2, L3, l4
but in asynchronous:
L3, L2, L1, L4
.

Difference 3: Incomplete chains (without consumer) do not work at all


In many traditional programming languages, it is common to associate function calls or object properties with dots.
For example, the following JavaScript function call chain turns the word “good” into “dog”:
„good“.split(„“).reverse().join(„“).replace(„oo“, „o“);

Sequences (chains) can be long. For reasons of reuse or convenience, they can be cut into pieces and partially performed.
Splitting a chain in RP into two parts and calling only one of them usually leads to a lack of result, since only the full chain (with the consumer at the end) is performed.

Why all this?


Probably, many readers are already asking themselves the question, “Have they not gone crazy collectively, these reactive programmers? Why is it needed, such programming? ”
I do not presume to predict what the creators and experts of the Republic of Poland would answer this question, but my answer is this: such programming is necessary, because many objects of the real world behave just like that.
Computing graphs - this is what Excel is built on, from which not only accountants, but also project managers are delighted.
Asynchronous operations . When you make coffee or make tea in your kitchen, do you stand in the kitchen all this time and look at your coffee pot or teapot? No. The device boils water and does its job, while you are doing something else for now.
Complete chain of operations.Try unplugging your desk lamp from the wall outlet and pushing the switch. The lamp does not light up from this. This object works only if there is a complete chain - from the source to the consumer of electricity. And there are many, if not most, of such objects in the real world.

I want to reassure you, your knowledge of traditional programming and cursor MM should not be thrown into the trash due to the appearance of RP. Reactive programming left them alone and expanded them with new operations on new types of objects. How - we’ll talk about this later.

The Space of Mental Models Programming and the place of MM RP in it


Speaking about the place of RP in the general landscape of Programming, authors often mention two dimensions - the complexity of the processed objects and the synchronism / asynchrony of operations. An example of such a classification can be found in the book “RxJS in Action” [7], in the chapter “When and where to use RxJS”.
In this classification, the dimension of objects is divided into single objects and multi-objects: arrays, lists, etc. Operations are divided into synchronous and asynchronous.
Thus, this classification divides the programming world into four areas. RP is one of these areas responsible for processing multi-objects with asynchronous operations.
I find this classification very interesting, but I would like to look at it from the point of view of mental models. The table below presents them.
Single Values ​​and Objects, ,
, (Stream)
, (Promise)(Workflow)

We assume that the mental models of instructions and the cursor do not require further explanation.
The cycle is an extension of MM instructions and the cursor due to the additional instructions of the cycle or return to some point. This allows a set of processing instructions for a single object to “wrap” in a loop, and thereby process many such objects. In this case, the cursor moves inside the cycle as in the previous model, and having reached the transition point, it either jumps to the beginning or the processing of the cycle stops if all objects are processed.
Jet. The difference between this mental model and the previous one is that the cursor
pointing to the object being processed remains in place, and the objects themselves “run over” it.
Let's look at this with two examples. If you paint a wooden fence, you, by analogy with the cursor model, go from board to board. But the worker on the conveyor remains in place and, by analogy with the jet model, the parts to be processed themselves approach it. Such objects are often referred to by the term English Stream, for example, in the Java language.
Semaphore. This MM is easiest to associate with a traffic light at an intersection. Asynchronous objects periodically poll the state of a public variable and perform certain actions after changing its state. (like drivers in front of a traffic light)
Waiting.A suitable metaphor for this Mental Expectation Model is the letter on paper or Emall that you expected when you last got your job. There may be a positive or negative answer. Your behavior after receiving the letter very much depends on its content. The English term Promise is often used to describe these kinds of objects. That, from the point of view of the user is an expectation, for the contractor providing the services, it is rather a promise.
As we see from the description, movement along each dimension (from top to bottom or from right to left in the table) means a qualitative change in the Mental Model.
As can be seen from the table, the jets and expectations are neighbors on the left and on the top of the southeastern cell of interest to us. What is new in the Mental Models of Flows inhabiting the cell of interest to us compared to them?

What is the extension?


The expansion of Streams in comparison with Expectations is that the expected information can arrive not once, but in many portions. In this case, the process may end without ending. Those. after a series of successful servings, we will receive an error notification. In addition, another version of the information is added - a notification of the end of the process.
This means, for example, that it is possible to receive several (but not all) portions of the expected information and (without an error message) a message about the end of processing.
Recall again, with Waiting, we have only two alternative options for the resulting information.
The Mental Jet Model is well suited to comprehend, discuss, and implement the process of transforming a sequence of objects of the same type. MM Stream expands it with the following aspects:
  • There can be many jets and we can merge them together
  • The jets may be heterogeneous
  • We can split the jets into new ones according to different criteria
  • We can “close” and / or transform them into new ones within the framework of one stream.

So, we determined the place of MM RP (Streams) in the space or landscape of the objects of Programming. Let’s now lower a bird’s eye view and take a closer look at Streams and their Mental Models.

Streams and phases of their life cycle


As a first approximation, RP flows can be imagined as water flows in water pipes or electricity flows. It should be remembered that like any other analogy, this analogy has its limits and is not always applicable.
Speaking about the flow, the following important aspects can be distinguished:

  1. Each thread somehow arises
  2. He is somehow moving towards the consumer.
  3. Something happens on the way with him (he transforms)
  4. It can be split into several streams or merged with other streams
  5. The consumer somehow uses the flow, ceasing to exist.

The listed aspects are simultaneously phases of the life cycle of individual elements of the flow.
Let's consider them in more detail using the example of RxJS functions.

Thread creation


Streams can be created from passive elements such as an array or a list of objects in your program, bytes, file lines, etc. This kind of stream sources is called cold (although technically there is a more accurate definition of cold stream sources).
The so-called hot springs "live their own lives" and if you do not connect to them in time, the information will be lost. This category includes information about user actions on a computer, tablet, smartphone, for example, information about keystrokes, mouse movements or touching the screen. Also in this category are data requested by various protocols such as HTTP, data from various sensors.
It should be noted that there are so-called “warm” springs. In addition, some “hot” springs can be “cooled”, and “cold” can be “warmed up”. But you should read about this in special literature, for example, in the book [7].
It is important for us to know that all operations of creating flows create objects of the same type, which can be further processed by the same operations, regardless of content. In this article, we call these objects streams. The corresponding English name is Observable.

Consumer movement and flow transformation


Flow transformation operations can be carried out both on the way to the consumer and by himself. In both cases, the processing operations of the flow elements are strictly sequential, i.e. the next operation is launched strictly only after the previous one has completed and passed its result to it.
Unlike Stream, which in some programming languages ​​are language constructs with their own syntax and semantics, reactive extensions like RxJS in JavaScript are forced to use the syntax and basic semantics of the extensible language. Therefore, RxJs implements the pipe () function, inside which you can place calls to functions - handlers of both the stream itself and its individual elements.
It is important to note that only special, pipeable, functions can be flow handlers.

"Three-phase flow"


If we continue the analogy with electricity, then the flows we are considering can be called three-phase. Along with the “normal wire” transmitting the basic information, there is also a “error wire” and a “stream termination wire”. Transformation operations allow not only changing the object, but also redirecting it to another “wire”. This technique is used, for example, when processing alleged errors in working with servers using the HTTP protocol. For example, if one server does not respond, you can try to request another without informing the user about the failure to request the first server.
This is another very important element of your Mental Flow Model. If in traditional programming paradigms the error is returned from the processing function as an error code or should be intercepted as an interruption (exception), then in the flows the error "flows" independent of the main channel.
There it can be processed. For example, if a user has entered an incorrect password, they may be given an additional opportunity to try to enter it one or more times.

Splitting and merging flows


The splitting of flows is carried out in two stages. At the first stage, empty threads are started. Then, in the second stage (stream processing stage), in one of the processing functions, the elements will be analyzed and redirected to the desired stream. Technically, there are many options for how to do this. For example, removing it from the current thread or cloning it before starting it in a new thread.
You can merge multiple streams into one in a surprisingly large number of ways.
The simplest ways that come to mind are to merge them in the order of receipt, or first all from the first stream and then all from the second.
The method shown below in the picture allows one of two streams to form one containing ordered pairs of objects from the first and second flows. In this case, a new pair is formed if a new element appears in one of the flows. A contains a pair of strictly the last elements of each stream. This leads to the fact that the same element can be included in several pairs.
The graphical notation used in this example is called Marble diagrams and is very effective in explaining the semantics of splitting and merging flows.
If this topic interests you, I advise you to study the operations and their Marble diagrams on the resource [8].


Using streams


In order to use the elements of the stream, the user or client must first subscribe to it. As a rule, at the end of processing, he should unsubscribe from it, since garbage collectors can not always automatically deactivate a subscription when they try to utilize a subscriber.
Many clients can subscribe to one thread. In RxJs, the subscription function is called subscribe (). In it, in most cases it is advisable to place processing calls of the "normal" elements of the stream, an error handler and (relatively rarely) a stream termination handler.
Each of the subscribers to the stream receives its copy of the element or a clone of the original element. In order not to cause problems, the stream is implemented in such a way that the elements received for processing become immutable. In some situations, this limitation can still be circumvented, but it is better not to.

Dangerous charm of streams


Streams are very complicated objects, somewhat akin to integrals in mathematics. It is one thing to know that they exist and even to roughly imagine what it is, and quite another to be able to use them.
Understanding the internal logic of their functioning, necessary to apply them well in practice, requires considerable intellectual effort.
Streams are intrinsically closely related to functional programming. For the competent use of flows, it is useful to understand how it is possible to build and apply second-order functions - functions for which other functions serve as arguments.
Then the beauty and elegance of the flows will be fully revealed to you.
Streams are contagious. After comprehending their beauty, I want to use them in all tasks, which of course is not necessary.
In what tasks it is advisable to use flows, and where traditional methods should be used, everyone decides for himself.

Summarize


In this article I tried to tell you about the Mental Models of Reactive Programming (MM RP) and even partially put them in your Consciousness. Let us repeat the main points again.

  1. MM RP are special, not similar to the Mental Models of traditional Programming.
  2. When embarking on Reactive Programming, we must remember that some well-established in other areas of the MM such as the cursor, call chains or loops do not work, or they do not work like that.
  3. The main Mental Model of RP is a “three-channel stream” with a channel for “normal” elements, errors and information about the end of the stream.
  4. Streams can be finite and infinite.
  5. «», «» «» . «» «».
  6. . (, ). .
  7. , .
  8. , .
  9. . .
  10. , «».


If you are interested in this topic, you can "play" with streams using the simulators available on the site [8].
If you want to better understand the concepts of RP, I recommend that you work through the book [7], and of course, get acquainted with The Reactive Manifesto [11].
You will reach the next level in the formation of your own MM RP by working through books [9] and [10] on the design and modeling of reactive systems.

Literature and references


  1. Programming is the materialization of ideas. (Article on Habr. Habr.com/ru/post/425321 )
  2. Sirotin V. RPSE: Reification as Paradigm of Software Engineering. arxiv.org/abs/1810.01904
  3. Event-driven programming. en.m.wikipedia.org/wiki/Event-driven_programming
  4. Dataflow-programming. en.m.wikipedia.org/wiki/Dataflow_programming
  5. Stream-processing. en.m.wikipedia.org/wiki/Stream_processing
  6. Rx-Extensions: reactivex.io/languages.html
  7. RxJS in Action. – 4. August 2017. Paul P. Daniels (Autor), Luis Atencio. Manning Publications. ISBN-13: 978-1617293412
  8. RxJS online Documentstion. xgrommx.imtqy.com/rx-book/index.html
  9. Reactive Design Patterns. 2017. Roland Kuhn Dr., Brian Hanafee, Jamie Allen. Manning Publications. ISBN-13: 978-1617291807
  10. Functional and Reactive Domain Modeling. 2016. Debasish Ghosh.Manning Publications. ISBN-13: 978-1617292248
  11. The Reactive Manifesto www.reactivemanifesto.org


: geralt

Source: https://habr.com/ru/post/undefined/


All Articles