Deprecating the Observer Pattern with Scala.React EPFL-REPORT-176887

Deprecating the Observer Pattern with Scala.React EPFL-REPORT-176887

Deprecating the Observer Pattern with Scala.React EPFL-REPORT-176887 Ingo Maier Martin Odersky EPFL {firstname}.{lastname}@epfl.ch Abstract path object from mouse movements during a drag operation Programming interactive systems by means of the observer and displays it on the screen. For brevity, we use Scala clo- pattern is hard and error-prone yet is still the implementation sures as observers. standard in many production environments. We show how var path: Path = null to integrate different reactive programming abstractions into val moveObserver = { (event: MouseEvent) => a single framework that help migrate from observer-based path.lineTo(event.position) event handling logic to more declarative implementations. draw(path) Our central API layer embeds an extensible higher-order } data-flow DSL into our host language. This embedding is control.addMouseDownObserver { event => enabled by a continuation passing style transformation. path = new Path(event.position) control.addMouseMoveObserver(moveObserver) General Terms Design, Languages } Keywords data-flow language, reactive programming, user control.addMouseUpObserver { event => interface programming, Scala control.removeMouseMoveObserver(moveObserver) path.close() 1. Introduction draw(path) We are seeing a continuously increasing demand in inter- } active applications, driven by a growing number of non- expert computer users. In contrast to traditional batch mode The above example, and as we will argue the observer programs, interactive applications require a considerable pattern as generally defined in [25], encourages one to vio- amount of engineering to deal with continuous user input late an impressive line-up of important software engineering and output. Yet, our programming models for user inter- principles: faces and other kinds of continuous state interactions have Side-effects Observers promote side-effects on the API not changed much. The predominant approach to deal with level. state changes in production software is still the observer Encapsulation Multiple observers are often needed to sim- pattern [25]. For an answer on whether it is actually worth ulate a state machine as in the drag example. The state is bothering we quote an Adobe presentation from 2008 [37] stored in variables (such as path above) that are visible to on the status of current production systems: all involved observers. For practical reasons, these vari- • 1/3 of the code in Adobe’s desktop applications is de- ables are often placed in a broader scope were it can be voted to event handling misused by unrelated code. • 1/2 of the bugs reported during a product cycle exist in Composability Multiple observers, dealing with a single this code concern such as a drag operation, form a loose collection We believe these numbers are bad not only because of the of objects that are installed at different points at different number of bugs is disproportional. We also believe that event times. Therefore, we cannot, e.g., easily add or remove handling code should account for a smaller fraction and that drag behavior. the way to achieve this is to provide better event handling Resource management An observer’s life-time needs to be abstractions. To illustrate the concrete problems of the ob- managed explicitly. For performance reasons, we want to server pattern, we start with a simple and ubiquitous exam- observe mouse move events only during a drag operation. ple: mouse dragging. The following example constructs a Therefore, we need to explicitly install and uninstall the 1 2012/5/4 mouse move observer and we need to remember the sub- • We extend Scala with a typed higher-order reactive pro- ject (point of installation). gramming library that allows to combine reactive pro- Separation of concerns The observers from our example gramming both in a functional as well as in impera- not only trace the mouse path but also call a draw- tive style. We show how these abstraction integrate with ing command, or more generally, include two different Scala’s object system and how the trait concept simplifies concerns in the same code location. It is often prefer- binding observer-based interfaces to our reactive abstrac- able to separate the concerns of constructing the path tions. and displaying it, e.g., as in the model-view-controller • We show how to incorporate a synchronous data-flow (MVC) [31] pattern. language similar to Esterel [6], but extended to a multi- Data consistency We can achieve a separation of concerns valued domain and with higher-order features, into our with a path that itself publishes events when it changes. reactive framework. Unfortunately, there is no guarantee for data consistency • We present an approach based on traits and weak refer- in the observer pattern. Suppose we create a rectangle ences that automatically binds the life-time of observers that represents the bounds of our path, i.e., a publisher to an enclosing object, eliminating a common source of that depends on changes in our original path. Also con- memory leaks in observer-based and reactive program- sider an observer listening to changes in both the path and ming. its bounds in order to draw a framed path. This observer • We show how to implement our language as a library needs to track explicitly whether the bounds are already in terms of a dependency stack, closures and delimited updated and, if not, defer the drawing operation. Other- continuations. In contrast to previous similar systems, our wise the user could observe a frame on the screen that has implementation is agnostic of any host language feature, the wrong size, which is an example of a glitch. and treats every expression uniformly, no matter whether Uniformity Different methods to install different observers built-in or user-defined, including function application, decrease code uniformity. conditionals, loops, exceptions and custom control flow Abstraction The observer pattern promotes the use of heavy- operators, without special treatment by a macro system weight interfaces. The example relies on a control class or explicit lifting. that often defines a much larger interface than a method • We present and analyze performance results of different to install mouse event observers. Therefore, we cannot implementation approaches in Scala.React and present abstract over event sources individually. For instance, we the first performance comparison of two related reac- could let the user abort a drag operation by hitting any tive libraries on different, production quality platforms. predefined key or use a different pointer device such as a In order to obtain meaningful results, we measure per- touch screen or graphics tablet. formance of reactive implementations relative to corre- Semantic distance The example is hard to understand be- sponding observer-based implementations on the same cause the control flow is inverted which results in much platform. We analyze existing functional reactive pro- boilerplate code and increases the semantic distance be- grams and propose favorable solutions using alternative tween the programmers intention and the actual code. abstractions in Scala.React. Mouse dragging is just an example of the more general Although our running example is drawn from user inter- set of input gesture recognition. If we further generalize this face programming, Scala.React can also be used for a many to event sequence recognition with (bounded or unbounded) other event processing domains, such as financial engineer- loops, all the problems we mentioned above still remain. ing, industrial control systems, automatic patient supervi- Many examples in user interface programming are therefore sion, RFID tracking or robotics. equally hard to implement with observers, such as selecting a set of items, stepping through a series of dialogs, editing and marking text – essentially every operation where the user 2. Event streams: a uniform interface for goes through a number of steps. composable events As a first step to simplify event logic we introduce a gen- 1.1 Contributions and Overview eral event interface, addressing the issues of uniformity and We present Scala.React, a library of composable, discrete abstraction from above. A third aspect to this is reusability: reactive programming abstractions that provides several API by hiding event propagation and observer handling behind a layers to allow programmers to stepwise migrate from an general interface, clients can easily publish events for their observer-based to a reactive data-flow programming model own data structures. We introduce a class EventSource[A], that eventually addresses all of the issues raised above. Our which represents generic sources of events of type A. We can contributions are in particular: schedule an event source to emit a value at any time. Here 2 2012/5/4 is how we create an event source of integers and emit two Propagation in Scala.React acts in turns, i.e., emits all pend- events:1 ing changes before it proceeds to the next turn. It is impossi- val es = new EventSource[Int] ble to start a turn before the previous one has finished, which es emit 1; es emit 2 is important to ensure the absence of glitches. Since propa- gation is also synchronous, we can have event streams emit- We can print all events from our event source to the console ting values simultaneously. The given merge operator is bi- as follows: ased towards the receiver, i.e., if event streams a and b emit val ob = observe(es) { x => println("Receiving " + x) } simultaneously, a merge b emits the event from a. We will discuss details of our propagation model in more detail be- Method observe takes event stream es and a closure that low. accepts event values x from es. The resulting observer ob We say that the newly created event stream depends on can be disposed by a single method call to ob.dispose(), the arguments of the merge operator; together they are part which uninstalls ob from all sources. Unlike in the traditional of a larger dependency graph as we will see shortly.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    20 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us