defmodule Scopes.Core do alias Scopes.Core.Actor alias Scopes.Core.Shape alias Scopes.Util require Logger require Util def neuron(scope) do #Logger.info(Util.show [scope]) Actor.create(fn msg -> process(msg, scope) end) end def update(scope) do Actor.become(self(), fn msg -> process(msg, scope) end) end def synapse(rcvr, op, delay \\ 0) do op = is_list(op) && compose(op) || op fn msg -> Actor.send(rcvr, op.(msg), delay) end end # message handlers / proc steps def process(msg, scope) do proc(scope).(msg, scope) end def notify(msg, scope) do Logger.info(Util.show [msg]) Actor.send(env(scope), msg) end def forward(msg, scope) do #for s <- syns(scope), reduce: 0, do: (acc -> s.(msg); acc + 1) for s <- syns(scope), reduce: false, do: (_acc -> s.(msg)) #Enum.reduce(syns(scope), false, fn s, _acc -> s.(msg); true end) end def create(msg, scope = {state, proc, _syns, env}) do new = neuron({state, proc, [], env}) notify_created(msg, new, scope) new end def connect(msg, {state, proc, syns, env}) do data = Shape.data(msg) op = data[:op] || [] syn = synapse(data[:target], op, 0) update({state, proc, [syn | syns], env}) end # synapse operations def filter_head(pat) do fn msg -> if List.starts_with?(Shape.head(msg), pat), do: msg end end def filter_address(pat) do fn msg -> [dom, _act | rest] = Shape.head(msg) if List.starts_with?([dom | rest], pat), do: msg end end def data_only(), do: filter_head([:csys, :data]) # send shortcuts def send_message(rcvr, head, data \\ %{}) do Logger.debug(Util.show [rcvr, head, data]) Actor.send(rcvr, Shape.create(head, data: data)) end def send_value(rcvr, val) do send_message(rcvr, [:csys, :data], %{value: val}) end # notifications def notify_created(msg, new, scope) do msg1 = Shape.create(~w(csys created)a, data: Map.merge(Shape.data(msg), %{old: self(), new: new})) notify(msg1, scope) end # scope access shortcuts def state(scope), do: elem(scope, 0) def proc(scope), do: elem(scope, 1) def syns(scope), do: elem(scope, 2) def env(scope), do: elem(scope, 3) # helpers def compose(ops) do &(for op <- ops, reduce: &1 do nil -> nil val -> op.(val) end) end end