This library replaces the idea of Publishers
and Subscribers
with EventStreams
. An EventStream
can be treated like a collection of events; it can be transformed similarly to any other Scala collection, and instead of needing to call publisher.addEventListener(new EventListener(){...})
, you simply call eventStream.foreach{...}
###Background
Scala FRP (stands for Functional Reactive Programming) is a library inspired by Ingo Maier's paper, Deprecating the Observer Pattern. Ingo Maier made an implementation of his "scala.react" framework which is available on Github in its original form. I also made a version of the same library that works with SBT to manage its dependencies, available here.
###Getting it
Add the following to your build.sbt
file:
libraryDependencies += "io.dylemma" %% "scala-frp" % "1.2"
###Example Usage
import io.dylemma.frp._
// Mix in `Observer` for free memory management.
object Example extends App with Observer {
// Create a source of events.
val ints = EventSource[Int]
// You can derive new event streams any other event stream.
val evenInts = ints.filter{ _ % 2 == 0 }
val intsWithIndices = ints.zipWithIndex
val soonInts = ints.before(2 seconds fromNow)
// Attach event handlers.
ints.foreach{ x => println(s"An int: $x") }
evenInts.foreach { x => println(s"Even int: $x") }
intsWithIndices.foreach { case (x, i) => println(s"$ith int was $x") }
soonInts.foreach { x => println(s"$x came soon enough") }
// Fire events!
ints fire 1
ints fire 2
ints fire 3
}
###FRP-101
Attaching event handlers requires an implicit Observer
. The observer helps make sure that no cyclical references are made between the EventStream
and the handler function; it keeps a weak reference to the handler so that the handler function may be garbage-collected once the Observer
is garbage-collected. You can get an implicit Observer
one of two ways:
- Make one yourself:
implicit object myObserver extends Observer
- Mix it into the containing class, like in the example above. The
Observer
trait has an implicit reference to itself.
With that requirement out of the way, you're ready to start! There are two main classes that you will want to interact with.
- EventStream is a read-only class that you can attach event handlers to, and create mappings and combinations with.
- EventSource is a concrete implementation of
EventStream
that also exposesfire
andstop
methods.
###FRP-102
EventStreams
are finite. At a low level, they emit events as an Event[A]
, which can either be a Fire(item)
or a Stop
. When you call foreach
on a stream, it will receive all Fire
events until the stream emits a Stop
. You can attach to these lower-level events with the sink(handler: Event[A] => Boolean)
method. The handler will remain attached until it returns false
in response to an event.
Because of the concept of finite EventStreams
, streams are able to be concatenated; handlers may be attached that simply wait for the last event from the stream, or the eventual Stop
.
For further details and a full list of capabilities, check out the docs