Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
85 views

Introduction To Reactive Programming

This document provides an introduction to reactive programming. It begins by defining reactive systems as those that continuously interact with their environment and update their state, as opposed to transformational systems which take input, perform computation, and produce output. It then discusses the Observer pattern as a common way to implement reactivity. The document outlines different event-based languages that provide language support for events, such as C# and REScala, a Scala extension. It provides examples of how events can be declared and handled in these languages.

Uploaded by

john
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
85 views

Introduction To Reactive Programming

This document provides an introduction to reactive programming. It begins by defining reactive systems as those that continuously interact with their environment and update their state, as opposed to transformational systems which take input, perform computation, and produce output. It then discusses the Observer pattern as a common way to implement reactivity. The document outlines different event-based languages that provide language support for events, such as C# and REScala, a Scala extension. It provides examples of how events can be declared and handled in these languages.

Uploaded by

john
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 79

An

Introduction to Reactive
Programming
Prof. Guido Salvaneschi

Guido Salvaneschi: introduction to reactive programming


Outline

• Intro to reactive applications


• The Observer pattern
• Event-based languages
• Reactive languages

Guido Salvaneschi: introduction to reactive programming


INTRO TO REACTIVE APPLICATIONS

Guido Salvaneschi: introduction to reactive programming


Software Taxonomy
• A transformational system
– Accepts input, performs computation on it, produces
output, and terminates
– Compilers, shell tools,
scientific computations

• A reactive system
– Continuously interacts with the environment
– Updates its state

Guido Salvaneschi: introduction to reactive programming


Use of State
• Transformational systems:
– Express transformations as incremental modifications of
the internal data structures
State is not necessary to describe the system

• Reactive systems:
– Represent the current state of interaction
– Reflect changes of the external world during interaction

State is essential to describe the system

Guido Salvaneschi: introduction to reactive programming


Reactive Applications

Interactive Applications Monitoring / Control


UI Systems

Guido Salvaneschi: introduction to reactive programming


Reactive Applications
• Many other examples
– Web applications
– Mobile apps
– Distributed computations
• Cloud
– …

• Typical operations
– Detect events/notifications and react
– Combine reactions
– Propagate updates/changes

Guido Salvaneschi: introduction to reactive programming


Reactive Applications
Why should we care?

• Event handling:
– 30% of code in desktop applications
– 50% of bugs reported during production cycle

Guido Salvaneschi: introduction to reactive programming


Reactive Programming
Now…
– Reactive applications are extremely common
– Can we design new language features
to specifically address this issue ?

• Think about the problems solved by exceptions,


visibility modifiers, inheritance, …

Guido Salvaneschi: introduction to reactive programming


REACTIVE PROGRAMMING

Guido Salvaneschi: introduction to reactive programming


Reactive Programming
Definition… ?
“Programming language abstractions
(techniques and patterns)
to develop reactive applications”

For example, abstractions to:


Represent event streams
Automatically propagate changes in the state
Combine events

Guido Salvaneschi: introduction to reactive programming


Reactive Programming
• Haskell: Fran, Yampa
• FrTime, Flapjax, REScala, Scala.react, …

• Angular.js, Bacon.js, Reactive.js, …


• Microsoft Reactive Extensions (Rx)

• Books 2014-16

Guido Salvaneschi: introduction to reactive programming


Reactive Programming

Guido Salvaneschi: introduction to reactive programming


Reactive Programming

Guido Salvaneschi: introduction to reactive programming


THE OBSERVER PATTERN

Guido Salvaneschi: introduction to reactive programming


The (good? old) Observer Pattern

Guido Salvaneschi: introduction to reactive programming


The (good? old) Observer Pattern
boolean highTemp; State
boolean smoke;

void Init() {
tempSensor.register(this);
smokeSensor.register(this); Registration
}

void notifyTempReading(TempEvent e ) { Callback


highTemp = e.getValue() > 45; functions
if (highTemp && smoke) {
alert.start(); Control
} statements
}

void notifySmokeReading(SmokeEvent e) { Callback


smoke = e.getIntensity() > 0.5; functions
if (highTemp && smoke) {
alert.start(); Control
} statements
}

Guido Salvaneschi: introduction to reactive programming


The Observer Pattern

• What about Java Swing ?


– javax.swing

BEEEEP!

Guido Salvaneschi: introduction to reactive programming


public class Beeper extends JPanel implements ActionListener {
JButton button;

public Beeper() {
super(new BorderLayout()); BEEEEP!
button = new JButton("Click Me");
button.setPreferredSize(new Dimension(200, 80));
add(button, BorderLayout.CENTER);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().beep();
}
private static void createAndShowGUI() { // Create the GUI and show it.
JFrame frame = new JFrame("Beeper"); //Create and set up the window.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new Beeper(); //Create and set up the content pane.
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack(); //Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater( new Runnable() { public void run() {createAndShowGUI();}});
}
}

Guido Salvaneschi: introduction to reactive programming


EVENT-BASED LANGUAGES

Guido Salvaneschi: introduction to reactive programming


Event-based Languages
Language-level support for events

• Events as object attributes


– Describe changes of the object's state
– Part of the interface

• Event-based languages are better!


– More concise, clear programming intention, …
– C#, Ptolemy, EScala, EventJava, …

Guido Salvaneschi: introduction to reactive programming


Example in C#
public class Drawing {
Collection<Figure> figures;
public event NoArgs Changed();
public virtual void Add(Figure figure) {
figures.Add(figure);
figure.Changed += OnChanged;
OnChanged();
}
public virtual void Remove(Figure figure) {
figures.Remove(figure);
figure.Changed -= OnChanged;
OnChanged();
}
protected virtual void OnChanged() {
if (Changed != null) { Changed(); }
}
...
}

Guido Salvaneschi: introduction to reactive programming


EVENTS IN SCALA

Guido Salvaneschi: introduction to reactive programming


REScala
• www.rescala-lang.com
– An advanced event-based system
– Abstractions for time-changing values
– Bridging between them

• Philosophy: foster a more declarative and functional


style without sacrificing the power of OO design
• Pure Scala

Guido Salvaneschi: introduction to reactive programming


Adding Events to Scala
• C# events are recognized by the compiler
– Scala does not support events by itself, but…

• Can we introduce events using the powerful Scala


support for DSLs?
• Can we do even better than C# ?
– E.g., event composition ?

Guido Salvaneschi: introduction to reactive programming


REScala events: Summary
• Different types of events: Imperative, declarative, …

• Events carry a value


– Bound to the event when the event is fired
– Received by all the handlers

• Events are parametric types.


– Event[T], Evt[T]

• All events are subtype of Event[T]

Guido Salvaneschi: introduction to reactive programming


Imperative Events
• Valid event declarations

val e1 = Evt[Unit]()
val e2 = Evt[Int]()
val e3 = Evt[String]()
val e4 = Evt[Boolean]()

val e5: Event[Int] = Evt[Int]()

class Foo
val e6 = Evt[Foo]()

Guido Salvaneschi: introduction to reactive programming


Imperative Events
• Multiple values for the same event
are expressed using tuples

val e1 = Evt[(Int,Int)]()
val e2 = Evt[(String,String)]()
val e3 = Evt[(String,Int)]()

val e4 = Evt[(Boolean,String,Int)]()

val e5: Evt[(Int,Int)] = Evt[(Int,Int)]()

Guido Salvaneschi: introduction to reactive programming


Handlers
• Handlers are executed when the event is fired
– The += operator registers the handler.
• The handler is a first class function
– The attached value is the function parameter.
var state = 0
val e = Evt[Int]()
e += { println(_) }
e += (x => println(x))
e += ((x: Int) => println(x))
e += (x => { // Multiple statements in the handler
state = x
println(x)
})

Guido Salvaneschi: introduction to reactive programming


Handlers
• The signature of the handler must conform the event
– E.g., Event[(Int,Int)] requires (Int,Int) =>Unit
– The handler:
• receives the attached value
• performs side effects.

val e = Evt[(Int,String)]()
e += (x => {
println(x._1)
println(x._2)
})
e += (x: (Int,String) => {
println(x)
})

Guido Salvaneschi: introduction to reactive programming


Handlers

• Events without arguments still need a


Unit argument in the handler.

val e = Evt[Unit]()
e += { x => println(“Fired!”) }
e += { (x: Unit) => println(“Fired!”) }

Guido Salvaneschi: introduction to reactive programming


Methods as Handlers
• Methods can be used as handlers.
– Partially applied functions syntax
– Types must be correct

def m1(x: Int) = {


val y = x + 1
println(y)
}

val e = Evt[Int]
e += m1 _
e(10)

Guido Salvaneschi: introduction to reactive programming


Firing Events
• Method call syntax
• The value is bound to the event occurrence

val e1 = Evt[Int]()
val e2 = Evt[Boolean]()
val e3 = Evt[(Int,String)]()

e1(10)
e2(false)
e3((10,"Hallo"))

Guido Salvaneschi: introduction to reactive programming


Firing Events
• Registered handlers are executed every time the
event is fired.
– The actual parameter is provided to the handler

val e = Evt[Int]()
e += { x => println(x) }
e(10)
e(11)

-- output ----
10
11

Guido Salvaneschi: introduction to reactive programming


Firing Events
• All registered handlers are executed
– The execution order is non deterministic

val e = Evt[Int]()
e += { x => println(x) }
e += { x => println("n: " + x)}
e(10)
e(11)

-- output ----
10
n: 10
11
n: 11

Guido Salvaneschi: introduction to reactive programming


Firing Events
• The .remove operator val e = Evt[Int]()
val handler1 = { x: Int => println(x) }
unregisters a handler val handler2 = { x: Int => println("n: " + x) }
via its handle val h1 = e += handler1
• The += operator also val h2 = e += handler2
e(10)
returns the handle h1.remove
e(10)
that will be used for h2.remove
unregistration e(10)

-- output ----
10
n: 10
n: 10

Guido Salvaneschi: introduction to reactive programming


Imperative Events

• Events can be referred to generically

val e1: Event[Int] = Evt[Int]()

Guido Salvaneschi: introduction to reactive programming


DECLARATIVE EVENTS

Guido Salvaneschi: introduction to reactive programming


The Problem
• Imperative events are fired by the programmer
• Conceptually, certain events depend on other events

• Examples:
– mouseClickE -> museClickOnShape
– mouseClose, keyboardClose -> closeWindow
• Can we solve this problem enhancing the language?
Guido Salvaneschi: introduction to reactive programming
Declarative Events
• Declarative events are defined by a combination of
other events.

• Some valid declarations:


val e1 = Evt[Int]()
val e2 = Evt[Int]()

val e3 = e1 || e2
val e4 = e1 && ((x: Int)=> x>10)
val e5 = e1 map ((x: Int)=> x.toString)

Guido Salvaneschi: introduction to reactive programming


OR events
• The event e1 || e2 is fired upon the occurrence of
one among e1 or e2.
– The events in the event expression have the same
parameter type
val e1 = Evt[Int]()
val e2 = Evt[Int]()
val e1_OR_e2 = e1 || e2
e1_OR_e2 += ((x: Int) => println(x))
e1(10)
e2(10)

-- output ----
10
10

Guido Salvaneschi: introduction to reactive programming


Predicate Events
• The event e && p is fired if e occurs and the
predicate p is satisfied.
– The predicate is a function that accepts the event
parameter as a formal and returns Boolean.
– && filters events using a parameter and a predicate.

val e = Evt[Int]()
val e_AND: Event[Int] = e && ((x: Int) => x>10)
e_AND += ((x: Int) => println(x))
e(5)
e(15)
-- output ----
15

Guido Salvaneschi: introduction to reactive programming


Map Events
• The event e map f is obtained by applying f to the
value carried by e.
– The map function takes the event parameter as a formal.
– The return type of map is the type parameter of the
resulting event.
val e = Evt[Int]()
val e_MAP: Event[String] = e map ((x: Int) => x.toString)
e_MAP += ((x: String) => println("Here: " + x))
e(5)
e(15)
-- output ----
Here: 5
Here: 15

Guido Salvaneschi: introduction to reactive programming


EXAMPLES OF RESCALA EVENTS

Guido Salvaneschi: introduction to reactive programming


Example: Figures
abstract class Figure {
val moved[Unit] = afterExecMoveBy
val resized[Unit]
val changed[Unit] = resized || moved || afterExecSetColor
val invalidated[Rectangle] = changed.map( _ => getBounds() )
...
val afterExecMoveBy = new Evt[Unit]
val afterExecSetColor = new Evt[Unit]

def moveBy(dx: Int, dy: Int) { position.move(dx, dy); afterExecMoveBy() }
def resize(s: Size) { size = s }
def setColor(col: Color) { color = col; afterExecSetColor() }
def getBounds(): Rectangle
...
}

Guido Salvaneschi: introduction to reactive programming


Example: Figures

class Connector(val start: Figure, val end: Figure) {


val h1 = start.changed += updateStart _
val h2 = end.changed += updateEnd _
...
def updateStart() { ... }
def updateEnd() { ... }
...
def dispose {
h1.remove
h2.remove
}
}

Guido Salvaneschi: introduction to reactive programming


Example: Figures
• Inherited events
abstract class Figure {
– May be overridden val moved[Unit] = afterExecMoveBy
val resized[Unit]
– Are late bound …
}

class RectangleFigure extends Figure {


val resized = afterExecResize || afterExecSetBounds
override val moved = super.moved || afterExecSetBounds
...
val afterExecResize = new Evt[Unit]
val afterExecSetBounds = new Evt[Unit]

def resize(s: Size) { ... ; afterExecResize() }
def setBounds(x1: Int, y1: Int, x2: Int, y2: Int) { ... ; afterExecSetBounds }
...
}

Guido Salvaneschi: introduction to reactive programming


Example: Temperature Sensor
class TemperatureSensor {
val tempChanged[Int] = new Evt[Int]

...
def run {
var currentTemp = measureTemp()
while(!stop) {
val newTemp = measureTemp()
if (newTemp != currentTemp) {
tempChanged(newTemp)
currentTemp = newTemp
}
sleep(100)
}
}
}

Guido Salvaneschi: introduction to reactive programming


REACTIVE LANGUAGES

Guido Salvaneschi: introduction to reactive programming


Events and Functional Dependencies
Events are often used for
val update = Evt[Unit]()
functional dependencies var a = 3
boolean highTemp := (temp.value > 45); var b = 7
var c = a + b // Functional dependency

update += ( _ =>{
var a = 3 c = a + b
var b = 7 })
val c = a + b
a = 4
a = 4 update()
b = 8 b = 8
update()

Guido Salvaneschi: introduction to reactive programming


Constraints
• What about expressing functional dependencies
as constraints ?

val a = 3 val a = 3
val b = 7 val b = 7
val c = a + b // Statement val c := a + b // Constraint
println(c) println(c)
> 10 > 10
a= 4 a= 4
println(c) println(c)
> 10 > 11

Guido Salvaneschi: introduction to reactive programming


EMBEDDING REACTIVE
PROGRAMMING IN SCALA

Guido Salvaneschi: introduction to reactive programming


Reactive Values

• Vars: primitive reactive values val a = Var(3)


– Updated “manually” val b = Var(7)
val c = Signal{ a() + b() }
println(c.now)
• Signals: reactive expressions > 10
– The constraints “automatically” a()= 4
enforced println(c.now)
> 11

Guido Salvaneschi: introduction to reactive programming


Reference Model
• Change propagation model i
– Dependency graph
f g h
– Push-driven evaluation
d e


val a = Var(3) d
val b = Var(7)
val c = Signal{ a() + b() }
val d = Signal { 2 * c() } c

a b

Guido Salvaneschi: introduction to reactive programming


SIGNALS AND VARS

Guido Salvaneschi: introduction to reactive programming


Vars
• Vars wrap normal Scala values
val a = Var(0)
• Var[T] is a parametric type. val b = Var("Hello World")
val c = Var(false)
val d: Var[Int] = Var(30)
val e: Var[String] = Var("REScala")
– The parameter T is the type val f: Var[Boolean] = Var(false)
the var wraps around
– Vars are assigned by the
a()= 3
“()=“ operator b()=“New World”
c()=true

Guido Salvaneschi: introduction to reactive programming


Signals
• Syntax: Signal{sigexpr}
– Sigexpr should be side-effect free

• Signals are parametric types.


– A signal Signal[T] carries a value of type T

Guido Salvaneschi: introduction to reactive programming


Signals: Collecting Dependencies

• A Var or a Signal called with () in a signal expression


is added to the dependencies of the defined signal

// Multiple vars
// in a signal expression c
val a = Var(0)
val b = Var(0)
val s = Signal{ a() + b() } a b

Guido Salvaneschi: introduction to reactive programming


Signals: Examples

val a = Var(0)
val b = Var(0)
val c = Var(0)

val r: Signal[Int] = Signal{ a() + 1 } // Explicit type in var decl

val s = Signal{ a() + b() } // Multiple vars is a signal expression

val t = Signal{ s() * c() + 10 } // Mix signals and vars in signal expressions

val u = Signal{ s() * t() } // A signal that depends on other signals

Guido Salvaneschi: introduction to reactive programming


Signals: Examples
val a = Var(0)
val b = Var(2)
val c = Var(true)
val s = Signal{ if (c()) a() else b() }

def factorial(n: Int) = ...


val a = Var(0)

val s: Signal[Int] = Signal{ // A signal expression can be any code block


val tmp = a() * 2
val k = factorial(tmp)
k + 2 // Returns an Int
}

Guido Salvaneschi: introduction to reactive programming


Signals
• Accessing reactive values: now
– Often used to return to a traditional computation

val a = Var(0)
val b = Var(2)
val c = Var(true)
val s: Signal[Int] = Signal{ a() + b() }
val t: Signal[Boolean] = Signal{ !c() }

val x: Int = a.now


val y: Int = s.now

val z: Boolean = t.now


println(z)

Guido Salvaneschi: introduction to reactive programming


EXAMPLES OF SIGNALS

Guido Salvaneschi: introduction to reactive programming


Example

Guido Salvaneschi: introduction to reactive programming


Example: Observer

/* Create the graphics */


title = "Reactive Swing App"
val button = new Button { /* The logic */
text = "Click me!" listenTo(button)
} var nClicks = 0
val label = new Label { reactions += {
text = "No button clicks registered" case ButtonClicked(b) =>
} nClicks += 1
contents = new BoxPanel(Orientation.Vertical) { label.text = "Number of button clicks: " + nClicks
contents += button if (nClicks > 0)
contents += label button.text = "Click me again"
} }

Guido Salvaneschi: introduction to reactive programming


Example: Signals

title = "Reactive Swing App"


val label = new ReactiveLabel
val button = new ReactiveButton

val nClicks = button.clicked.fold(0) {(x, _) => x + 1}

label.text = Signal { ( if (nClicks() == 0) "No" else nClicks() ) + " button clicks registered" }

button.text = Signal { "Click me" + (if (nClicks() == 0) "!" else " again " )}

contents = new BoxPanel(Orientation.Vertical) {


contents += button
contents += label
}

Guido Salvaneschi: introduction to reactive programming


Example: Smashing Particles

class Oval(center: Signal[Point], radius: Signal[Int]) { … }

val base = Var(0) // Increases indefinitely


val linearTime = base()
val cyclicTime = Signal{linearTime() % 200}

val point1 = Signal{ new Point(20+ cyclicTime (), 20+ cyclicTime ()) }
new Oval(point1, cyclicTime )
… // 4 times

Guido Salvaneschi: introduction to reactive programming 66


BASIC CONVERSION FUNCTIONS

Guido Salvaneschi: introduction to reactive programming


REScala design principles

• Signals (and events) are objects fields


– Inheritance, late binding, visibility modifiers, …

• Conversion functions
bridge signals and events SIGNALS

EVENTS

Guido Salvaneschi: introduction to reactive programming


Basic Conversion Functions

• Changed :: Signal[T] -> Event[T]


• Latest :: Event[T] -> Signal[T]

Guido Salvaneschi: introduction to reactive programming


Example: Changed
val SPEED = 10
val time = Var(0)
val space = Signal{ SPEED * time() }

while (true) {
Thread sleep 20
time() = time.now + 1
}

space.changed += ((x: Int) => println(x))

-- output --
10
20
30
40
...

Guido Salvaneschi: introduction to reactive programming


Example: Latest

val senseTmp = Evt[Int]() // Fahrenheit


val threshold = 40

val fahrenheitTmp = senseTmp.latest(0)


val celsiusTmp = Signal{ (fahrenheitTmp() – 32) / 1.8 }

val alert = Signal{ if (celsiusTmp() > threshold ) “Warning ” else “OK” }

Guido Salvaneschi: introduction to reactive programming


Quiz 1

val v1 = Var(4)
val v2 = Var(2)
val s1 = Signal{ v1() + v2() }
val s2 = Signal{ s1() / 3 }

assert(s2.now ==
assert(s2.now == 2)
v1()=1
assert(s2.now ==
assert(s2.now == 1)

Guido Salvaneschi: introduction to reactive programming


Quiz 2

var test = 0
val v1 = Var(4)
val v2 = Var(2)
val s1 = Signal{ v1() + v2() }
s1.changed += ((x: Int)=>{test+=1})

assert(test ==
assert(test == 0)
v1()=1
assert(test == 1)
assert(test ==

Guido Salvaneschi: introduction to reactive programming


Quiz 3

val e = Evt[Int]()
val v1 = Var(4)
val v2 = Var(2)
val s1 = e.latest(0)
val s2 = Signal{ v1() + v2() + s1() }

assert(s2.now ==
assert(s2.now == 6)
e(2)
assert(s2.now == 8)
assert(s2.now ==
e(1)
assert(s2.now ==
assert(s2.now == 7)

Guido Salvaneschi: introduction to reactive programming


TRUBLESHOOTING

Guido Salvaneschi: introduction to reactive programming


Common pitfalls
• Establishing dependencies c

– () creates a dependency. a b
Use only in signal expressions
– now returns the current value val a = Var(2)
val b = Var(3)
val c = Signal{ a.now + b() }

• Signals are not assignable.


– Depend on other signals and vars
– Are automatically updated

Guido Salvaneschi: introduction to reactive programming


Common pitfalls
• Avoid side effects in signal expressions
var c = 0 val c = Signal{
val s = Signal{ val sum = a() + b();
val sum = a() + b(); sum * 2
c = sum * 2 }
} ...
... foo(c.now)
foo(c)

• Avoid cyclic dependencies


val a = Var(0)
val s = Signal{ a() + t() }
val t = Signal{ a() + s() + 1 }

Guido Salvaneschi: introduction to reactive programming


Reactive Abstractions and Mutability
• Signals and vars hold references to objects, not the
objects themselves.
class Foo(init: Int){ class Foo(init: Int){ class Foo(x: Int) //Immutable
var x = init var x = init val foo = new Foo(1)
} }
val foo = new Foo(1) val foo = new Foo(1) val varFoo = Var(foo)
val s = Signal{
val varFoo = Var(foo) val varFoo = Var(foo) varFoo().x + 10
val s = Signal{ val s = Signal{ }
varFoo().x + 10 varFoo().x + 10 assert(s.now == 11)
} } varFoo()= new Foo(2)
assert(s. now== 11) assert(s.now == 11) assert(s.now == 12)
foo.x = 2 foo.x = 2
assert(s.now == 11) varFoo()=foo
assert(s.now == 11)

Guido Salvaneschi: introduction to reactive programming


QUESTIONS?

Guido Salvaneschi: introduction to reactive programming

You might also like