Reactive programming java. Reactive Programming in Objective-C


Over time, programming languages ​​constantly change and evolve due to new technologies, modern requirements, or a simple desire to refresh the style of coding. Reactive programming can be implemented using various frameworks such as Reactive Cocoa. It changes the imperative style of the Objective-C language and this approach to programming has something to offer to the standard paradigm. This, of course, attracts the attention of iOS developers.

ReactiveCocoa brings declarative style to Objective-C. What do we mean by this? The traditional imperative style, which is used by languages ​​such as: C, C++, Objective-C, and Java, etc., can be described as follows: You write directives for a computer program that must be executed in a certain way. In other words, you say "how to do" something. While declarative programming allows you to describe the flow of control as a sequence of actions, “what to do,” without defining “how to do it.”

Imperative vs Functional programming

The imperative approach to programming involves describing in detail each step that a computer must take to complete tasks. In fact, the imperative style is used in native programming languages ​​(or used when writing machine code). This, by the way, is a characteristic feature of most programming languages.

In contrast, the functional approach solves problems using a set of functions that need to be performed. You define the input parameters for each function, and what each function returns. These two programming approaches are very different.

Here are the main differences between languages:

1. State changes

For pure functional programming, state change does not exist because there are no side effects. A side effect involves state changes in addition to the return value due to some external interaction. RP (Referential Transparency) of a subexpression is often defined as "no side effects" and is primarily related to pure functions. SP does not allow function execution to have external access to the function's volatile state, because every subexpression is a function call by definition.

To clarify the point, pure functions have the following attributes:

  • the only noticeable output is the return value
  • the only dependence of the input parameters is the arguments
  • arguments are fully specified before any output is generated

Despite the fact that the functional approach minimizes side effects, they cannot be completely avoided since they are an inherent part of any development.

On the other hand, functions in imperative programming do not have referential transparency, and this may be the only difference between the declarative approach and the imperative one. Side effects are widely used to implement state and I/O. Commands in the source language can change state, resulting in different values ​​for the same language expression.

How about ReactiveCocoa? This is a functional framework for Objective-C, which is a conceptually imperative language without explicitly including pure functions. When trying to avoid a change in condition, there are no limitations to the side effects.

2. First class facilities

In functional programming, there are objects and functions, which are first class objects. What does it mean? This means that functions can be passed as a parameter, assigned to a variable, or returned from a function. Why is this convenient? This makes it easy to manage execution blocks, create and combine functions in a variety of ways without complications such as function pointers (char *(*(**foo)()); - have fun!).

Languages ​​that use the imperative approach have their own peculiarities regarding first-class expressions. What about Objective-C? It has blocks as closure implementations. Higher order functions (HOFs) can be modeled by taking blocks as parameters. In this case, the block is a closure and a higher order function can be created from a specific set of blocks.

However, the process of manipulating FVP in functional languages ​​is faster and requires fewer lines of code.

3. Main flow control

Imperative style loops are represented as recursion function calls in functional programming. Iteration in functional languages ​​is usually done through recursion. Why? Probably for the sake of complexity. For Objective-C developers, loops seem much more programmer-friendly. Recursions can cause problems, such as excessive RAM consumption.

But! We can write a function without using loops or recursion. For each of the infinitely possible specialized actions that can be applied to each element of a collection, functional programming uses reusable iterative functions such as “ map”, “fold”, “" These functions are useful for refactoring source code. They reduce duplication and do not require writing a separate function. (read on, we have more information about this!)

4. Order of execution

Declarative expressions show only the logical relationships between the arguments of the subexpression function and the constant state relationship. So, in the absence of side effects, the state transition of each function call occurs independently of the others.

The functional order of execution of imperative expressions depends on the non-persistent state. Therefore, the order of execution matters and is implicitly determined by the organization of the source code. In this matter, we can point out the difference between the evaluation strategies of both approaches.

Lazy evaluation, or on-demand evaluation, is a strategy in functional programming languages. In this case, the evaluation of the expression is delayed until its value is needed, thereby avoiding repeated evaluations. In other words, expressions are evaluated only when the dependent expression is evaluated. The order of operations becomes uncertain.

In contrast, energetic evaluation in an imperative language means that the expression will be evaluated as soon as it is bound to a variable. This implies dictating the order of execution. Thus, it is easier to determine when subexpressions (including functions) will be evaluated, because subexpressions can have side effects that affect the evaluation of other expressions.

5. Number of code

This is important; the functional approach requires writing less code than the imperative approach. This means fewer crashes, less code to test, and a more productive development cycle. Since the system is constantly evolving and growing, this is important.

Main components of ReactiveCocoa

Functional programming works with concepts known as future (a read-only representation of a variable) and promise (a read-only representation of a variable future). What's good about them? In imperative programming, you have to work with pre-existing values, which leads to the need to synchronize asynchronous code and other difficulties. But the concepts of futures and promises allow you to work with values ​​that have not yet been created (asynchronous code is written in a synchronous way).


Signal

Future and promise are represented as signals in reactive programming. - the main component of ReactiveCocoa. It makes it possible to imagine the flow of events that will be presented in the future. You subscribe to a signal and get access to events that will happen over time. A signal is a push-driven flow and can represent a button click, asynchronous network operations, timers, other UI events, or anything else that changes over time. They can bind the results of asynchronous operations and efficiently combine multiple event sources.

Subsequence

Another type of flow is a sequence. Unlike a signal, a sequence is a pull-driven flow. This is a kind of collection that has a similar purpose to NSArray. RACSequence allows certain operations to be performed when you need them, rather than sequentially as with a collection NSArray. Values ​​in a sequence are only evaluated when specified by default. Using only part of the sequence potentially improves performance. RACSequence allows Cocoa collections to be processed in a generic and declarative way. RAC adds the -rac_sequence method to most Cocoa collection classes so that they can be used as RACSequences.

Team

Created in response to certain actions RACCcommand and subscribes to the signal. This applies primarily to UI interactions. Categories UIKit provided by ReactiveCocoa for most controls UIKit, give us a correct way to handle UI events. Let's imagine that we have to register a user in response to a button click. In this case, the command may represent a network request. When the process starts executing, the button changes its state to “inactive” and vice versa. What else? We can pass an active signal in a command (Reachable is a good example). Therefore, if the server is unavailable (which is our “on signal”), then the command will be unavailable, and every command of the associated control will reflect this state.

Examples of basic operations

Here are some diagrams of how basic operations with RACSignals work:

Merge

+ (RACSignal *)merge:(id )signals;


Result streams have both event streams combined together. So "+merge" is useful when you don't care about the specific event source but would like to process them in one place. In our example, stateLabel.text uses 3 different signals: progress, completion, errors.

RACCommand *loginCommand = [ initWithSignalBlock:^RACSignal *(id input) ( // let's login! )]; RACSignal *executionSignal = ; RACSignal *completionSignal = filter:^BOOL(RACEvent *event) ( return event.eventType == RACEventTypeCompleted; )] map:^id(id value) ( ​​return @"Done"; )]; )]; RACSignal *errorSignal = ; RAC(self.stateLabel, text) = ];

+ (RACSignal *)combineLatest:(id )signals reduce:(id (^)())reduceBlock;

As a result, the stream contains the latest values ​​of the transmitted streams. If one of the streams does not have a value, then the result will be empty.


When can we use it? Let's take our previous example and add more logic to it. It's useful to only enable the login button when the user has entered the correct email and password, right? We can declare this rule like this:

ACSignal *enabledSignal = reduce:^id (NSString *email, NSString *password) ( return @( && password.length > 3); )];

*Now let's change our login command a little and connect it to the actual loginButton

RACCommand *loginCommand = [ initWithEnabled:enabledSignal signalBlock:^RACSignal *(id input) ( // let's login! )]; ;

- (RACSignal *)flattenMap:(RACStream * (^)(id value))block;

You create new streams for each value in the original stream using the given function (f). The result stream returns new signals based on the values ​​generated in the original streams. Therefore it can be asynchronous.


Let's imagine that your authorization request to the system consists of two separate parts: receiving data from Facebook (identifier, etc.) and passing it to the Backend. One of the requirements is to be able to cancel login. Therefore, client code must handle the state of the login process to be able to cancel it. This results in a lot of boilerplate code, especially if you can log in from multiple places.

How does ReactiveCocoa help you? This could be a login implementation:

- (RACSignal *)authorizeUsingFacebook ( return [[ flattenMap:^RACStream *(FBSession *session) ( return ; )] flattenMap:^RACStream *(NSDictionary *profile) ( return ; )]; )

Legend:

+ - a signal that leads to an opening FBSession. If necessary, this can lead to entering Facebook.

- - a signal that retrieves profile data through a session, which is transmitted as self.

The advantage of this approach is that for the user the entire flow is fuzzy, represented by a single signal that can be canceled at any “stage”, be it a Facebook login or a Backend call.

Filter

- (RACSignal *)filter:(BOOL (^)(id value))block;

The resulting stream contains the values ​​of stream “a” filtered according to the specified function.


RACSequence *sequence = @[@"Some", @"example", @"of", @"sequence"].rac_sequence; RACSequence *filteredSequence = ; )];

Map

- (RACSignal *)map:(id (^)(id value))block;

Unlike FlattenMap, Map runs synchronously. The value of property “a” is passed through the given function f(x + 1) and returns the mapped original value.


Let's say you need to enter the title of a model on the screen, applying some attributes to it. Map comes into play when “Applying some attributes” is described as a separate function:

RAC(self.titleLabel, text) = initWithString:modelTitle attributes:attributes]; )];

How we work: unites self.titleLabel.text with changes model.title by applying custom attributes to it.

Zip

+ (RACSignal *)zip:(id )streams reduce:(id (^)())reduceBlock;

Result stream events are created when each thread has generated an equal number of events. It contains values, one from each of the 3 merged streams.


For some practical examples, zip can be described as dispatch_group_notify For example, you have 3 separate signals and need to combine their responses at a single point:

NSArray *signals = @; return ;

- (RACSignal *)throttle:(NSTimeInterval)interval;

Using a timer set for a certain period of time, the first value of stream “a” is transferred to the result stream only when the timer ends. In case a new value is produced within a given time interval, it holds the first value, preventing it from being passed to the result stream. Instead, a second value appears in the result stream.


Amazing case: we need to search for a query when the user changes the searchField. Standard task, right? However, it is not very efficient for constructing and sending a network request whenever text changes, since textField can generate many such events per second, and you end up with inefficient network usage.
The solution here is to add a delay before we actually complete the network request. This is usually achieved by adding an NSTimer. With ReactiveCocoa it's much easier!

[[ throttle:0.3] subscribeNext:^(NSString *text) ( // perform network request )];

*The important note here is that all "previous" textFields are changed before the "latest" ones are deleted.

Delay

- (RACSignal *)delay:(NSTimeInterval)interval;

The value received in stream “a” is delayed and transferred to the result stream after a certain time interval.


Like -, delay will only delay the sending of “next” and “completed” events.

[ subscribeNext:^(NSString *text) ( )];

What we love about Reactive Cocoa

  • Introduces Cocoa Bindings to iOS
  • Ability to create operations on future data. Here's some theory about futures & promises from Scala.
  • The ability to represent asynchronous operations in a synchronous manner. Reactive Cocoa simplifies asynchronous software such as network code.
  • Convenient decomposition. Code that deals with user events and application state changes can become very complex and confusing. Reactive Cocoa makes dependent operation models especially simple. When we represent operations as bundled threads (for example, processing network requests, user events, etc.), we can achieve high modularity and loose coupling, which leads to more frequent code reuse.
  • Behaviors and relationships between properties are defined as declarative.
  • Solves synchronization problems - if you combine multiple signals, then there is one single place to handle all the results (be it next value, completion signal or errors)

With the RAC framework, you can create and transform sequences of values ​​in a better, higher-level way. RAC makes it easier to manage everything that waits for an asynchronous operation to complete: the network response, the dependent value change, and the subsequent reaction. He may seem difficult to deal with at first, but ReactiveCocoa is infectious!

I want to tell you about a modern programming discipline that meets the growing demands for scalability, fault tolerance and responsiveness, and is indispensable in both multi-core environments and cloud computing, and also introduce an open online course on it, which will start in just a few days.

If you haven't heard anything about reactive programming, that's okay. This is a rapidly developing discipline that combines concurrency with event-drivenness and asynchrony. Reactivity is inherent in any web service and distributed system, and serves as the core of many high-performance, highly parallel systems. In short, the course authors propose to consider reactive programming as a natural extension of functional programming (with higher-order functions) to parallel systems with distributed state, coordinated and orchestrated by asynchronous data streams exchanged between active subjects, or actors.

This is described in more understandable terms in the Reactive Manifesto; below I will retell the main points from it, and the full translation is published on Habré. According to Wikipedia, the term reactive programming has existed for quite a long time and has practical applications of varying degrees of exoticism, but it received a new impetus for development and distribution quite recently, thanks to the efforts of the authors of the Reactive Manifesto - an initiative group from Typesafe Inc. Typesafe is known in the functional programming community as the company founded by the authors of the beautiful Scala language and the revolutionary Akka parallel platform. They are now positioning their company as the creator of the world's first next-generation jet platform. Their platform allows you to quickly develop complex user interfaces and provides a new level of abstraction over parallel computing and multi-threading, reducing their inherent risks with guaranteed predictable scaling. It puts the ideas of the Reactive Manifesto into practice and allows the developer to conceptualize and create applications that meet modern needs.

You can become familiar with the framework and reactive programming by taking the Principles of Reactive Programming massive open online course. This course is a continuation of Martin Odersky's Principles of Functional Programming on Scala, which has over 100,000 participants and has one of the highest success rates in the world for a massive open online course by its participants. Together with the creator of the Scala language, the new course is taught by Eric Meyer, who developed the Rx environment for reactive programming under .NET, and Roland Kuhn, who currently leads the Akka development team at Typesafe. The course covers the key elements of reactive programming and shows how they are used to design event-driven systems that are scalable and fault-tolerant. The educational material is illustrated with short programs and accompanied by a set of tasks, each of which is a software project. If all tasks are successfully completed, participants receive certificates (of course, both participation and certificates are free). The course lasts 7 weeks and starts this Monday, November 4th. A detailed outline, as well as an introductory video, are available on the course page: https://www.coursera.org/course/reactive.

For those who are interested or in doubt, I offer a condensed summary of the basic concepts of the Reactive Manifesto. Its authors note significant changes in application requirements in recent years. Today, applications are deployed in any environment from mobile devices to cloud clusters with thousands of multi-core processors. These environments place new demands on software and technology. In previous generation architectures, the emphasis was on managed servers and containers, and scaling was achieved through additional expensive hardware, proprietary solutions, and parallel computing through multi-threading. A new architecture is now evolving, in which four important features can be identified that are increasingly prevalent in both consumer and corporate industrial environments. Systems with such an architecture are: event-driven, scalable, fault-tolerant and have a fast response, i.e. responsive. This provides a seamless user experience with a real-time feel, supported by a self-healing, scalable application stack ready for deployment in multi-core and cloud environments. Each of the four characteristics of reactive architecture applies to the entire technology stack, which distinguishes them from links in layered architectures. Let's look at them in a little more detail.


Event-driven applications assume asynchronous communication components and implement their loosely coupled design: the sender and recipient of the message do not need information about each other or about the method of transmitting the message, which allows them to concentrate on the content of the communication. In addition to the fact that loosely coupled components significantly improve maintainability, extensibility and evolution of the system, the asynchrony and non-blocking nature of their interaction also makes it possible to free up a significant part of resources, reduce call time and ensure O greater throughput compared to traditional applications. It is the event-driven nature that makes the remaining features of reactive architecture possible.

Scalability in the context of reactive programming, this is the system’s response to load changes, i.e. elasticity, achieved by the ability to add or release compute nodes as needed. With low coupling, asynchronous messaging, and location transparency, the deployment method and application topology become a deployment time decision and a matter of configuration and load-responsive algorithms. Thus, the computer network becomes part of an application that initially has an explicit distributed nature.

fault tolerance Reactive architecture is also becoming part of the design, and this makes it significantly different from traditional approaches to ensuring continuous system availability by redundant servers and taking over control in the event of failure. The resilience of such a system is achieved by its ability to correctly respond to failures of individual components, isolate these failures by storing their context in the form of messages that caused them, and pass these messages to another component that can make decisions about how to handle the error. This approach allows you to keep the business logic of the application pure, separating from it the logic for handling failures, which is formulated in an explicit declarative form to register, isolate, and handle failures using the system itself. To build such self-healing systems, components are ordered hierarchically, and the problem is escalated to the level that can solve it.

And finally responsiveness- this is the ability of the system to respond to user input regardless of load and failures; such applications involve the user in interaction, create a feeling of close connection with the system and sufficient equipment to perform current tasks. Responsiveness is not only relevant in real-time systems, but is also necessary for a wide range of applications. Moreover, a system that is unable to respond quickly even at the moment of failure cannot be considered fault-tolerant. Responsiveness is achieved by using observable models, event streams, and stateful clients. Observable models generate events when their state changes and enable real-time interaction between users and systems, and event streams provide the abstraction on which this interaction is built through non-blocking asynchronous transformations and communications.

Thus, reactive applications represent a balanced approach to solving a wide range of problems in modern software development. Built on an event-driven foundation, they provide the tools needed to guarantee scalability and fault tolerance and support a rich, responsive user experience. The authors expect that an increasing number of systems will adhere to the principles of the reactive manifesto.

In addition, I provide the course plan without translation. Just in case you've read this far and are still interested.

Week 1: Review of Principles of Functional Programming: substitution model, for-expressions and how they relate to monads. Introduces a new implementation of for-expressions: random value generators. Shows how this can be used in randomized testing and gives an overview of ScalaCheck, a tool which implements this idea.

Week 2: Functional programming and mutable state. What makes an object mutable? How this impacts the substitution model. Extended example: Digital circuit simulation

Week 3: Futures. Introduces futures as another monad, with for-expressions as concrete syntax. Shows how futures can be composed to avoid thread blocking. Discusses cross-thread error handling.

Week 4: Reactive stream processing. Generalizing futures to reactive computations over streams. Stream operators.

Week 5: Actors. Introduces the Actor Model, actors as encapsulated units of consistency, asynchronous message passing, discusses different message delivery semantics (at most once, at least once, exactly once) and eventual consistency.

Week 6: Supervision. Introduces reification of failure, hierarchical failure handling, the Error Kernel pattern, lifecycle monitoring, discusses transient and persistent state.

Week 7: Conversation Patterns. Discusses the management of conversational state between actors and patterns for flow control, routing of messages to pools of actors for resilience or load balancing, acknowledgment of reception to achieve reliable delivery.

Check information. It is necessary to check the accuracy of the facts and reliability of the information presented in this article. There should be an explanation on the talk page... Wikipedia

Interactivity is a concept that reveals the nature and degree of interaction between objects. Used in the fields of: information theory, computer science and programming, telecommunications systems, sociology, industrial design and others. In... ... Wikipedia

This article should be Wikified. Please format it according to the article formatting rules. This term has other meanings, see Elektromash (meanings) ... Wikipedia

foreign psychotherapeutic techniques- DEPTH TECHNIQUES Active psychotherapy (Fromm Reichmann). Analysis of Being (Binswanger). Analysis of fate (Sondi). Character analysis (W. Reich). Self Analysis (H. Kohut, E. Erikson). Analytical play therapy (M. Klein). Analytical Family Therapy (Richter).... ... Great psychological encyclopedia

Books

  • Reactive programming in C++. Designing Parallel and Asynchronous Applications Using , Pai Prasidh, Abraham Peter. Designing parallel and asynchronous applications using the RxCpp library and modern C++17 Parallel programming tools supported by the language standard Collaborative…
  • , Nurkiewicz T., Christensen B.. In these days, when programs are asynchronous and fast response is the most important property, reactive programming will help you write more reliable, better scalable and faster running code.…
  • Reactive programming using RxJava, Nurkiewicz Tomasz, Christensen Ben. In a day and age where programs are asynchronous and responsiveness is paramount, reactive programming can help you write more reliable, better scalable, and faster running code.…

The world of OOP development in general and the Java language in particular live a very active life. There are fashion trends here, and today we’ll look at one of the main trends of the season - the ReactiveX framework. If you're still on the fence about this wave, I promise you'll love it! It's definitely better than high-waisted jeans :).

Reactive Programming

As soon as OOP languages ​​reached mass adoption, developers realized how sometimes the capabilities of C-like languages ​​were lacking. Since writing code in the functional programming style seriously destroys the quality of OOP code, and therefore the maintainability of the project, a hybrid was invented - reactive programming.

The reactive development paradigm is based on the idea of ​​constantly monitoring changes in the state of an object. If such changes have occurred, then all interested objects should receive the updated data and work only with them, forgetting about the old ones.

A good example of a reactive programming idea is an Excel spreadsheet. If you link multiple cells with one formula, the result of the calculation will change every time the data in those cells changes. For accounting, such dynamic changes in data are commonplace, but for programmers this is rather an exception.

A=3; b=4; c=a + b; F1(c); a=1; F2(c);

In this example, functions F1 and F2 will work with different values ​​of the variable C. Often it is required that both functions have only the most current data - reactive programming will allow you to immediately call F1 with new parameters without changing the logic of the functions themselves. This code structure gives the application the ability to instantly respond to any changes, which makes it fast, flexible and responsive.

ReactiveX

Implementing reactive programming ideas from scratch can be quite troublesome - there are pitfalls, and it will take a lot of time. Therefore, for many developers this paradigm remained only theoretical material until ReactiveX appeared.

The ReactiveX framework is a reactive programming tool that works with all popular OOP languages. The creators themselves call it a multi-platform API for asynchronous development, based on the Observer pattern.

If the term “reactive programming” is a kind of theoretical model, then the “Observer” pattern is a ready-made mechanism for tracking changes in a program. And you have to track them quite often: downloading and updating data, notifications about events, and so on.

The Observer pattern has been around for about as long as OOP itself. An object whose state can change is called a publisher (a popular translation of the term Observable). All other participants who are interested in these changes are subscribers (Observer, Subscriber). To receive notifications, subscribers register with the publisher by explicitly providing their ID. The publisher from time to time generates notifications that are sent to the list of registered subscribers.

Actually, the creators of ReactiveX didn’t come up with anything revolutionary, they just conveniently implemented the pattern. And although many OOP languages, and Java in particular, have ready-made implementations of the pattern, this framework contains additional “tuning” that turns the Observer into a very powerful tool.

RxAndroid

The port of the ReactiveX library for the Android world is called rxAndroid and is connected, as always, via Gradle.

Compile "io.reactivex:rxandroid:1.1.0"

The publisher that generates notifications is specified here using the Observable class. The publisher can have several subscribers; to implement them, we will use the Subscriber class. The standard behavior for an Observable is to issue one or more messages to subscribers and then exit or issue an error message. Messages can be either variables or entire objects.

Rx.Observable myObserv = rx.Observable.create(new rx.Observable.OnSubscribe () ( @Override public void call(Subscribersubscriber) ( subscriber.onNext("Hello"); subscriber.onNext("world"); subscriber.onCompleted(); ) ));

In this case, the myObserv publisher will first send the strings hello and message, and then a success message. The publisher can call the onNext() , onCompleted() and onEror() methods, so subscribers must have them defined.

Subscriber mySub = new Subscriber () (... @Override public void onNext(String value) (Log.e("got data", " " + value);) );

Everything is ready to go. All that remains is to connect the objects with each other - and “Hello, world!” ready for reactive programming!

MyObserv.subscribe(mySub);

I must say that this was a very simple example. ReactiveX has many options for the behavior of all pattern participants: filtering, grouping, error handling. The benefits of reactive programming can only be felt by trying it in action. Let's move on to a more serious task.

Continuation is available only to members

Option 1. Join the “site” community to read all materials on the site

Membership in the community within the specified period will give you access to ALL Hacker materials, increase your personal cumulative discount and allow you to accumulate a professional Xakep Score rating!







2024 gtavrl.ru.