dylemma / scala.frp

Functional Reactive Programming for Scala
http://javadoc.io/doc/io.dylemma/scala-frp_2.11/1.3
MIT License
24 stars 6 forks source link

Scala FRP

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:

With that requirement out of the way, you're ready to start! There are two main classes that you will want to interact with.

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