an introduction to reactive programmingstg-tud.github.io/sedc/lecture/ws16-17/rp1.pdf ·...
TRANSCRIPT
GuidoSalvaneschi:introductiontoreactiveprogramming
AnIntroductiontoReactiveProgramming
Prof.GuidoSalvaneschi
GuidoSalvaneschi:introductiontoreactiveprogramming
Outline
• Introtoreactiveapplications• TheObserverpattern• Event-basedlanguages• Reactivelanguages
GuidoSalvaneschi:introductiontoreactiveprogramming
INTROTOREACTIVEAPPLICATIONS
GuidoSalvaneschi:introductiontoreactiveprogramming
• Atransformational system– Acceptsinput,performscomputationonit,producesoutput,andterminates
– Compilers,shelltools,scientificcomputations
• Areactive system– Continuouslyinteractswiththeenvironment– Updatesitsstate
SoftwareTaxonomy
GuidoSalvaneschi:introductiontoreactiveprogramming
• Transformationalsystems:– Expresstransformationsasincrementalmodificationsoftheinternaldatastructures
• Reactivesystems:– Representthecurrentstateofinteraction– Reflectchangesoftheexternalworldduringinteraction
UseofState
Stateisnotnecessarytodescribethesystem
Stateisessentialtodescribethesystem
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveApplications
InteractiveApplicationsUI
Monitoring/ControlSystems
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveApplications• Manyotherexamples
– Webapplications– Mobileapps– Distributedcomputations
• Cloud– …
• Typicaloperations– Detect events/notificationsandreact– Combinereactions– Propagateupdates/changes
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveApplicationsWhyshouldwecare?
• Eventhandling:– 30%ofcodeindesktopapplications– 50%ofbugsreportedduringproductioncycle
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveProgramming
Now…– Reactiveapplicationsareextremelycommon– Canwedesignnewlanguagefeaturestospecificallyaddressthisissue?
• Thinkabouttheproblemssolvedbyexceptions,visibilitymodifiers,inheritance,…
GuidoSalvaneschi:introductiontoreactiveprogramming
REACTIVEPROGRAMMING
GuidoSalvaneschi:introductiontoreactiveprogramming
Definition…?
Forexample,abstractionsto:RepresenteventstreamsAutomaticallypropagatechangesinthestateCombineevents…
ReactiveProgramming
“Programminglanguageabstractions(techniquesandpatterns)
todevelopreactiveapplications”
GuidoSalvaneschi:introductiontoreactiveprogramming
• Haskell:Fran,Yampa• FrTime,Flapjax,REScala,Scala.react,…
• Angular.js,Bacon.js,Reactive.js,…• MicrosoftReactiveExtensions(Rx)
• Books2014-16
ReactiveProgramming
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveProgramming
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveProgramming
GuidoSalvaneschi:introductiontoreactiveprogramming
THEOBSERVERPATTERN
GuidoSalvaneschi:introductiontoreactiveprogramming
The(good?old)ObserverPattern
GuidoSalvaneschi:introductiontoreactiveprogramming
The(good?old)ObserverPatternbooleanhighTemp;booleansmoke;
voidInit(){tempSensor.register(this);smokeSensor.register(this);
}
voidnotifyTempReading(TempEvente){highTemp=e.getValue()>45;if(highTemp&&smoke){
alert.start();}
}
voidnotifySmokeReading(SmokeEvente){smoke=e.getIntensity()>0.5;if(highTemp&&smoke){
alert.start();}
}
Callbackfunctions
State
Controlstatements
Registration
Callbackfunctions
Controlstatements
GuidoSalvaneschi:introductiontoreactiveprogramming
TheObserverPattern
• WhataboutJavaSwing?– javax.swing
BEEEEP!
GuidoSalvaneschi:introductiontoreactiveprogramming
public class Beeperextends JPanel implements ActionListener {JButton button;
public Beeper(){super(new BorderLayout());button=new JButton("ClickMe");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(){//CreatetheGUIandshowit.JFrame frame=new JFrame("Beeper");//Createandsetupthewindow.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);JComponent newContentPane =new Beeper();//Createandsetupthecontentpane.newContentPane.setOpaque(true);frame.setContentPane(newContentPane);frame.pack();//Displaythewindow.frame.setVisible(true);
}public static voidmain(String[]args){javax.swing.SwingUtilities.invokeLater(new Runnable(){public void run(){createAndShowGUI();}});
}}
BEEEEP!
GuidoSalvaneschi:introductiontoreactiveprogramming
EVENT-BASEDLANGUAGES
GuidoSalvaneschi:introductiontoreactiveprogramming
Event-basedLanguages
Language-levelsupportforevents
• Eventsasobjectattributes– Describechangesoftheobject'sstate– Partoftheinterface
• Event-basedlanguagesarebetter!– Moreconcise,clearprogrammingintention,…– C#,Ptolemy,EScala,EventJava,…
GuidoSalvaneschi:introductiontoreactiveprogramming
ExampleinC#publicclassDrawing{Collection<Figure>figures;publiceventNoArgs Changed();publicvirtualvoidAdd(Figurefigure){figures.Add(figure);figure.Changed +=OnChanged;OnChanged();
}publicvirtualvoidRemove(Figurefigure){figures.Remove(figure);figure.Changed -=OnChanged;OnChanged();
}protectedvirtualvoidOnChanged(){if (Changed!=null){Changed();}
}...}
GuidoSalvaneschi:introductiontoreactiveprogramming
EVENTSINSCALA
GuidoSalvaneschi:introductiontoreactiveprogramming
REScala
• www.rescala-lang.com– Anadvancedevent-basedsystem– Abstractionsfortime-changingvalues– Bridgingbetweenthem
• Philosophy:fosteramoredeclarativeandfunctionalstylewithoutsacrificingthepowerofOOdesign
• PureScala
GuidoSalvaneschi:introductiontoreactiveprogramming
AddingEventstoScala
• C#eventsarerecognizedbythecompiler– Scala doesnotsupporteventsbyitself,but…
• CanweintroduceeventsusingthepowerfulScalasupportforDSLs?
• CanwedoevenbetterthanC#?– E.g.,eventcomposition?
GuidoSalvaneschi:introductiontoreactiveprogramming
REScala events:Summary
• Differenttypesofevents:Imperative,declarative,…
• Eventscarryavalue– Boundtotheeventwhentheeventisfired– Receivedbyallthehandlers
• Eventsareparametrictypes.– Event[T],Evt[T]
• AlleventsaresubtypeofEvent[T]
GuidoSalvaneschi:introductiontoreactiveprogramming
ImperativeEvents
• Valideventdeclarations
val e1=Evt[Unit]()val e2=Evt[Int]()val e3=Evt[String]()val e4=Evt[Boolean]()
val e5:Event[Int]=Evt[Int]()
class Fooval e6=Evt[Foo]()
GuidoSalvaneschi:introductiontoreactiveprogramming
ImperativeEvents
• Multiplevaluesforthesameeventareexpressedusingtuples
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)]()
GuidoSalvaneschi:introductiontoreactiveprogramming
Handlers
• Handlersareexecutedwhentheeventisfired– The+=operatorregistersthehandler.
• Thehandlerisafirstclassfunction– Theattachedvalueisthefunctionparameter.
var state=0val e=Evt[Int]()e+={println(_)}e+=(x=>println(x))e+=((x:Int)=>println(x))e+=(x=>{//Multiplestatementsinthehandlerstate=xprintln(x)})
GuidoSalvaneschi:introductiontoreactiveprogramming
Handlers
• Thesignatureofthehandlermustconformtheevent– E.g.,Event[(Int,Int)]requires(Int,Int)=>Unit– Thehandler:
• receivestheattachedvalue• performssideeffects.
val e=Evt[(Int,String)]()e+=(x=>{println(x._1)println(x._2)})e+=(x:(Int,String)=>{println(x)})
GuidoSalvaneschi:introductiontoreactiveprogramming
Handlers
• EventswithoutargumentsstillneedaUnitargument inthehandler.
val e=Evt[Unit]()e+={x=>println(“Fired!”)}e+={(x:Unit)=>println(“Fired!”)}
GuidoSalvaneschi:introductiontoreactiveprogramming
MethodsasHandlers
• Methodscanbeusedashandlers.– Partiallyappliedfunctionssyntax– Typesmustbecorrect
defm1(x:Int)={val y=x+1println(y)}
val e=Evt[Int]e+=m1_e(10)
GuidoSalvaneschi:introductiontoreactiveprogramming
FiringEvents
• Methodcallsyntax• Thevalueisboundtotheeventoccurrence
val e1=Evt[Int]()val e2=Evt[Boolean]()val e3=Evt[(Int,String)]()
e1(10)e2(false)e3((10,"Hallo"))
GuidoSalvaneschi:introductiontoreactiveprogramming
FiringEvents
• Registeredhandlersareexecutedeverytimetheeventisfired.– Theactualparameterisprovidedtothehandler
val e=Evt[Int]()e+={x=>println(x)}e(10)e(11)
-- output----1011
GuidoSalvaneschi:introductiontoreactiveprogramming
FiringEvents
• Allregisteredhandlersareexecuted– Theexecutionorderisnondeterministic
val e=Evt[Int]()e+={x=>println(x)}e+={x=>println("n:" +x)}e(10)e(11)
-- output----10n:1011n:11
GuidoSalvaneschi:introductiontoreactiveprogramming
FiringEvents
• The.removeoperatorunregistersahandlerviaitshandle
• The+=operatoralsoreturnsthehandlethatwillbeusedforunregistration
val e=Evt[Int]()val handler1={x:Int =>println(x)}val handler2={x:Int =>println("n:"+x)}
val h1=e+=handler1val h2=e+=handler2e(10)h1.removee(10)h2.removee(10)
-- output----10n:10n:10
GuidoSalvaneschi:introductiontoreactiveprogramming
ImperativeEvents
• Eventscanbereferredtogenerically
val e1:Event[Int]=Evt[Int]()
GuidoSalvaneschi:introductiontoreactiveprogramming
DECLARATIVEEVENTS
GuidoSalvaneschi:introductiontoreactiveprogramming
TheProblem
• Imperativeeventsarefiredbytheprogrammer• Conceptually,certaineventsdependonotherevents
• Examples:– mouseClickE ->museClickOnShape– mouseClose,keyboardClose ->closeWindow
• Canwesolvethisproblemenhancingthelanguage?
?
GuidoSalvaneschi:introductiontoreactiveprogramming
DeclarativeEvents
• Declarativeeventsaredefinedbyacombinationofotherevents.
• Somevaliddeclarations:
val e1=Evt[Int]()val e2=Evt[Int]()
val e3=e1||e2val e4=e1&&((x:Int)=>x>10)val e5=e1map((x:Int)=>x.toString)
GuidoSalvaneschi:introductiontoreactiveprogramming
ORevents
• Theevente1||e2isfiredupontheoccurrenceofoneamonge1ore2.– Theeventsintheeventexpressionhavethesameparametertype
val e1=Evt[Int]()val e2=Evt[Int]()val e1_OR_e2=e1||e2e1_OR_e2+=((x:Int)=>println(x))e1(10)e2(10)
-- output----1010
GuidoSalvaneschi:introductiontoreactiveprogramming
PredicateEvents
• Theevente&&pisfiredifeoccursandthepredicatepissatisfied.– ThepredicateisafunctionthatacceptstheeventparameterasaformalandreturnsBoolean.
– &&filterseventsusingaparameterandapredicate.
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
GuidoSalvaneschi:introductiontoreactiveprogramming
MapEvents
• Theeventemapfisobtainedbyapplyingftothevaluecarriedbye.– Themapfunctiontakestheeventparameterasaformal.– Thereturntypeofmapisthetypeparameteroftheresultingevent.
val e=Evt[Int]()val e_MAP:Event[String]=emap((x:Int)=>x.toString)e_MAP +=((x:String)=>println("Here:" +x))e(5)e(15)-- output----Here:5Here:15
GuidoSalvaneschi:introductiontoreactiveprogramming
EXAMPLESOFRESCALA EVENTS
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:Figures
abstractclassFigure{valmoved[Unit]=afterExecMoveByval resized[Unit]val changed[Unit]=resized||moved||afterExecSetColorval invalidated[Rectangle]=changed.map(_=>getBounds())...val afterExecMoveBy =newEvt[Unit]val afterExecSetColor =newEvt[Unit]…defmoveBy(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
...}
GuidoSalvaneschi:introductiontoreactiveprogramming
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.removeh2.remove
}}
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:Figures
• Inheritedevents– Maybeoverridden– Arelatebound
class RectangleFigure extends Figure{val resized=afterExecResize ||afterExecSetBoundsoverride valmoved=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 }...}
abstractclassFigure{valmoved[Unit]=afterExecMoveByval resized[Unit]…}
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:TemperatureSensor
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)
}}
}
GuidoSalvaneschi:introductiontoreactiveprogramming
REACTIVELANGUAGES
GuidoSalvaneschi:introductiontoreactiveprogramming
EventsandFunctionalDependencies
Eventsareoftenusedforfunctionaldependenciesboolean highTemp :=(temp.value >45);
val update=Evt[Unit]()var a=3var b=7var c=a+b//Functionaldependency
update+=(_=>{c=a+b})
a =4update()b =8update()
var a=3var b=7val c=a+b
a =4b =8
GuidoSalvaneschi:introductiontoreactiveprogramming
Constraints
• Whataboutexpressingfunctionaldependenciesasconstraints?
val a=3val b=7val c=a+b// Statementprintln(c)>10a=4println(c)>10
val a=3val b=7val c:=a+b// Constraintprintln(c)>10a=4println(c)>11
GuidoSalvaneschi:introductiontoreactiveprogramming
EMBEDDINGREACTIVEPROGRAMMINGINSCALA
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveValues
• Vars:primitivereactivevalues– Updated“manually”
• Signals:reactiveexpressions– Theconstraints“automatically”enforced
val a=Var(3)val b=Var(7)val c=Signal{a()+b()}println(c.now)>10a()=4println(c.now)>11
GuidoSalvaneschi:introductiontoreactiveprogramming
ReferenceModel
• Changepropagationmodel– Dependencygraph– Push-drivenevaluation
val a=Var(3)val b=Var(7)val c=Signal{a()+b()}val d =Signal{2*c()}…
a b
c
d
f
d
h
e
i
g
…
GuidoSalvaneschi:introductiontoreactiveprogramming
SIGNALSANDVARS
GuidoSalvaneschi:introductiontoreactiveprogramming
Vars
• Varswrapnormal Scalavalues
• Var[T]isaparametrictype.
– TheparameterTisthetypethevar wrapsaround
– Vars areassignedbythe“()=“operator
val a=Var(0)val b=Var("HelloWorld")val c=Var(false)val d:Var[Int]=Var(30)val e:Var[String]=Var("REScala")val f:Var[Boolean]=Var(false)
a()=3b()=“New World”c()=true
GuidoSalvaneschi:introductiontoreactiveprogramming
Signals
• Syntax:Signal{sigexpr}– Sigexprshouldbeside-effect free
• Signalsareparametrictypes.– AsignalSignal[T]carriesavalueoftypeT
GuidoSalvaneschi:introductiontoreactiveprogramming
Signals:CollectingDependencies
• AVar oraSignal calledwith()inasignalexpressionisaddedtothedependenciesofthedefinedsignal
//Multiplevars//inasignalexpressionval a=Var(0)val b=Var(0)val s=Signal{a()+b()} a b
c
GuidoSalvaneschi:introductiontoreactiveprogramming
Signals:Examples
val a=Var(0)val b=Var(0)val c=Var(0)
val r:Signal[Int]=Signal{a()+1}//Explicittypeinvar decl
val s=Signal{a()+b()} //Multiplevars isasignalexpression
val t=Signal{s()*c()+10} //Mixsignalsandvars insignalexpressions
val u=Signal{s()*t()} //Asignalthatdependsonothersignals
GuidoSalvaneschi:introductiontoreactiveprogramming
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{ //Asignalexpressioncanbeanycodeblockval tmp =a()*2val k=factorial(tmp)k+2 //ReturnsanInt}
GuidoSalvaneschi:introductiontoreactiveprogramming
Signals
• Accessingreactivevalues:now– Oftenusedtoreturntoatraditional 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.nowval y:Int =s.now
val z:Boolean=t.nowprintln(z)
GuidoSalvaneschi:introductiontoreactiveprogramming
EXAMPLESOFSIGNALS
GuidoSalvaneschi:introductiontoreactiveprogramming
Example
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:Observer
/*Createthegraphics*/title="ReactiveSwingApp"val button=newButton{text="Clickme!"}val label=new Label{text="Nobuttonclicksregistered"}contents=new BoxPanel(Orientation.Vertical){contents+=buttoncontents+=label}
/*Thelogic*/listenTo(button)var nClicks =0reactions+={case ButtonClicked(b)=>nClicks +=1label.text ="Numberofbuttonclicks:"+nClicksif (nClicks >0)button.text ="Clickmeagain"
}
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:Signals
title="ReactiveSwingApp"val label=new ReactiveLabelval button=new ReactiveButton
val nClicks =button.clicked.fold(0){(x,_)=>x+1}
label.text =Signal{(if (nClicks()==0)"No"else nClicks())+"buttonclicksregistered"}
button.text =Signal{"Clickme"+(if (nClicks()==0)"!" else "again" )}
contents=new BoxPanel(Orientation.Vertical){contents+=buttoncontents+=label
}
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:SmashingParticles
66
class Oval(center:Signal[Point],radius:Signal[Int]){…}
val base=Var(0)//Increasesindefinitelyval linearTime =base()val cyclicTime =Signal{linearTime()%200}
val point1=Signal{new Point(20+cyclicTime (),20+cyclicTime ())}new Oval(point1,cyclicTime )…//4times
GuidoSalvaneschi:introductiontoreactiveprogramming
BASICCONVERSIONFUNCTIONS
GuidoSalvaneschi:introductiontoreactiveprogramming
• Signals(andevents)areobjectsfields– Inheritance,latebinding,visibilitymodifiers,…
• Conversionfunctionsbridgesignalsandevents
REScala designprinciples
EVENTS
SIGNALS
GuidoSalvaneschi:introductiontoreactiveprogramming
BasicConversionFunctions
• Changed :: Signal[T] -> Event[T]• Latest :: Event[T] -> Signal[T]
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:Changedval SPEED=10val time=Var(0)val space=Signal{SPEED*time()}
while (true){Threadsleep20time()=time.now +1}
space.changed +=((x:Int)=>println(x))
-- output--10203040...
GuidoSalvaneschi:introductiontoreactiveprogramming
Example:Latest
val senseTmp =Evt[Int]()//Fahrenheitval threshold=40
val fahrenheitTmp =senseTmp.latest(0)val celsiusTmp =Signal{(fahrenheitTmp()– 32)/1.8 }
val alert=Signal{if (celsiusTmp()>threshold)“Warning” else “OK”}
GuidoSalvaneschi:introductiontoreactiveprogramming
Quiz1
val v1=Var(4)val v2=Var(2)val s1=Signal{v1()+v2()}val s2=Signal{s1()/3}
assert(s2.now==2)v1()=1assert(s2.now==1)
val v1=Var(4)val v2=Var(2)val s1=Signal{v1()+v2()}val s2=Signal{s1()/3}
assert(s2.now==v1()=1assert(s2.now==
GuidoSalvaneschi:introductiontoreactiveprogramming
Quiz2
var test=0val v1=Var(4)val v2=Var(2)val s1=Signal{v1()+v2()}s1.changed+=((x:Int)=>{test+=1})
assert(test==0)v1()=1assert(test==1)
var test=0val v1=Var(4)val v2=Var(2)val s1=Signal{v1()+v2()}s1.changed+=((x:Int)=>{test+=1})
assert(test==v1()=1assert(test==
GuidoSalvaneschi:introductiontoreactiveprogramming
Quiz3
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==6)e(2)assert(s2.now==8)e(1)assert(s2.now==7)
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==e(2)assert(s2.now==e(1)assert(s2.now==
GuidoSalvaneschi:introductiontoreactiveprogramming
TRUBLESHOOTING
GuidoSalvaneschi:introductiontoreactiveprogramming
Commonpitfalls
• Establishingdependencies– ()createsadependency.Useonlyinsignalexpressions
– now returnsthecurrentvalue
• Signalsarenot assignable.– Dependonothersignalsandvars– Areautomaticallyupdated
val a=Var(2)val b=Var(3)val c =Signal{a.now +b()}
a
c
b
GuidoSalvaneschi:introductiontoreactiveprogramming
Commonpitfalls
• Avoidsideeffectsinsignalexpressions
• Avoidcyclicdependencies
var c=0val s=Signal{val sum=a()+b();c=sum*2}...foo(c)
val c=Signal{val sum=a()+b();sum*2}...foo(c.now)
val a=Var(0)val s=Signal{a()+t()}val t=Signal{a()+s()+1}
GuidoSalvaneschi:introductiontoreactiveprogramming
ReactiveAbstractionsandMutability
• Signalsandvars holdreferencestoobjects,nottheobjectsthemselves.class Foo(init:Int){var x=init}val foo=new Foo(1)
val varFoo=Var(foo)val s=Signal{varFoo().x+10}assert(s.now==11)foo.x=2assert(s.now ==11)
class Foo(x:Int)//Immutableval foo=new Foo(1)
val varFoo =Var(foo)val s=Signal{varFoo().x+10
}assert(s.now ==11)varFoo()=new Foo(2)assert(s.now ==12)
class Foo(init:Int){var x=init}val foo=new Foo(1)
val varFoo =Var(foo)val s=Signal{varFoo().x+10}assert(s.now ==11)foo.x =2varFoo()=fooassert(s.now ==11)
GuidoSalvaneschi:introductiontoreactiveprogramming
QUESTIONS?