Current section
Files
Jump to
Current section
Files
lib/signal_derived.ex
defmodule Reactivity.Signal.Derived do
use GenServer
require Logger
alias Reactivity.Signal.Source
alias Reactivity.Signal.Derived
# last_value: The last generated value.
# generator : The lambda that will generate the next value.
# sleep : The time between each new signal.
defstruct last_value: nil, generator: nil, children: [], parent: nil
#############
# GenServer #
#############
def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
def init(%{val: v, gen_func: f}) do
{:ok, %Derived{last_value: v, generator: f, children: []}}
end
#############
# Callbacks #
#############
def handle_cast(m, state) do
Logger.debug "Cast: #{inspect m}"
{:noreply, state}
end
def handle_call({:add_child, closure}, _from, state) do
new_state = %{state | children: [closure | state.children]}
{:reply, :ok, new_state}
end
def handle_call(m, from, state) do
{:reply, :ok, state}
end
def handle_info({:tick, new_value}, state) do
# Compute the new value
new_value = state.generator.(new_value)
# Tick all my children.
Enum.map(state.children, fn(c) -> send(c, {:tick, new_value}) end)
# Compute the new state.
new_state = %{state | last_value: new_value}
{:noreply, new_state}
end
def handle_info(m, state) do
Logger.debug "Info: #{inspect m}"
{:noreply, state}
end
###########
# Private #
###########
#############
# Interface #
#############
@doc """
Creates a new source signal based on a function.
It's not the point to be used in programs, merely as a simulation.
"""
def new(gen_func, default) do
{:ok, pid} = GenServer.start_link(__MODULE__, %{val: default, gen_func: gen_func})
{:ok, {:signal, :derived, pid}}
end
def add_child(parent, {:signal, _, child}) do
GenServer.call(parent, {:add_child, child})
end
end