/reactive-programming

Principles of Reactive Programming

Primary LanguageScala

Principles of Reactive Programming

These are my notes and study guide how I approach studying Reactive Programming.

Build Status

What is Reactive Programming (RP)?

The basic principle of reactive programming is: Reacting to sequence of events that happen in time, and, using these patterns to, build software systems that are more robust, more resilient, more flexible and better positioned to meet modern demands. -- Reactive Manifesto

In computing, reactive programming is a programming paradigm oriented around data flows and the propagation of change. This means that it should be possible to express static or dynamic data flows with ease in the programming languages used, and that the underlying execution model will automatically propagate changes through the data flow. -- Wikipedia

Typesafe Reactive Platform

Video

Rx

Video

Scala

Scalaz

Stream Processing

Stream processing is a different paradigm to the Actor Model or to Future composition, therefore it may take some careful study of this subject until you feel familiar with the tools and techniques. -- Akka Streams Documentation

Video

Documentation

GitHub Markdown

Parleys

Week 3 - Futures and Composition

Futures provide a nice way to reason about performing many operations in parallel– in an efficient and non-blocking way. The idea is simple, a Future is a sort of a placeholder object that you can create for a result that does not yet exist. Generally, the result of the Future is computed concurrently and can be later collected. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code. -- ScalaDocs

Documentation

Video

Week 2 - Functional Reactive Programming

Functional reactive programming (FRP) is a programming paradigm for reactive programming (asynchronous dataflow programming) using the building blocks of functional programming (e.g. map, reduce, filter). FRP has been used for programming graphical user interfaces (GUIs), robotics, and music, aiming to simplify these problems by explicitly > modeling time. -- Wikipedia

I would advice reading / viewing the resources below to get a good idea on what Functional Reactive Programming is. The model we use this week is push based, in which systems take events and push them through a 'signal' network to achieve a result. The basic idea of FRP that we focus on this week is that events are combined into 'signals' that always have a current value, but change discretely. The changes are event-driven. But instead of having an event handler that returns Unit, (like the onClick handler and such), it returns a value.

FRP in a nutshell (for now at least):

When we do an assignment in Scala, the following happens:

scala> var a = 1
a: Int = 1

scala> var b = 2
b: Int = 2

scala> var c = a + b
c: Int = 3

scala> a = 2
a: Int = 2

scala> c
res1: Int = 3

scala> var c = a + b
c: Int = 4

As we can see, the value of c did not change, when we changed the value of a from 1 to 2. This is normal behavior because we have expressed the relationship at one point in the execution of the program.

But what if, c would change when we changed the value of a dependent value like a. This would mean that there is a dependency created between c, a and b that expresses how these values will relate over time. So the basic idea is that c will change when we change either a and/or b.

Hint 1: TweetText

The following should work:

 def tweetRemainingCharsCount(tweetText: Signal[String]): Signal[Int] =
    Signal(MaxTweetLength - tweetLength(tweetText()))

  def colorForRemainingCharsCount(remainingCharsCount: Signal[Int]): Signal[String] =
    Signal {
      remainingCharsCount() match {
        case count if (0 to 14).contains(count) => "orange"
        case count if count < 0 => "red"
        case _ => "green"
      }
    }

Hint 2: Polynomal

Please first try it yourself, then if you wish, verify.

  def computeDelta(a: Signal[Double], b: Signal[Double], c: Signal[Double]): Signal[Double] =
  Signal {
    Math.pow(b(), 2) - (4 * a() * c())
  }

  def computeSolutions(a: Signal[Double], b: Signal[Double], c: Signal[Double], delta: Signal[Double]): Signal[Set[Double]] =
    Signal {
      delta() match {
        case discriminant if discriminant < 0 => Set()
        case discriminant if discriminant == 0 => Set(calcLeft(a(), b(), c()))
        case discriminant => Set(calcLeft(a(), b(), c()), calcRight(a(), b(), c()))
      }
    }

  def calcLeft(a: Double, b: Double, c: Double): Double =
    (-1 * b + Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a)

  def calcRight(a: Double, b: Double, c: Double): Double =
    (-1 * b - Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a)

Hint 3: Calculator

Please first try it yourself, then if you wish, verify.

  def computeValues(namedExpressions: Map[String, Signal[Expr]]): Map[String, Signal[Double]] = {
    namedExpressions.mapValues { expr =>
      Signal(eval(expr(), namedExpressions))
    }
  }

  def eval(expr: Expr, references: Map[String, Signal[Expr]]): Double = {
    expr match {
      case Literal(v) => v
      case Ref(name) => eval(getReferenceExpr(name, references), references - name)
      case Plus(aExpr, bExpr)   => eval(aExpr, references) + eval(bExpr, references)
      case Minus(aExpr, bExpr)  => eval(aExpr, references) - eval(bExpr, references)
      case Times(aExpr, bExpr)  => eval(aExpr, references) * eval(bExpr, references)
      case Divide(aExpr, bExpr) => eval(aExpr, references) / eval(bExpr, references)
      case _ => Double.MaxValue
    }
  }

  /** Get the Expr for a referenced variables.
   *  If the variable is not known, returns a literal NaN.
   */
  private def getReferenceExpr(name: String, references: Map[String, Signal[Expr]]): Expr = {
    references.get(name).fold[Expr](Literal(Double.NaN)) {
      exprSignal => exprSignal()
    }
  }

Documentation

FRP Libraries

Books

Video

Week 1 - Functional Programming

Video

Example source code

Test frameworks

Hints

Hint 1

For testing, you want to insert two values into the heap, for example with:

forAll { (x: Int, y: Int) => 
}

When you add the values into the heap and search for the minimum findMin, it would be handy to know whether x or y is the smallest, the following will help:

scala> def order = scala.math.Ordering.Int
order: math.Ordering.Int.type

scala> order.min(2,1)
res0: Int = 1

The heap has the method ord that does the same.

Hint 2

To generate a heap, you can use the following code:

lazy val genHeap: Gen[H] = for {
    n <- arbitrary[Int]
    h <- oneOf(empty, genHeap)
  } yield insert(n, h)

Hint 3

Did you notice the following? We have three methods, isEmpty, findMin and deleteMin. When you combine these methods you can iterate over the heap until its empty and put the contents into a list:

 def heapToList(h: H): List[Int] =
    if(isEmpty(h)) Nil else findMin(h) :: heapToList(deleteMin(h))

Lists can be sorted:

scala> val xs = List(2,3,1,5,6,2,3,4,0,1,2)
xs: List[Int] = List(2, 3, 1, 5, 6, 2, 3, 4, 0, 1, 2)

scala> xs.sorted
res0: List[Int] = List(0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6)

Lists can also be compared:

scala> List(1, 2) == List(1, 2)
res0: Boolean = true
              
scala> List(1, 2) == List(2, 1)
res1: Boolean = false

scala> List(1, 2) == List(2, 1).sorted
res2: Boolean = true

scala> List(2, 1).sorted == List(2, 1).sorted
res3: Boolean = true