RAY: Integrating Rx and Async for Direct-Style Reactive Streams
📜 Abstract
Languages like F#, C#, and recently also Scala, provide “async” extensions which aim to make asynchronous programming easier by avoiding an inversion of control that is inherent in traditional callback-based programming models (for the purpose of this paper called the “Async” model). This paper outlines a novel approach to integrate the Async model with observable streams of the Reactive Extensions model which is best-known from the .NET platform, and of which popular implementations exist for Java, Ruby, and other widespread languages. We outline the translation of “Reactive Async” programs to efficient state machines, in a way that generalizes the state machine translation of regular Async programs. Finally, we sketch a formalization of the Reactive Async model in terms of a small-step operational semantics.
✨ Summary
Summary
The paper proposes RAY, a Scala-oriented programming model that combines direct-style asynchronous programming with the observable streams of the Reactive Extensions model. Whereas conventional async/await abstractions primarily address computations that eventually produce one result, reactive streams can produce multiple values, terminate normally, fail, or remain active indefinitely. RAY therefore extends the Async model to support stream-oriented suspension and production.
The central constructs are rasync, awaitNext, awaitNextOrDone, and yieldNext. A rasync block creates an observable stream. awaitNext suspends execution until the input stream produces a value, while awaitNextOrDone represents either a next value or stream completion using Option. yieldNext publishes arbitrarily many values from within a reactive asynchronous computation. These constructs allow programs such as stream forwarders and event collectors to be written in direct style rather than through explicit callbacks or higher-order stream combinators.
The proposed implementation translates RAY programs into efficient state machines. The translation uses an observable flow pool to represent the output stream, non-blocking channels to buffer incoming events, and subscriptions that resume suspended continuations when events arrive. The design also closes subscriptions when an asynchronous computation terminates. The paper sketches a small-step operational semantics for the model, including rules for yielding values, completing observables, suspending on input streams, resuming suspended computations, and maintaining dormant queues so that events are not lost while a computation is between await operations.
A later paper by the same authors, “A Formal Model for Direct-style Asynchronous Observables,” developed the formal treatment further and presented correctness properties including an observable protocol and subject reduction. (csc.kth.se) A subsequent 2016 paper, “Reactive Async: Expressive Deterministic Concurrency,” reused the Reactive Async direction in a broader deterministic-concurrency model based on cells, adding support for dynamic and cyclic dependencies, fallbacks, a Scala implementation, and case studies involving JVM bytecode analysis. (kth.diva-portal.org)
The available evidence indicates a documented influence on subsequent programming-languages research, particularly the authors’ later formalization and deterministic-concurrency work. The search did not identify a well-documented production deployment or industry adoption specifically attributable to this paper; the concrete implementation evidence is primarily associated with Scala research prototypes and libraries. The paper’s publication venue and date are corroborated by EPFL and the REM 2013 workshop program. (epfl.ch)