Reactive Programming
Curing your asynchronous programming blues
Copyright 2013, 2014 Wuala / andee@wuala.com
What is Reactive Programming?
- "Reactive programming is a programming paradigm oriented around data flows and the propagation of change." (Wikipedia)
- "Reactive programming considers software systems whose behaviors basically consist in reacting to activations."
(INRIA - MIMOSA project)
- "Reactive systems are interactive programs that react continuously to sequences of activation from the external world."
(Frédéric Boussinot)
- "[A reactive program] is event-driven, scalable, resilient, responsive."
(Martin Odersky)
"Reactive systems have been defined by Harel and Pnueli [On the development of reactive systems, 1985] as systems that are supposed to maintain an ongoing relationship with their environment. Such systems do not lend themselves naturally to description in terms of functions and transformations: they cannot be described adequately as computing a function from an initial state to a terminal state. On the contrary, behaviours of reactive systems are better seen as reactions to external stimuli. The role of reactive systems is to react continuously to external inputs by producing outputs. For example, man–machine interface handlers or computer games fall into the category of reactive systems."
Reactive C: An Extension of C to Program Reactive Systems (F. Boussinot, 1991)
A Simple Example
int a = 15;
int b = 16;
int c = a + b;
a = 42;
// What is the value of c ?
- Under the imperative paradigm, c is being assigned the result of a + b in the instant the expression is evaluated. Changing a or b has no effect on the value of c .
-
In reactive programming on the other hand, the value of c would be automatically updated when a or b changes.
-> cf. Verilog's non-blocking vs. blocking assignment operators.
History
- Hardware Description Languages, e.g. Verilog (1984)
- D Harel & A Pnueli, On the Development of Reactive Systems (1985)
- C Elliot, A Brief Introduction to ActiveVRML (1996)
- C Elliot, Fran - Functional Reactive Animation (1998)
- INRIA - MIMOSA project (Institute national de recherche en informatique et en automatique - Migration et mobilité: sémantique et applications) (2000)
Renewed Interest
- Why the hype? (http://www.reactivemanifesto.org)
- Continued interest in the Haskell community since Elliot's work on Fran.
- Robotics: Automaton that receives inputs ("stimuli") from the external world and needs to react ("respond") to it -> perfect match
- Picked up by the Javascript-developer community:
- L Meyerovich et al., Flapjax, 2009
- Bacon.js, Knockout, Meteor React.js etc.
- Erik Meijer (Microsoft Research, Cloud Programmability Team): Work on LINQ, Project Volta, and Rx.NET.
- Rx.NET used by Netflix:
- Initially in client (frontend).
- Ported to Java and used on server-side as well.
- DDD-Community: Problem of long-running transactions. Udi Dahan (and others): "Don't use blocking (and locking) transactions, it doesn't scale." -> Sagas, Event Sourcing.
- Requirements have changed...
Changing Requirements...
- In 2003:
- Number of servers: tens
- Response times: seconds
- Maintenance downtimes: hours
- Data volume: GBs
- In 2013:
- Number of servers: thousands
- Response times: milliseconds
- Maintenance downtime: none
- Data volume: TBs -> PBs -> EBs
...Require New Approaches
- In 2003:
- Managed servers and containers
- Systems are composed of multiple threads communicating via shared, synchronized state.
- Need to handle higher load? Scale up.
- In 2013:
- Individual nodes communicating via asynchronous protocols.
- Systems are composed of loosely coupled components that handle events asynchronously.
- Need to handle higher load? Scale out.
- Only problem:
- Asynchronous programming is hard.
- Event handling via callbacks is easy in the simple case, but is not composable and can turn code into a mess quickly.
Deriving Rx
- Duality (Mathematics)
- Other approach: Refactor Java's Future
Duality (Category Theory)
Duality - Examples
- Electrical Engineering: inductor and capacitor
-> interchanging voltage and current yields the other formula
- Logic: De Morgan's Law
-> interchanging AND and OR yields the other formula
Duality - Two Facets of the Same Concept
Duality for Dummies
- No idea? Just look it up in the mathematician's cookbook
- http://en.wikipedia.org/wiki/Dual_(category_theory)
- "reversing arrows": Input becomes output and vice versa
Easy enough, let's give it a try
public interface Iterable<T> { Iterator<T> iterator(); }
public interface Iterator<E> { boolean hasNext(); E next(); void remove(); }
Step 0 - Simplify
public interface Iterable<T> { Iterator<T> iterator(); }
public interface Iterator<E> { boolean hasNext(); E next();
void remove();}
- Let us not mix concepts (iterating vs. modifying).
Step 0 - Simplify
public interface Iterable<T> { Iterator<T> iterator(); }
public interface Iterator<E> { boolean hasNext();
E next() throws Exception; }
- next may throw an exception when it fails to produce the next element or if there are no more elements.
- Let's make the exception explicit.
Step 1 - Input and Output
public interface Iterable<T> { Iterator<T> getIterator(void); }
public interface Iterator<E> { boolean hasNext(void);
E getNext(void) throws Exception; }
- An exception is an output too!
Step 1 - Input and Output
public interface Iterable<T> { Iterator<T> getIterator(void); }
public interface Iterator<E> { (true | false) hasNext(void);
(E | Exception) getNext(void); }
- Hypothetical syntax:
- Multiple return values
- Discriminated unions
(aka tagged union, algebraic data type)
Step 1 - Input and Output
public interface Iterable<T> { Iterator<T> getIterator(void); }
public interface Iterator<E> { (true | false) hasNext(void);
(E | Exception) getNext(void); }
- Thinking about hasNext returning true
:
- Means that getNext will return an E.
-
On the other hand if hasNext returns
false, we will get a NoSuchElementException.
- So, let's consolidate both methods.
Step 1 - Input and Output
public interface Iterable<T> { Iterator<T> getIterator(void); }
public interface Iterator<E> {
(true | false) hasNext(void);(E | void | Exception) getNext(void); }
- Get rid of hasNext.
- Return void (null) instead of throwing an exception when there are no more elements.
- We might still get an exception in case of an error while producing the next element of the iteration.
Step 2 - Duality Applied
public interface IterableDual<T> { void someMethod(IteratorDual<T> it); }
public interface IteratorDual<E> {
void firstMethod(E elem);
void secondMethod(void);
void thirdMethod(Exception ex);
}
- We reverse input and output.
- As the one remaining method has three different return values, we end up with three methods in IteratorDual.
- Now we just need to cleanup and think of some decent names for the methods...
Step 3 - Cleaning Up
public interface IterableDual<T> { void setIteratorDual(IteratorDual<T> it); }
public interface IteratorDual<E> {
void gotNext(E elem);
void gotNothing(void);
void gotException(Exception ex);
}
- Not bad. Just let's use some more common/telling names...
Design Patterns Applied
public interface Observable<T> { Closable subscribe(Observer<T> observer); }
public interface Observer<E> {
void onNext(E value);
void onCompleted();
void onException(Exception ex);
}
- What we have learned:
- Observer and Iterator are intimately related.
- In fact Observer is the dual of Iterator.
- Implications...
A Mirror World
Grammar
Observable.just(42) ------O-----|---->
Observable.empty() ------------|---->
Observable.error() ------------x---->
Observable.never() ----------------->
Demo Time!
Other stuff worth investigating
- Actor model of computation (C Hewitt et al. 1973)
- Erlang (http://www.erlang.org)
- Akka (http://akka.io)
ReactiveProgramming
By Andreas Frischknecht
ReactiveProgramming
- 2,414