Funtional Reactive Programming with Examples in Scala + GWT

8,896 views
8,608 views

Published on

Slides of the talk by Sasha Kazachonak on FRP, GWT and Scala at scalaby#10

0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
8,896
On SlideShare
0
From Embeds
0
Number of Embeds
3,446
Actions
Shares
0
Downloads
39
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Funtional Reactive Programming with Examples in Scala + GWT

  1. 1. Functional Reactive Programming with Examples in Scala + GWT Sasha Kazachonak kazachonak.com
  2. 2. Google Web Toolkit Tutorial
  3. 3. Scala + GWT. Part 1.class StockWatcher extends EntryPoint { val stocksFlexTable = new FlexTable val stocks = new mutable.ArrayBuffer[String]() def onModuleLoad { stocksFlexTable.setText(0, 0, "Symbol") stocksFlexTable.setText(0, 1, "Price") stocksFlexTable.setText(0, 2, "Change") stocksFlexTable.setText(0, 3, "Remove") }
  4. 4. Scala + GWT. Part 2.private def addStock(symbol: String) { if (stocks.contains(symbol)) return val row = stocksFlexTable.getRowCount stocks += symbol stocksFlexTable.setText(row, 0, symbol) stocksFlexTable.setWidget(row, 2, new Label) val removeStockButton = new Button("x", new ClickHandler { def onClick(event: ClickEvent) = { val removedIndex = stocks.indexOf(symbol) stocks.remove(removedIndex) stocksFlexTable.removeRow(removedIndex + 1) } })
  5. 5. Scala + GWT. Part 3.private def updateTable(prices: Array[StockPrice]) { prices.foreach(updateTable)}private def updateTable(price: StockPrice) { if (!stocks.contains(price.symbol)) { return } val row = stocks.indexOf(price.symbol) + 1 stocksFlexTable.setText(row, 1, price.price) val changeWidget = stocksFlexTable.getWidget(row, 2).asInstanceOf [Label] changeWidget.setText(price.changePercent + "%")
  6. 6. github.com/kazachonak/contourclass StockWatcherView extends View { val widget = FlexTable(Stock.all)( stock => List( Column("Symbol")(Label(stock.symbol)), Column("Price")(Label(stock.price.toString)), Column("Change")( Label(stock.changePercent + "%") ), Column("Remove")(Button(Stock.all -= stock, "x")) )) private def addStock(symbol: String) { Stock.all += new Stock(symbol)
  7. 7. Good luck doing it in another language● No additional variables manipulation● Much cooler: FlexTable is not fully re-rendered. Just the required rows are updated.● Model-View synchronization is incapsulated in Reactive Collection implementation and View DSL implementation.● So it can be reused. No need to synchronize by hand.● All those abstractions are fully typesafe (indispensable when abstractions are non-trivial)● Implemented using only standard Scala language features without any magic.
  8. 8. The essenceof functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration. Heinrich Apfelmus the author of the Haskell library Reactive-banana
  9. 9. The basic ideas● Datatypes that represent a value "over time".● Computations that involve these changing- over-time values will themselves are values that change over time.● Imagine your program is a spreadsheet and all of your variables are cells. If any of the cells in a spreadsheet change, any cells that refer to that cell change as well.
  10. 10. The basic ideas. Example.● You could represent the mouse coordinates as a pair of integer-over-time: x = mouse.x y = mouse.y● We only need to make this assignment once, and the x and y variables will stay "up to date" automatically. No need to mutate variables.● Computations based on result in values that change over time: minX = x – 16 minY = y – 16 maxX = x + 16 maxY = y + 16 minX will always be 16 less than the x coordinate of the mouse pointer.● With reactive-aware libraries you could then say something like: rectangle(minX, minY, maxX, maxY) And a 32x32 box will be drawn around the mouse pointer and will track it. wherever it moves.
  11. 11. Deprecating the Observer Pattern● By Martin Odersky, Ingo Maier, Tiark Rompf● Published in 2010● Abstract: Programming interactive systems by means of the observer pattern is hard and error-prone yet is still the implementation standard in many production environments. We present an approach to gradually deprecate observers in favor of reactive programming abstractions…
  12. 12. Status quo● Growing number of non-expert computer users● Increasingly multimedia capable hardware● Increasing demand in interactive applications● Such apps require much efforts to deal with continuous user input and output● Programming models for user interfaces have not changed much● The predominant approach to deal with state changes in production software is still the observer pattern
  13. 13. Observers => Bugs● Quote from Adobe presentation from 2008: ● 1/3 of the code in Adobe’s desktop applications is devoted to event handling logic ● 1/2 of the bugs reported during a product cycle exist in this code● We claim that we can reduce event handling code by at least a factor of 3 once we replace publishers and observers with more appropriate abstractions.● The same abstractions should help us to reduce the bug ratio.● We believe that event handling code on average should be one of the least error-prone parts of an application.
  14. 14. Mouse Dragging Examplevar path: Path = nullval moveObserver = { (event: MouseEvent) => path.lineTo(event.position) draw(path)}control.addMouseDownObserver{ event => path = new Path(event.position) control.addMouseMoveObserver(moveObserver)}control.addMouseUpObserver{ event => control.removeMouseMoveObserver(moveObserver) path.close() draw(path)
  15. 15. The observer pattern violates many software engineering principles● Side-effects● Encapsulation● Composability● Separation of concerns● Scalablity● Uniformity● Abstraction● Resource management● Semantic distance
  16. 16. Let the tutorial begin● reactive-web.co.cc library written in Scala by Naftoli Gugenheim● Ive modified it to make it work when compiled to JavaScript by Google Web Toolkit: github.com/kazachonak/reactive
  17. 17. EventStream● Similar to a collection of values, except that rather than all values existing simultaneously, each one exists at a different point in time.● Methods in reactive-core are named like the corresponding methods in the scala collections framework.
  18. 18. Creating an EventStreamval eventSource = new EventSource[String] {}scheduleTask(10000) { eventSource.fire("Event after 10 seconds")}val eventStream: EventStream[String] = eventSourceval widgets =List(Widgets.EventSourceInput(eventSource), Widgets.EventStreamOutput(eventStream))
  19. 19. Timerclass EventStream_Timer extends Demo { // Create a timer that fires every 2 seconds, // starting at 0, for 30 seconds private val timer = new Timer(0, 2000, {t => t >= 32000}) val widgets = List(Widgets.EventStreamOutput(timer))
  20. 20. Adding listeners: foreachval eventSource = new EventSource[String] {}//The following is syntactic sugar for// eventSource.foreach(event =>// Window.alert("You fired: " + event))for(event <- eventSource) { Window.alert("You fired: " + event)}val widgets = List(Widgets.EventSourceInput(eventSource))
  21. 21. EventStream Transformations● Similar to how you can transform collections: List (1,2,3).map(_ * 10).filter(_ < 25).● Consumers of the resulting EventStream dont need to care about how it relates to the original EventStream.● Whenever the original EventStream fires an event, the transformed EventStreams may fire their own events that are based on the originals event, according to the transformation.
  22. 22. A more focused EventStream: filter val eventSource = new EventSource[String] {} // Only allow short events val eventStream = eventSource.filter(_.length < 5) val widgets = List(Widgets.EventSourceInput(eventSource), Widgets.EventStreamOutput (eventStream))
  23. 23. A transformed EventStream: mapval eventSource = new EventSource[String] {}// Reverse the eventval eventStream = eventSource.map(_.reverse)val widgets =List(Widgets.EventSourceInput(eventSource), Widgets.EventStreamOutput(eventStream))
  24. 24. flatMap val original = List(1, 2, 3) val flatMapped = original.flatMap(x => List(x*10, x*10+1, x*10+2)) flatMapped == List(10,11,12, 20,21,22, 30,31,32)Similarly, flatMap allows you to create an EventStream that fires events that are fired by different other EventStreams.
  25. 25. Combining EventStreams: |val allClicks = leftClicks | middleClicks | rightClicks
  26. 26. Signal● EventStream represents a stream of discrete values over time. In practice that means that you can never say "what is the current value?"● Signal represents a continuous value. In practical terms, a Signal has a current value, and an EventStream that, whenever the Signals value changes, fires the new value.
  27. 27. Signal Examples. val myVal = Val(72)val myVar = Var(31)myVar.change.foreach(println)myVar() = 29 // prints 29
  28. 28. Signal. map. val myVar = Var(3)val mapped = myVar.map(_ * 10)println(mapped.now) // prints 30myVar() = 62println(mapped.now) // prints 620
  29. 29. Signal. map.val myVar = Var("This is a Var")val mapped = myVar.map(s => "Reversed: "+s.reverse)val widgets =List(Widgets.VarInput(myVar), Widgets.SignalOutput(mapped))
  30. 30. Signal. flatMap. val myVar1 = Var(72)val myVar2 = Var(69)val myVar3 = Var(false)val flatMapped = myVar3 flatMap { case true => myVar1 case false => myVar2}println(flatMapped.now) // prints 69myVar3() = trueprintln(flatMapped.now) // prints 72myVar2() = 2myVar1() = 1println(flatMapped.now) // prints 1myVar3() = false
  31. 31. Signal. flatMap.def filteredList(filterSignal: Signal[String], itemsSignal: Signal[List[String]]) = for { filter <- filterSignal items <- itemsSignal } yield items.filter(s => s.indexOf(filter) >= 0)
  32. 32. SeqSignal[T] extends Signal[Seq[T]] val bufferSignal = BufferSignal(1, 2, 3, 4, 5) bufferSignal.value += 6 // fires an Insert // fires a Remove and an Insert bufferSignal() = List(2, 3, 4, 5, 6, 7)
  33. 33. Stock.all is SeqSignal[Stock]class StockWatcherView extends View { val widget = FlexTable(Stock.all)( stock => List( Column("Symbol")(Label(stock.symbol)), Column("Price")(Label(stock.price.toString)), Column("Change")( Label(stock.changePercent + "%") ), Column("Remove")(Button(Stock.all -= stock, "x")) )) private def addStock(symbol: String) { Stock.all += new Stock(symbol)
  34. 34. ?

×