Created
July 20, 2025 19:51
-
-
Save c-u-l8er/2a83d2f8163d02c64829254766f8887c to your computer and use it in GitHub Desktop.
Actor model system with navigable DSL for EnhancedADT modules. Provides hierarchical navigation instead of link-based references.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule EnhancedADT.Actors do | |
@moduledoc """ | |
Actor model system with navigable DSL for EnhancedADT modules. | |
Provides hierarchical navigation instead of link-based references. | |
""" | |
defmacro __using__(_opts) do | |
quote do | |
import EnhancedADT.Actors.DSL | |
import EnhancedADT.Actors.Navigation | |
import EnhancedADT.Actors.Exports | |
import EnhancedADT.Actors.Runtime | |
use EnhancedADT | |
end | |
end | |
end | |
defmodule EnhancedADT.Actors.DSL do | |
@moduledoc """ | |
Navigation-based DSL for defining actor modules and their interactions | |
""" | |
@doc """ | |
Define an actor module with navigable structure | |
## Example | |
```elixir | |
defactor Calculator do | |
state :: %{value: number(), history: list()} | |
exports do | |
add/1 -> :math/arithmetic | |
subtract/1 -> :math/arithmetic | |
get_history/0 -> :data/access | |
end | |
navigation do | |
:math -> MathOperations | |
:data -> DataOperations | |
:ui -> UIOperations | |
end | |
handles Add(amount) do | |
new_value = state.value + amount | |
history_entry = {:add, amount, new_value} | |
state | |
|> put_in([:value], new_value) | |
|> update_in([:history], &[history_entry | &1]) | |
end | |
end | |
``` | |
""" | |
defmacro defactor(name, do: body) do | |
{state_def, exports_def, navigation_def, handlers} = extract_actor_parts(body) | |
quote do | |
defmodule unquote(name) do | |
use GenServer | |
use EnhancedADT | |
# State definition | |
unquote(generate_state_type(state_def)) | |
# Message types (automatically derived from handlers) | |
unquote(generate_message_types(handlers)) | |
# Navigation structure | |
@navigation unquote(extract_navigation(navigation_def)) | |
@exports unquote(extract_exports(exports_def)) | |
# Actor metadata | |
def __actor_info__ do | |
%{ | |
name: unquote(name), | |
navigation: @navigation, | |
exports: @exports, | |
message_types: __message_types__() | |
} | |
end | |
# Navigation functions | |
unquote(generate_navigation_functions()) | |
# Export functions | |
unquote(generate_export_functions(exports_def)) | |
# GenServer callbacks | |
def start_link(initial_state \\ nil) do | |
GenServer.start_link(__MODULE__, initial_state, name: __MODULE__) | |
end | |
def init(initial_state) do | |
state = initial_state || default_state() | |
{:ok, state} | |
end | |
# Message handlers | |
unquote_splicing(generate_message_handlers(handlers)) | |
# Navigation-based message sending | |
unquote(generate_nav_send_functions()) | |
# Default state | |
unquote(generate_default_state(state_def)) | |
end | |
end | |
end | |
@doc """ | |
Define exports with navigation paths | |
""" | |
defmacro exports(do: export_list) do | |
quote do | |
@exports unquote(extract_export_mappings(export_list)) | |
end | |
end | |
@doc """ | |
Define navigation structure | |
""" | |
defmacro navigation(do: nav_list) do | |
quote do | |
@navigation unquote(extract_navigation_mappings(nav_list)) | |
end | |
end | |
@doc """ | |
Define message handlers with pattern matching | |
""" | |
defmacro handles(pattern, do: body) do | |
quote do | |
def handle_call(unquote(pattern), _from, state) do | |
new_state = (fn state -> unquote(body) end).(state) | |
{:reply, :ok, new_state} | |
end | |
def handle_cast(unquote(pattern), state) do | |
new_state = (fn state -> unquote(body) end).(state) | |
{:noreply, new_state} | |
end | |
end | |
end | |
# Helper functions for extracting actor parts | |
defp extract_actor_parts({:__block__, _, blocks}) do | |
Enum.reduce(blocks, {nil, nil, nil, []}, fn block, {state, exports, nav, handlers} -> | |
case block do | |
{:::, _, [{:state, _, _}, state_type]} -> | |
{state_type, exports, nav, handlers} | |
{:exports, _, [[do: export_block]]} -> | |
{state, export_block, nav, handlers} | |
{:navigation, _, [[do: nav_block]]} -> | |
{state, exports, nav_block, handlers} | |
{:handles, _, _} = handler -> | |
{state, exports, nav, [handler | handlers]} | |
_ -> | |
{state, exports, nav, [block | handlers]} | |
end | |
end) | |
end | |
defp extract_actor_parts(single_block), do: extract_actor_parts({:__block__, [], [single_block]}) | |
defp generate_state_type(state_def) when state_def != nil do | |
quote do | |
@type state :: unquote(state_def) | |
end | |
end | |
defp generate_state_type(_), do: quote(do: @type state :: any()) | |
defp generate_message_types(handlers) do | |
message_variants = Enum.map(handlers, fn | |
{:handles, _, [pattern, _]} -> extract_message_pattern(pattern) | |
_ -> nil | |
end) | |
|> Enum.filter(&(&1 != nil)) | |
if message_variants != [] do | |
quote do | |
defsum Message do | |
unquote_splicing(message_variants) | |
end | |
def __message_types__, do: unquote(message_variants) | |
end | |
else | |
quote do | |
def __message_types__, do: [] | |
end | |
end | |
end | |
defp extract_message_pattern({name, _, args}) when is_list(args) do | |
quote do: unquote(name)(unquote_splicing(args)) | |
end | |
defp extract_message_pattern({name, _, _}) do | |
quote do: unquote(name) | |
end | |
defp extract_message_pattern(name) when is_atom(name) do | |
quote do: unquote(name) | |
end | |
defp generate_navigation_functions() do | |
quote do | |
def navigate(path) when is_list(path) do | |
Enum.reduce(path, __MODULE__, fn segment, current_module -> | |
nav_map = current_module.__actor_info__().navigation | |
Map.get(nav_map, segment, current_module) | |
end) | |
end | |
def navigate(segment) when is_atom(segment) do | |
navigate([segment]) | |
end | |
def nav(path), do: navigate(path) | |
end | |
end | |
defp generate_export_functions(exports_def) when exports_def != nil do | |
exports = extract_export_mappings(exports_def) | |
Enum.map(exports, fn {func_spec, nav_path} -> | |
{func_name, arity} = parse_func_spec(func_spec) | |
quote do | |
def unquote(func_name)(unquote_splicing(generate_args(arity))) do | |
target_module = navigate(unquote(nav_path)) | |
message = {unquote(func_name), unquote_splicing(generate_args(arity))} | |
GenServer.call(target_module, message) | |
end | |
end | |
end) | |
end | |
defp generate_export_functions(_), do: [] | |
defp generate_args(0), do: [] | |
defp generate_args(arity) do | |
Enum.map(1..arity, fn i -> Macro.var(:"arg#{i}", nil) end) | |
end | |
defp parse_func_spec({:/, _, [name, arity]}) when is_atom(name) and is_integer(arity) do | |
{name, arity} | |
end | |
defp extract_export_mappings({:__block__, _, mappings}) do | |
Enum.map(mappings, &extract_single_export/1) | |
end | |
defp extract_export_mappings(single_mapping) do | |
[extract_single_export(single_mapping)] | |
end | |
defp extract_single_export({:->, _, [func_spec, nav_path]}) do | |
path_list = case nav_path do | |
{:/, _, [base, segment]} -> [base, segment] | |
single when is_atom(single) -> [single] | |
_ -> nav_path | |
end | |
{func_spec, path_list} | |
end | |
defp extract_navigation(nav_def) when nav_def != nil do | |
extract_navigation_mappings(nav_def) | |
end | |
defp extract_navigation(_), do: %{} | |
defp extract_navigation_mappings({:__block__, _, mappings}) do | |
mappings | |
|> Enum.map(&extract_single_navigation/1) | |
|> Enum.into(%{}) | |
end | |
defp extract_navigation_mappings(single_mapping) do | |
[extract_single_navigation(single_mapping)] |> Enum.into(%{}) | |
end | |
defp extract_single_navigation({:->, _, [key, module]}) do | |
{key, module} | |
end | |
defp extract_exports(exports_def) when exports_def != nil do | |
extract_export_mappings(exports_def) |> Enum.into(%{}) | |
end | |
defp extract_exports(_), do: %{} | |
defp generate_message_handlers(handlers) do | |
Enum.map(handlers, fn | |
{:handles, _, [pattern, [do: body]]} -> | |
quote do | |
def handle_call(unquote(pattern), _from, state) do | |
result = (fn state -> unquote(body) end).(state) | |
case result do | |
{reply, new_state} -> {:reply, reply, new_state} | |
new_state -> {:reply, :ok, new_state} | |
end | |
end | |
def handle_cast(unquote(pattern), state) do | |
new_state = (fn state -> unquote(body) end).(state) | |
{:noreply, new_state} | |
end | |
end | |
_ -> nil | |
end) | |
|> Enum.filter(&(&1 != nil)) | |
end | |
defp generate_nav_send_functions() do | |
quote do | |
def send_to(path, message, opts \\ []) do | |
target = navigate(path) | |
mode = Keyword.get(opts, :mode, :call) | |
case mode do | |
:call -> GenServer.call(target, message) | |
:cast -> GenServer.cast(target, message) | |
:info -> send(target, message) | |
end | |
end | |
def call(path, message), do: send_to(path, message, mode: :call) | |
def cast(path, message), do: send_to(path, message, mode: :cast) | |
def notify(path, message), do: send_to(path, message, mode: :info) | |
end | |
end | |
defp generate_default_state(state_def) when state_def != nil do | |
quote do | |
def default_state() do | |
# Generate appropriate default based on state type | |
case unquote(state_def) do | |
{:%, _, [struct_name, _]} -> struct(struct_name) | |
_ -> %{} | |
end | |
end | |
end | |
end | |
defp generate_default_state(_) do | |
quote do | |
def default_state(), do: %{} | |
end | |
end | |
end | |
defmodule EnhancedADT.Actors.Navigation do | |
@moduledoc """ | |
Navigation utilities for actor hierarchies | |
""" | |
@doc """ | |
Navigate through actor hierarchy using path notation | |
## Examples | |
```elixir | |
# Single level navigation | |
nav(:math) |> call(Add(5)) | |
# Multi-level navigation | |
nav([:ui, :forms, :validation]) |> call(Validate(data)) | |
# Pipe-friendly navigation | |
Calculator | |
|> nav(:math) | |
|> nav(:arithmetic) | |
|> call(Add(10)) | |
``` | |
""" | |
defmacro nav(module, path) do | |
quote do | |
unquote(module).navigate(unquote(path)) | |
end | |
end | |
@doc """ | |
Broadcast message to all actors in a navigation subtree | |
""" | |
defmacro broadcast(path, message, opts \\ []) do | |
quote do | |
EnhancedADT.Actors.Navigation.broadcast_impl( | |
unquote(path), | |
unquote(message), | |
unquote(opts) | |
) | |
end | |
end | |
def broadcast_impl(path, message, opts) do | |
# Implementation for broadcasting to actor subtrees | |
# This would traverse the navigation tree and send to all matching actors | |
:ok | |
end | |
@doc """ | |
Query actor hierarchy for capabilities | |
""" | |
defmacro query(path, capability) do | |
quote do | |
EnhancedADT.Actors.Navigation.query_capability( | |
unquote(path), | |
unquote(capability) | |
) | |
end | |
end | |
def query_capability(path, capability) do | |
# Implementation for querying actor capabilities | |
# Returns list of actors that support the given capability | |
[] | |
end | |
end | |
defmodule EnhancedADT.Actors.Exports do | |
@moduledoc """ | |
Export management for actor modules | |
""" | |
@doc """ | |
Export functions with automatic navigation binding | |
""" | |
defmacro export_to(target_path, functions) do | |
quote do | |
@export_bindings Map.put(@export_bindings || %{}, unquote(target_path), unquote(functions)) | |
end | |
end | |
@doc """ | |
Import functions from other actors with navigation | |
""" | |
defmacro import_from(source_path, functions, opts \\ []) do | |
alias_prefix = Keyword.get(opts, :as) | |
function_imports = Enum.map(functions, fn func -> | |
{func_name, arity} = parse_function_spec(func) | |
imported_name = if alias_prefix do | |
:"#{alias_prefix}_#{func_name}" | |
else | |
func_name | |
end | |
quote do | |
def unquote(imported_name)(unquote_splicing(generate_args(arity))) do | |
target = navigate(unquote(source_path)) | |
GenServer.call(target, {unquote(func_name), unquote_splicing(generate_args(arity))}) | |
end | |
end | |
end) | |
quote do | |
unquote_splicing(function_imports) | |
end | |
end | |
defp parse_function_spec({:/, _, [name, arity]}), do: {name, arity} | |
defp parse_function_spec(name) when is_atom(name), do: {name, 0} | |
defp generate_args(0), do: [] | |
defp generate_args(arity) do | |
Enum.map(1..arity, fn i -> Macro.var(:"arg#{i}", nil) end) | |
end | |
end | |
defmodule EnhancedADT.Actors.Runtime do | |
@moduledoc """ | |
Runtime utilities for actor system | |
""" | |
@doc """ | |
Start an actor hierarchy with navigation structure | |
""" | |
defmacro start_hierarchy(root_actor, do: structure) do | |
quote do | |
supervisor_spec = EnhancedADT.Actors.Runtime.build_supervisor_spec( | |
unquote(root_actor), | |
unquote(structure) | |
) | |
Supervisor.start_link(supervisor_spec, strategy: :one_for_one) | |
end | |
end | |
def build_supervisor_spec(root_actor, structure) do | |
# Build supervisor specification from navigation structure | |
# This would analyze the navigation tree and create appropriate child specs | |
[ | |
{root_actor, []} | |
] | |
end | |
@doc """ | |
Hot-swap actor implementations while preserving state | |
""" | |
def hot_swap(actor_module, new_implementation) do | |
# Implementation for hot-swapping actor code | |
:ok | |
end | |
@doc """ | |
Actor lifecycle management | |
""" | |
def lifecycle(actor, event) do | |
case event do | |
:start -> GenServer.start_link(actor, []) | |
:stop -> GenServer.stop(actor) | |
:restart -> | |
GenServer.stop(actor) | |
GenServer.start_link(actor, []) | |
:pause -> GenServer.call(actor, :pause) | |
:resume -> GenServer.call(actor, :resume) | |
end | |
end | |
end | |
# Example usage with EnhancedADT integration | |
defmodule EnhancedADT.Actors.Examples do | |
use EnhancedADT.Actors | |
# Calculator actor with navigation structure | |
defactor Calculator do | |
state :: %{value: number(), history: list(), status: atom()} | |
exports do | |
add/1 -> :math/arithmetic | |
subtract/1 -> :math/arithmetic | |
multiply/1 -> :math/arithmetic | |
divide/1 -> :math/arithmetic | |
get_result/0 -> :data/access | |
get_history/0 -> :data/access | |
clear/0 -> :control/reset | |
end | |
navigation do | |
:math -> MathModule | |
:data -> DataModule | |
:control -> ControlModule | |
:ui -> UIModule | |
end | |
# Message types defined using EnhancedADT | |
defsum Operation do | |
Add(number()) | |
Subtract(number()) | |
Multiply(number()) | |
Divide(number()) | |
Clear | |
GetResult | |
GetHistory | |
end | |
handles Add(amount) do | |
new_value = state.value + amount | |
entry = {:add, amount, new_value, DateTime.utc_now()} | |
state | |
|> Map.put(:value, new_value) | |
|> Map.update(:history, [entry], &[entry | &1]) | |
end | |
handles Subtract(amount) do | |
new_value = state.value - amount | |
entry = {:subtract, amount, new_value, DateTime.utc_now()} | |
state | |
|> Map.put(:value, new_value) | |
|> Map.update(:history, [entry], &[entry | &1]) | |
end | |
handles GetResult(_from, state) do | |
{:reply, state.value, state} | |
end | |
handles GetHistory(_from, state) do | |
{:reply, state.history, state} | |
end | |
handles Clear do | |
%{value: 0, history: [], status: :ready} | |
end | |
end | |
# Math operations module | |
defactor MathModule do | |
navigation do | |
:arithmetic -> ArithmeticModule | |
:advanced -> AdvancedMathModule | |
end | |
import_from [:calculator], [add/1, subtract/1], as: :calc | |
end | |
# Usage examples | |
def example_usage do | |
# Start the calculator | |
{:ok, _pid} = Calculator.start_link(%{value: 0, history: [], status: :ready}) | |
# Navigate and call operations | |
Calculator | |
|> nav(:math) | |
|> nav(:arithmetic) | |
|> call(Add(5)) | |
# Pipe-friendly operations | |
Calculator | |
|> call(Add(10)) | |
|> call(Subtract(3)) | |
|> call(GetResult()) | |
# Broadcast to multiple calculation modules | |
broadcast([:math], Clear()) | |
# Query for specific capabilities | |
math_actors = query([:math], :arithmetic) | |
# Use fold operations on calculation history | |
history = Calculator.call(GetHistory()) | |
fold history, state: 0 do | |
{:add, amount, _, _} -> state + amount | |
{:subtract, amount, _, _} -> state - amount | |
_ -> state | |
end | |
end | |
def start_calculator_hierarchy do | |
start_hierarchy Calculator do | |
Calculator -> [ | |
{:math, MathModule} -> [ | |
{:arithmetic, ArithmeticModule}, | |
{:advanced, AdvancedMathModule} | |
], | |
{:data, DataModule}, | |
{:ui, UIModule} | |
] | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment