• Stars
    star
    86
  • Rank 382,193 (Top 8 %)
  • Language
    Scala
  • License
    MIT License
  • Created about 8 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The first and only true Functional Reactive Programming framework for Scala.

reactify

CI Gitter Maven Central Latest version Javadocs

The first and only true Functional Reactive Programming framework for Scala.

Justification

How can we say it's the first true FRP framework for Scala? Simple, because it is. In all other frameworks they add special framework-specific functions to do things like math (ex. adding two variables together), collection building (ex. a special implementation of ::: to concatenate two variables containing lists), or similar mechanisms to Scala's built-in collection manipulation (ex. map). These are great and mostly fill in the gaps necessary to solve your problems. But the goal for Reactify was a bit loftier. We set out to create a system that actually allows you to use ANY Scala functionality just like you would normally without any special magic (like Scala.rx's special operations require: https://github.com/lihaoyi/scala.rx#additional-operations).

In Reactify you just write code like you normally would and as used Vars and Vals change the reactive properties they have been assigned to will update as well. If you need a bit more clarification on just what the heck we mean, jump ahead to the More AdvancedExamples.

Setup

reactify is published to Sonatype OSS and Maven Central currently supporting:

  • Scala, Scala.js, and Scala Native (2.11, 2.12, 2.13, 3.x)

Configuring the dependency in SBT simply requires:

libraryDependencies += "com.outr" %% "reactify" % "4.0.8"

or, for Scala.js / Scala Native / cross-building:

libraryDependencies += "com.outr" %%% "reactify" % "4.0.8"

Concepts

This framework is intentionally meant to be a simplistic take on properties and functional reactive concepts. There are only four specific classes that really need be understood to take advantage of the framework:

  • Reactive - As the name suggests it is a simple trait that fires values that may be reacted to by Reactions.
  • Channel - The most simplistic representation of a Reactive, simply provides a public := to fire events. No state is maintained.
  • Val - Exactly as it is in Scala, this is a final variable. What is defined at construction is immutable. However, the contents of the value, if they are Reactive may change the ultimate value of this, so it is is Reactive itself and holds state.
  • Var - Similar to Val except it is mutable as it mixes in Channel to allow setting of the current value.

Val and Var may hold formulas with Reactives. These Reactives are listened to when assigned so the wrapping Val or Var will also fire an appropriate event. This allows complex values to be built off of other variables.

Using

Imports

Props is a very simple framework and though you'll likely want access to some of the implicit conversions made available in the package, everything can be had with a single import:

import reactify._

Creating

As discussed in the concepts there are only four major classes in Props (Reactive, Channel, Val, and Var). Of those classes, unless you are creating a custom Reactive you will probably only deal with the latter three.

Creating instances is incredibly simple:

val myChannel = Channel[String]           // Creates a Channel that receives Strings
val myVar = Var[Int](5)                   // Creates a Var containing the explicit value `5`
val myVal = Val[Int](myVar + 5)           // Create a Val containing the sum of `myVar` + `5`

Listening for Changes

This would all be pretty pointless if we didn't have the capacity to listen to changes on the values. Here we're going to listen to myVal and println the new value when it changes:

myVal.attach { newValue =>
  println(s"myVal = $newValue")
}

Modifying the Value

Since myVal is a Val it is immutable itself, but its value is derived from the formula myVar + 5. This means that a change to myVar will cause the value of myVal to change as a result:

myVar := 10

The above code modifies myVar to have the new value of 10. This will also cause myVal to re-evaluate and have the new value of 15 (myVar + 5). As a result, the observer we attached above will output:

myVal = 15

Derived Values

You can do clever things like define a value that is derived from other values:

val a = Var("One")
val b = Var("Two")
val c = Var("Three")

val list = Val(List(a, b, c))
list()      // Outputs List("One", "Two", "Three")

a := "Uno"
b := "Dos"
c := "Tres"

list()      // Outputs List("Uno", "Dos", "Tres")

More Advanced Examples

This is all pretty neat, but it's the more complex scenarios that show the power of what you can do with Reactify:

Complex Function with Conditional

val v1 = Var(10)
val v2 = Var("Yes")
val v3 = Var("No")
val complex = Val[String] {
    if (v1 > 10) {
      v2
    } else {
      v3
    }
}

Any changes to v1, v2, or v3 will fire a change on complex and the entire inlined function will be re-evaluated.

Multi-Level Reactive

A much more advanced scenario is when you have a Var that contains a class that has a Var and you want to keep track of the resulting value no matter what the first Var's instance is currently set to.

Consider the following two classes:

class Foo {
  val active: Var[Boolean] = Var(false)
}
class Bar {
  val foo: Var[Option[Foo]] = Var[Option[Foo]](None)
}

A Bar has a Var foo that holds an Option[Foo]. Now, say I have a Var[Option[Bar]]:

val bar: Var[Option[Bar]] = Var[Option[Bar]](None)

If we want to determine active on Foo we have several layers of mutability between the optional bar Var, the optional foo Var, and then the ultimate active Var in Foo. For a one-off we could do something like:

val active: Boolean = bar().flatMap(_.foo().map(_.active())).getOrElse(false)

This would give us true only if there is a Bar, Bar has a Foo, and active is true. But, if we want to listen for when that changes at any level (Bar, Foo, and active) it should be just as easy. Fortunately with Reactify it is:

val active: Val[Boolean] = Val(bar().flatMap(_.foo().map(_.active())).getOrElse(false))

Yep, it's that easy. Now if I set bar to Some(new Bar) then foo := Some(new Foo) on that, and finally set active to true on Foo my active Val will fire that it has changed. Reactify will monitor every level of the Vars and automatically update itself and fire when the resulting value from the function above has changed.

// Monitor the value change
active.attach { b =>
  ... do something ...    
}

// Set Bar
val b = new Bar
bar := Some(b)

// Set Foo
val f = new Foo
b.foo := Some(f)

// Set active
f.active := true

With Reactify you don't have to do any magic in your code, you just write Scala code the way you always have and let Reactify handle the magic.

Channels

As we saw above, Var and Val retain the state of the value assigned to them. A Channel on the other hand is like a Var (in that you can set values to it), but no state is retained. This is useful for representing firing of events or some other action that is meant to be observed but not stored.

Nifty Features

Dependency Variables

The core functionality is complete and useful, but we can build upon it for numeric values that are dependent on other numeric values or numeric values that may have multiple representations. For example, consider a graphical element on screen. It may have a left position for the X value originating on the left side of the element, but if we want to right-align something we have to make sure we account for the width in doing so and vice-versa for determining the right edge. We can simplify things by leveraging a Dep instance to represent it:

val width: Var[Double] = Var(0.0)

val left: Var[Double] = Var(0.0)
val center: Dep[Double, Double] = Dep(left)(_ + (width / 2.0), _ - (widht / 2.0))
val right: Dep[Double, Double] = Dep(left)(_ + width, _ - width)

Notice we've even added a center representation. These are dependent on left but their value is derived from a formula based on left and width. Of course, if representing the value alone were all we care about then a simple Val(left + width) could be used as our right value, but we also want to be able to modify center or right and have it properly reflect in left. Any changes made to Dep will properly update the variable it depends on left in this case. See DepsSpec for more detailed examples.

Dep also supports conversions between different types as well.

Binding

As of 1.6 you can now do two-way binding incredibly easily:

val a = Var[String]("Hello")
val b = Var[String]("World")
val binding = a bind b

By default this will assign the value of a to b and then changes to either will propagate to the other. If you want to detach the binding:

binding.detach()

This will disconnect the two to operate independently of each other again.

You can also do different typed binding:

implicit val s2i: String => Int = (s: String) => Integer.parseInt(s)
implicit val i2s: Int => String = (i: Int) => i.toString

val a = Var[String]("5")
val b = Var[Int](10)
a bind b

We need implicits to be able to convert between the two, but now changes to one will propagate to the other.

More Repositories

1

scribe

The fastest logging library in the world. Built from scratch in Scala and programmatically configurable.
Scala
524
star
2

youi

Next generation user interface and application development in Scala and Scala.js for web, mobile, and desktop.
Scala
210
star
3

scarango

ArangoDB client written in Scala
Scala
59
star
4

scalarelational

Type-Safe framework for defining, modifying, and querying SQL databases
Scala
58
star
5

lucene4s

Light-weight convenience wrapper around Lucene to simplify complex tasks and add Scala sugar.
Scala
54
star
6

media4s

Scala command-line wrapper around ffmpeg, ffprobe, ImageMagick, and other tools relating to media.
Scala
34
star
7

perfolation

Performance focused interpolation
Scala
30
star
8

profig

Powerful configuration management for Scala (JSON, properties, command-line arguments, and environment variables)
Scala
28
star
9

sgine

Scala Engine for OpenGL-based Desktop, Android, and iOS game and business development.
Scala
23
star
10

mailgun4s

Mailgun API implementation in Scala
Scala
17
star
11

neo4akka

Neo4j Scala client using Akka-Http
Scala
15
star
12

powerscala

Powerful framework providing many useful utilities and features on top of the Scala language.
Scala
15
star
13

scala-stripe

Complete Browser and Server client integration of Stripe in Scala and Scala.js
Scala
12
star
14

spice

Powerful client / server technology for Scala
Scala
9
star
15

jefe

Manages installation, updating, downloading, launching, error reporting, and more for your application.
Scala
8
star
16

googleapi.scala.js

Wrapper around Google APIs
Scala
6
star
17

scalajs-pixijs

Scala.js facade for Pixi.js
JavaScript
6
star
18

giant-scala

Advanced functionality for working with MongoDB in Scala
Scala
6
star
19

scalapass

Useful tools for managing storage and validation of passwords in Scala applications
Scala
5
star
20

pdf4s

Simplified wrapper to create PDFs in Scala
Scala
5
star
21

outrgl

DEPRECATED: Please use http://youi.io going forward
Scala
5
star
22

nextui

UI Abstraction Framework
Scala
4
star
23

youi-designer

User interface designer tool to create, edit, import, export, and generate user interfaces for youi.
Scala
4
star
24

pmc

Project Management in Code - An incredibly straight-forward project management and build tool for Scala.
Scala
4
star
25

lightdb

Bare Metal Modular Database
Scala
4
star
26

youi-template

Infrastructure for working with existing HTML files.
Scala
4
star
27

uberzip

Very fast multi-threaded unzipping utility.
Scala
3
star
28

youi-example

Example application built on YouI
Scala
3
star
29

robobrowser

Headless Browser wrapper library providing lots of features for API-access
Scala
3
star
30

hyperscala

DEPRECATED - See https://github.com/outr/youi for something far better.
Scala
3
star
31

youi-plugin

SBT plugin for use with YouI projects
Scala
3
star
32

outrbackup

Multi-threaded backup system.
Scala
2
star
33

iconsole

Web-based terminal / console with modular integration and distributed connectivity
Scala
2
star
34

async

Scala and Scala.js framework to execute and schedule asynchronous tasks
Scala
2
star
35

batcher

Command-line tool to batch operations, pause, save, and control concurrency
Scala
2
star
36

jsdoc2scalajs

Automated conversion of JSDocs to Scala.js facades.
Scala
1
star
37

smartystreets-scala-sdk

Scala SDK for SmartyStreets (https://smartystreets.com)
Scala
1
star
38

scalarelational-manual

Source for generating the ScalaRelational manual
Scala
1
star
39

webmidi.scala.js

Scala.js facade for Web MIDI API and https://github.com/cwilso/WebMIDIAPIShim
Scala
1
star
40

sgine-desktop.g8

Desktop-only template for Sgine
Scala
1
star
41

jar-heaven

The final solution to JAR Hell
Scala
1
star
42

torrent

Prototype for bittorrent management in Scala
Scala
1
star
43

scalajs-fabricjs

Facade around Fabric.js for Scala.js
Scala
1
star
44

ytscreensaver

Simple wrapper to play YouTube videos as a screensaver with the clock
HTML
1
star
45

geoscala

Locational data index that is full-text searchable and can update itself. Complete geospatial sorting and filtering support.
Scala
1
star