reactivity

A reactive programming library for Elixir. The purpose of this library is to experiment in an academic context.


License
GPL-3.0

Documentation

Reactive Elixir

Reactive Elixir is a small library to do reactive programming in Elixir. It's main purpose is to expiriment in an academic setting. My personal goal with this project is to run it on raspberry pi's and simulate IoT devices.

Concretly it needs to have the following features.

  • Simulate source signals (e.g., mouse location, temperature sensor value,..)
  • Create a DAG of source nodes, internal nodes, and sinks.

The library does not offer any kind of glitch freedom.

Source vs Derived

Source Signal

A source signal is a signal that is reified into the runtime. For example, Flapjax offers you the mouse position as a source signal. Because I want to simulate devices with several signals, I have decided to allow setting up a few source signals.

We create a temperature signal using Reactive.Signal.Source.new/4.

{:ok, t} = Source.new(fn(_old_value) -> Enum.random(10..100) end, -1, 5000, :temperature)

The parameters are the following.

  1. The function that generates a new value with the old one as the parameter.
  2. The initial value.
  3. The time between two signal generations.
  4. The human readable name of the signal.

Derived Signal

A derived signal, as the name hints, is a signal that is derived from the value of another signal. These can be either source or derived signals.

Assume we have the signal t in scope from the previous example. We can create a new signal based on that which generates "hot" or "cold".

t
|> Reactive.liftapp(&hot_cold/1)

The &hot_cold/1 function takes in an integer representing the temperature and returns "hot" or "cold" based on the value. We can lift this function into a signal world by calling Reactive.lift/1 on it. This returns a function that takes a Signal Integer and turns it into a String Integer.

To hook this lifted function up to the signal we need to apply it to the signal. Mind you that the function that is lifted is merely a function. We need to apply it in order for it to execute. liftapp/2 is a shorthand for apply(lift(f), signal).

Register

If an actor consumes many signals, and produces new signals, we might want to publish them for other actors to consume them. This is where the ETS table comes in. Each signal can be registered globally and read globally through the Registry.

Any signal can be registered using Reactivity.register/2. Consider the example below.

  def hot_cold(t) do
    if t > 30 do
      "hot"
    else
      "cold"
    end
  end
  
  def test do
    # Create a new signal :hotcold based on the source :temperature.
    source(:temperature)
    |> liftapp(&hot_cold/1)
    |> register(:hotcold)

  end

We know that there is a global signal :temperature and we can grab that by calling source(:temperature). this will return us a handle to that signal so we can liftapp over that signal. If we create a new signal (e.g., from Kelvin to Celcius) we can register that one, too. In the above example we register a new signal, producing the values "hot" or "cold", under the name :hotcold.

Installation

def deps do
  [{:reactivity, "~> 0.1.0"}]
end