From e8d87dc85366f39ea332055064cf04733daa381f Mon Sep 17 00:00:00 2001 From: l4e21 Date: Mon, 8 Jun 2026 15:58:36 +0100 Subject: [PATCH 1/9] Added async sends --- .mnesiastore/LATEST.LOG | Bin 65643 -> 65579 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL.ex | 59 +++++++++++++++++++--------------------- lib/AL/application.ex | 29 ++++++++++++++++---- lib/AL/command.ex | 10 +++---- lib/AL/objects.ex | 3 +- lib/AL/scheduler.ex | 52 +++-------------------------------- lib/examples/e_AL.ex | 29 +++++++++++++++----- 8 files changed, 83 insertions(+), 99 deletions(-) diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index 3fbfe36e597a367c1b8c1c031b5144bd097fa5bd..20ffbab199ba064b50e7258209c9e34a55f8a652 100644 GIT binary patch delta 3820 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 delta 202 zcmdnwv&m;ew-|3!^R7lMsm&639E{cfCtr}rnwVg**-E^Vk$010a~o7<_rJ{-ByKbE z-dxGi4i)WNfxlu4m-c7C!?Mwgw67Z~m%c%Lo88v`xPN diff --git a/lib/AL.ex b/lib/AL.ex index 79802b1..5589a6c 100644 --- a/lib/AL.ex +++ b/lib/AL.ex @@ -70,7 +70,7 @@ defmodule AL do | {:retract_super, AL.Var.t(), AL.Var.t()} | {:retract_method, AL.Var.t(), AL.Var.t(), AL.Var.t()} | {:retract_oapply, AL.Var.t(), AL.Var.t()} - | {:spawn_process, AL.Var.t(), AL.Var.t(), [goal()]} + | {:send_async, AL.Var.t(), AL.Var.t(), AL.Var.t()} | {:gensym, AL.Var.t()} | {:print, AL.Var.t()} | :fail @@ -174,8 +174,8 @@ defmodule AL do def ast_to_pattern({:findall, _, [template, condition, result]}), do: {:findall, ast_to_pattern(template), ast_to_pattern(condition), ast_to_pattern(result)} - def ast_to_pattern({:spawn_process, _, [object, head, body]}), - do: {:spawn_process, ast_to_pattern(object), ast_to_pattern(head), ast_to_pattern(body)} + def ast_to_pattern({:send_async, _, [object, method, args]}), + do: {:send_async, ast_to_pattern(object), ast_to_pattern(method), ast_to_pattern(args)} def ast_to_pattern({:defmethod, _, [class, method_name, head, body]}) do {:oapply, :defmethod, [ @@ -275,7 +275,7 @@ defmodule AL do tx_id: tx_id, trace: [], program: program - }) + }) if result.active_choicepoint.bindings == nil do :mnesia.abort(format_failure(result.trace)) @@ -355,7 +355,7 @@ defmodule AL do @spec continue(t()) :: t() | nil def continue(nil), do: nil - def continue(state) do + def continue(state) do cond do state.active_choicepoint.bindings == nil -> backtrack(state) @@ -394,7 +394,6 @@ defmodule AL do } result = interp(goal, next_frame) - continue(result) end end @@ -583,9 +582,7 @@ defmodule AL do AL.Var.unify({k_pattern, v_pattern}, pair, state.active_choicepoint.bindings) end) |> Enum.filter(fn t -> t end) do - [] -> - backtrack(state) - + [] -> backtrack(state) [choice | next_choices] -> %AL{ state @@ -652,19 +649,19 @@ defmodule AL do %AL{ state | active_choicepoint: %AL.Choicepoint{ - goals: body_pattern, - bindings: - AL.Var.unify( - {head_pattern, id}, - {bind_head_pattern, method_id_pattern}, - state.active_choicepoint.bindings - ), - continuations: [continuation | state.active_choicepoint.continuations], - goal_pointer: 0, - scope_pointer: freshener - }, - choicepoint_stack: - alternative_choicepoints ++ [{:mark, freshener} | state.choicepoint_stack] + goals: body_pattern, + bindings: + AL.Var.unify( + {head_pattern, id}, + {bind_head_pattern, method_id_pattern}, + state.active_choicepoint.bindings + ), + continuations: [continuation | state.active_choicepoint.continuations], + goal_pointer: 0, + scope_pointer: freshener + }, + choicepoint_stack: + alternative_choicepoints ++ [{:mark, freshener} | state.choicepoint_stack] } end end @@ -673,11 +670,12 @@ defmodule AL do %AL{ state | active_choicepoint: state.active_choicepoint, - choicepoint_stack: - Enum.drop_while(state.choicepoint_stack, fn choice -> - case choice do - {:mark, f} -> f != state.active_choicepoint.scope_pointer - _choice -> true + choicepoint_stack: + Enum.drop_while(state.choicepoint_stack, fn choice -> + case choice do + {:mark, f} -> + f != state.active_choicepoint.scope_pointer + _choice -> true end end) } @@ -849,12 +847,11 @@ defmodule AL do state end - def interp({:spawn_process, object, head, body}, state) do - AL.Command.spawn_process(state.tx_id, object, head, body) - + def interp({:send_async, object, method, args}, state) do + AL.Command.send_async(state.tx_id, object, method, args) state end - + def interp({:gensym, var}, state) do sym = :crypto.strong_rand_bytes(16) |> Base.encode16(case: :lower) |> String.to_atom() diff --git a/lib/AL/application.ex b/lib/AL/application.ex index d0ae979..315e3f1 100644 --- a/lib/AL/application.ex +++ b/lib/AL/application.ex @@ -66,13 +66,19 @@ defmodule AL.Application do ) do class(self, class) implies( - [lookup(class, method, id)], + [method(self, method, id)], [ - print(["calling", id, "from", class, "with args", [self | args]]), + print(["calling", id, "from", self, "with args", [self | args]]), oapply(id, [self | args]) ], - [:fail] - ) + [implies( + [lookup(class, method, id)], + [ + print(["calling", id, "from", class, "with args", [self | args]]), + oapply(id, [self | args]) + ], + [:fail] + )]) end set_class(:defmethod, :behaviour) @@ -136,11 +142,22 @@ defmodule AL.Application do end send(:class, :new, [%{name: :process, super: :object, slots: []}, _]) - defmethod(:process, :init, [self, args, new_obj]) do + defmethod(:process, :allocate, [self, args, new_obj]) do + class(self, meta) + + map_get(args, :method, method_name) map_get(args, :head, head) map_get(args, :body, body) + gensym(new_obj) - spawn_process(new_obj, head, body) + + set_class(new_obj, meta) + set_super(new_obj, :object) + + fresh_id(impl) + set_method(new_obj, method_name, impl) + set_class(impl, :behaviour) + set_oapply(impl, head, body) end end end diff --git a/lib/AL/command.ex b/lib/AL/command.ex index 6197531..a1819a5 100644 --- a/lib/AL/command.ex +++ b/lib/AL/command.ex @@ -24,7 +24,7 @@ defmodule AL.Command do | {:retract_super, {AL.Var.t(), AL.Var.t()}} | {:retract_method, {AL.Var.t(), AL.Var.t(), AL.Var.t()}} | {:retract_oapply, {AL.Var.t(), AL.Var.t()}} - | {:spawn_process, {AL.Var.t(), AL.Var.t(), [AL.goal()]}} + | {:send_async, {AL.Var.t(), AL.Var.t(), AL.Var.t()}} @doc """ Initialise the event log, or re-use the one on disc. @@ -176,11 +176,11 @@ defmodule AL.Command do write_command(tx_id, {:retract_oapply, {object, head}}) end - @spec spawn_process(non_neg_integer(), AL.Var.t(), AL.Var.t(), [AL.goal()]) :: :ok - def spawn_process(tx_id, object, head, body) do - write_command(tx_id, {:spawn_process, {object, head, body}}) + @spec send_async(non_neg_integer(), AL.Var.t(), AL.Var.t(), AL.Var.t()) :: :ok + def send_async(tx_id, object, method, args) do + write_command(tx_id, {:send_async, {object, method, args}}) end - + @spec write_command(non_neg_integer(), command()) :: :ok def write_command(tx_id, command) do {t1, _t2} = inc_system_time() diff --git a/lib/AL/objects.ex b/lib/AL/objects.ex index d6ed94a..d80b9b2 100644 --- a/lib/AL/objects.ex +++ b/lib/AL/objects.ex @@ -211,9 +211,8 @@ defmodule AL.Objects do :mnesia.write({:slots, object, merged}) - :spawn_process -> + :send_async -> :ok - # TODO Consider whether this should be where the scheduler hydration occurs end end diff --git a/lib/AL/scheduler.ex b/lib/AL/scheduler.ex index 482c914..6ab0279 100644 --- a/lib/AL/scheduler.ex +++ b/lib/AL/scheduler.ex @@ -5,50 +5,19 @@ defmodule AL.Scheduler do GenServer.start_link(__MODULE__, args, name: __MODULE__) end - def processes() do - GenServer.call(__MODULE__, :processes) - end - - @impl true - def handle_call(:processes, _from, state) do - {:reply, state.processes, state} - end - @impl true def init(_opts) do :mnesia.subscribe({:table, :command, :detailed}) - - {:atomic, commands} = :mnesia.transaction(fn -> AL.Command.commands_since(0) end) - - processes = - for {:command, t, _, {:spawn_process, {object, head, body}}} <- commands, into: %{} do - {object, start_process(object, head, body, t)} - end - - {:ok, %{processes: processes}} + {:ok, %{}} end @impl true def handle_info( {:mnesia_table_event, - {:write, :command, {:command, t, _tx_id, {:spawn_process, {object, head, body}}}, _old, _tid}}, - state - ) do - {:noreply, %{state | processes: Map.put(state.processes, object, start_process(object, head, body, t))}} - end - - @impl true - def handle_info( - {:mnesia_table_event, {:write, :command, {:command, t, _tx_id, command}, _old, _tid}}, + {:write, :command, {:command, _t, _tx_id, {:send_async, {object, method, args}}}, _old, _tid}}, state - ) do - Enum.each(state.processes, fn {_object, {head, pid}} -> - case AL.Var.unify(command, head, AL.Var.empty_bindings()) do - nil -> :ok - bindings -> send(pid, {:dispatch, t, bindings}) - end - end) - + ) do + Task.start(fn -> AL.eval([{:oapply, :send, [object, method, args]}]) end) {:noreply, state} end @@ -56,17 +25,4 @@ defmodule AL.Scheduler do def handle_info({:mnesia_table_event, _}, state) do {:noreply, state} end - - defp start_process(_object, head, body, _spawn_t) do - pid = spawn_link(fn -> worker_loop(body) end) - {head, pid} - end - - defp worker_loop(body) do - receive do - {:dispatch, _t, bindings} -> - AL.eval(body, bindings) - worker_loop(body) - end - end end diff --git a/lib/examples/e_AL.ex b/lib/examples/e_AL.ex index 000bbd0..c7927ef 100644 --- a/lib/examples/e_AL.ex +++ b/lib/examples/e_AL.ex @@ -216,28 +216,27 @@ defmodule Examples.AL do end example create_process() do - head = {:set_class, {:"$object", :"$class"}} + head = [:"$self", :"$object", :"$class"] body = [{:set_slots, :"$object", %{processed: true}}] {:atomic, {bindings, _}} = run do - send(:process, :new, [%{head: ^head, body: ^body}, new_proc]) + send(:process, :new, [%{method: :handle, head: ^head, body: ^body}, new_proc]) cut - end - + end + new_proc = Map.get(bindings, :"$new_proc") assert is_atom(new_proc) - assert Enum.any?(AL.Scheduler.processes(), fn {_t, {h, _pid}} -> h == head end) bindings end example process_handles_command() do - create_process() + new_proc = Map.get(create_process(), :"$new_proc") {:atomic, _} = run do - set_class(:test_object, :some_class) + send_async(^new_proc, :handle, [:test_object, :test_class]) end Process.sleep(50) @@ -247,6 +246,22 @@ defmodule Examples.AL do assert Enum.any?(results, fn {:slots, _, slots} -> Map.get(slots, :processed) == true end) end + + example process_called_by_var() do + _new_proc = Map.get(create_process(), :"$new_proc") + + {:atomic, _} = + run do + send_async(proc, :handle, [:test_object_2, :test_class]) + end + + Process.sleep(50) + + {:atomic, results} = + :mnesia.transaction(fn -> AL.Objects.scan_slots(:test_object_2, :"$slots") end) + + assert Enum.any?(results, fn {:slots, _, slots} -> Map.get(slots, :processed) == true end) + end example arithmetic() do {:atomic, {bindings, result}} = run do From cd3140b7f2daacb538087ce68554e3d9988d6dbd Mon Sep 17 00:00:00 2001 From: l4e21 Date: Mon, 8 Jun 2026 16:30:58 +0100 Subject: [PATCH 2/9] Add bridge for elixir processes --- .mnesiastore/LATEST.LOG | Bin 65579 -> 68572 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL.ex | 11 +++++ lib/AL/application.ex | 14 +++++- lib/AL/command.ex | 6 +++ lib/AL/objects.ex | 3 ++ lib/AL/scheduler.ex | 10 ++++ lib/examples/e_AL_genserver.ex | 88 +++++++++++++++++++++++++++++++++ test/al_test.exs | 4 ++ 9 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 lib/examples/e_AL_genserver.ex diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index 20ffbab199ba064b50e7258209c9e34a55f8a652..592031d9702cdfc5c9aaab577d0b44138bf0310a 100644 GIT binary patch literal 68572 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 diff --git a/lib/AL.ex b/lib/AL.ex index 5589a6c..0a16d55 100644 --- a/lib/AL.ex +++ b/lib/AL.ex @@ -71,6 +71,7 @@ defmodule AL do | {:retract_method, AL.Var.t(), AL.Var.t(), AL.Var.t()} | {:retract_oapply, AL.Var.t(), AL.Var.t()} | {:send_async, AL.Var.t(), AL.Var.t(), AL.Var.t()} + | {:send_elixir, AL.Var.t(), AL.Var.t()} | {:gensym, AL.Var.t()} | {:print, AL.Var.t()} | :fail @@ -177,6 +178,9 @@ defmodule AL do def ast_to_pattern({:send_async, _, [object, method, args]}), do: {:send_async, ast_to_pattern(object), ast_to_pattern(method), ast_to_pattern(args)} + def ast_to_pattern({:send_elixir, _, [pid, message]}), + do: {:send_elixir, ast_to_pattern(pid), ast_to_pattern(message)} + def ast_to_pattern({:defmethod, _, [class, method_name, head, body]}) do {:oapply, :defmethod, [ ast_to_pattern(class), @@ -191,6 +195,8 @@ defmodule AL do def ast_to_pattern({name, _, _module}), do: AL.Var.var(name) + def ast_to_pattern({a, b}), do: {ast_to_pattern(a), ast_to_pattern(b)} + def ast_to_pattern(x), do: x @doc """ @@ -851,6 +857,11 @@ defmodule AL do AL.Command.send_async(state.tx_id, object, method, args) state end + + def interp({:send_elixir, pid, message}, state) do + AL.Command.send_elixir(state.tx_id, pid, message) + state + end def interp({:gensym, var}, state) do sym = :crypto.strong_rand_bytes(16) |> Base.encode16(case: :lower) |> String.to_atom() diff --git a/lib/AL/application.ex b/lib/AL/application.ex index 315e3f1..f6c8fdb 100644 --- a/lib/AL/application.ex +++ b/lib/AL/application.ex @@ -141,7 +141,19 @@ defmodule AL.Application do findall([head, body], [clause(self, head, body)], clauses) end - send(:class, :new, [%{name: :process, super: :object, slots: []}, _]) + send(:class, :new, [%{name: :elixir_process, super: :object, slots: []}, _]) + defmethod(:elixir_process, :allocate, [self, args, new_obj]) do + class(self, meta) + map_get(args, :name, new_obj) + set_class(new_obj, meta) + set_super(new_obj, :elixir_process) + end + defmethod(:elixir_process, :init, [self, args, self]) do + map_get(args, :pid, pid) + set_slots(self, %{pid: pid}) + end + + send(:class, :new, [%{name: :process, super: :object, slots: []}, _]) defmethod(:process, :allocate, [self, args, new_obj]) do class(self, meta) diff --git a/lib/AL/command.ex b/lib/AL/command.ex index a1819a5..2aa1035 100644 --- a/lib/AL/command.ex +++ b/lib/AL/command.ex @@ -25,6 +25,7 @@ defmodule AL.Command do | {:retract_method, {AL.Var.t(), AL.Var.t(), AL.Var.t()}} | {:retract_oapply, {AL.Var.t(), AL.Var.t()}} | {:send_async, {AL.Var.t(), AL.Var.t(), AL.Var.t()}} + | {:send_elixir, {AL.Var.t(), AL.Var.t()}} @doc """ Initialise the event log, or re-use the one on disc. @@ -180,6 +181,11 @@ defmodule AL.Command do def send_async(tx_id, object, method, args) do write_command(tx_id, {:send_async, {object, method, args}}) end + + @spec send_elixir(non_neg_integer(), pid(), term()) :: :ok + def send_elixir(tx_id, pid, message) do + write_command(tx_id, {:send_elixir, {pid, message}}) + end @spec write_command(non_neg_integer(), command()) :: :ok def write_command(tx_id, command) do diff --git a/lib/AL/objects.ex b/lib/AL/objects.ex index d80b9b2..8480908 100644 --- a/lib/AL/objects.ex +++ b/lib/AL/objects.ex @@ -213,6 +213,9 @@ defmodule AL.Objects do :send_async -> :ok + + :send_elixir -> + :ok end end diff --git a/lib/AL/scheduler.ex b/lib/AL/scheduler.ex index 6ab0279..e27ce73 100644 --- a/lib/AL/scheduler.ex +++ b/lib/AL/scheduler.ex @@ -21,6 +21,16 @@ defmodule AL.Scheduler do {:noreply, state} end + @impl true + def handle_info( + {:mnesia_table_event, + {:write, :command, {:command, _t, _tx_id, {:send_elixir, {pid, message}}}, _old, _tid}}, + state + ) do + send(pid, message) + {:noreply, state} + end + @impl true def handle_info({:mnesia_table_event, _}, state) do {:noreply, state} diff --git a/lib/examples/e_AL_genserver.ex b/lib/examples/e_AL_genserver.ex new file mode 100644 index 0000000..2e7bf91 --- /dev/null +++ b/lib/examples/e_AL_genserver.ex @@ -0,0 +1,88 @@ +defmodule Examples.ALGenserver do + @moduledoc """ + I demonstrate the pattern of registering an Elixir GenServer as an AL object. + """ + + use ExExample + use AL + import ExUnit.Assertions + + defmodule CounterService do + use GenServer + use AL + + def start_link(object_id) do + GenServer.start_link(__MODULE__, object_id) + end + + @impl true + def init(object_id) do + pid = self() + run do + send(:elixir_process, :new, [%{name: ^object_id, pid: ^pid}, _]) + defmethod(^object_id, :increment, [self, amount]) do + get_slot(self, :pid, p) + send_elixir(p, {:increment, amount}) + end + end + {:ok, %{object_id: object_id, count: 0}} + end + + @impl true + def handle_info({:increment, amount}, state) do + {:noreply, %{state | count: state.count + amount}} + end + + @impl true + def handle_info({:get_count, reply_to}, state) do + send(reply_to, {:count, state.count}) + {:noreply, state} + end + + @impl true + def terminate(_reason, state) do + object_id = state.object_id + run do + retract_class(^object_id, c) + retract_super(^object_id, s) + end + end + + def count(pid) do + send(pid, {:get_count, self()}) + receive do + {:count, n} -> n + after + 1000 -> :timeout + end + end + end + + example genserver_registers_as_al_object() do + {:ok, pid} = CounterService.start_link(:my_counter) + + {:atomic, results} = + :mnesia.transaction(fn -> AL.Objects.scan_class(:my_counter, :"$class") end) + + assert Enum.any?(results, fn {:class, _, c} -> c == :elixir_process end) + + {:atomic, _} = + run do + send_async(:my_counter, :increment, [5]) + end + + Process.sleep(50) + + assert CounterService.count(pid) == 5 + + GenServer.stop(pid) + Process.sleep(50) + + {:atomic, after_stop} = + :mnesia.transaction(fn -> AL.Objects.scan_class(:my_counter, :"$class") end) + + assert after_stop == [] + + :ok + end +end diff --git a/test/al_test.exs b/test/al_test.exs index ba1a3cc..fa4576a 100644 --- a/test/al_test.exs +++ b/test/al_test.exs @@ -9,3 +9,7 @@ end defmodule AlObjectsTest do use ExExample.ExUnit, for: Examples.ALObjects end + +defmodule ALGenserverTest do + use ExExample.ExUnit, for: Examples.ALGenserver +end From 0437afb7e0e78eb8d6815d868b754e7ffc25ae77 Mon Sep 17 00:00:00 2001 From: l4e21 Date: Mon, 8 Jun 2026 17:43:43 +0100 Subject: [PATCH 3/9] Clean up the bootstrap --- .mnesiastore/LATEST.LOG | Bin 68572 -> 65658 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL.ex | 9 ++ lib/AL/application.ex | 157 ++--------------------------- lib/AL/bootstrap/core.ex | 109 ++++++++++++++++++++ lib/AL/bootstrap/elixir_process.ex | 19 ++++ lib/AL/bootstrap/lists.ex | 69 +++++++++++++ lib/AL/bootstrap/process.ex | 26 +++++ lib/examples/e_AL.ex | 60 ++--------- 9 files changed, 248 insertions(+), 201 deletions(-) create mode 100644 lib/AL/bootstrap/core.ex create mode 100644 lib/AL/bootstrap/elixir_process.ex create mode 100644 lib/AL/bootstrap/lists.ex create mode 100644 lib/AL/bootstrap/process.ex diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index 592031d9702cdfc5c9aaab577d0b44138bf0310a..fc274acde6dcd13748bfadea57439b2f29a69854 100644 GIT binary patch literal 65658 zcmeHQXMh{Wb;bcW;Bdz~QdBRLC>|(LjgokzM5DS=S$z0*!{-u+-;=%m~4#xAH zZl_xhk9N9^Za-=imUFq%zb@w%j{VzmZuSE&zPk}`Tj_;C z6xO0(xfPNJI~)19*a_NUTqa@#ojSP@o!SWFxo!_M4(qjk7{SfiUeKdpQNzqWG2J3Ht*-A?NiN(j~r>NScT)ANFb+z3xWXi@4I;S9`&lac^fo>cZX7xDC17J=p}28->nH z?!zBjm^!`hqZf2$rjGnC{Z|gm+yc(>YhjN%K)#U=@<9&r4?dx z-!;Ai7A+2VKS)IXP1*sA?Jx>R#!~MqoC#hhR5DyH9~VxB;MA`Lt<7-azV`9gUl+c1 z>VGEZYlD43_qAMeu&>1nqV8fYy3Xq;lKaF9;<^5*eiXKAQL`OF)s?&%sO^_-M z+;Qzq63#a3P=jnj71EP@=$=W}ev@?beW*#p1fzc2AkFU?Oxmzi5Yv_de4Llx+@uTe z^hzt}_f-jA4%XlTwHs;e1a3=6ariW9H>6I9B3$Y&uZJtpbWp7)UWz~TuG69vpZ@R3 zmEyq;p_k$};&3I4!#$Ci!}T|zaX!NwPUw{GaP*CdJKTwnd_p+fzyH_d9B!~fym^N! zTO970>>RGujZh7j=%c(9Yq&nwX@J9#Hzw|I4<7qx;c!PL=x~D_!f?1l<2R`@7Kb~L zmBW>wH`M5=n5=Lnw5dud!{rLGXjJEz-Yj;j@QY@B;y!rEjvaX(m!100$yIuTeZ=s= ztH$@iip2+C&ddk9!Nx}G)S1=HmZVatK3F6}K5=omTR$~%zx&jVYlYvP{*TG|-C)1a z{mwy*Hl@dC*$0I%K7@n9>n(!>wggadmo~;wQ874Kl8Ceen8+6%sGS1PBnmysUt_ja`O?bX*!V6pz zUZ@kgDTzk^JL{5Y&}CmRF4B#fJ>kW!2`|wJ-MEbrMP=FCI|u z!$r#5e5g9kFW%57!VC!=jVD(`1fZ|>!`5m9zacPT-0ar+x`mUyp=d=4o{+7)BU0QEsO8cV(*}XJ1LGwmP8loL#SS){ zwo(sQxnk(&SOTaTI=^`1csSW6_OZm7Mknr?D?DxR&%wtRiv zo)WzhOYb15N&ka3Hl-?U%+mn)1XGA{R5X)|6vQ z6BgSmLPA|)mA6_2Et(}=c5abTh36oXlRhr42F(^M(zX<9RX?fHt?s22a$Z)|i^xIL zO=3ET*jZ0x8}|~no@lyCS9VNS>Cz7RBJ%o~RoGV3fZZS>Lx{Jm6E`8f80l@a(K2z# zs{p0-cgQ`YiU8tTOjqf1T_lUCpk7oqB9&xH4Ja$J8BlpqgGPUqnZ?O1@vB5@&3;^FJw^CI z&SFyjjOghly4=oiX5U0Ef8;YP9$9Z4`c}RCA*^`Bb>r5sBK0Fz` zFAS#y(3A1nhqGy2p?tN!E|tTnibQrzN|uh^mxN41l#basU+u7N69ceTd-5G+?F5eQ zsv4N|X<9h+7n%7;mRf59$XnidmmY;WKncg9`NzU9>j8Z`SPqr~{k5|B+S}{C=8+4gN+pu}J zS@IuEjFr4t`4H%Q5ohR^&50 zw34RSgH5qVnS%AQphXc?Lt1PukuJqZmntOGHcdK^mfW-tCmQ{d;!bEmL&xN3j08Fx zD{;03h_}%ycm_|_#=RxfWfxwgf3+kRk=4JrGfUqb`xiC|ey~#xP}zEFy7{ z^uSUEoup7x7;Q=kCCat{6a5)A)+Ql#VTfHxi1c|72{D5qW(Me_qQMybw3%r)hTLtF zDlNK^Qq5zC`6L8$K$LAZId))>9ZEG>l$0K%wLKF8Txq~du*LUs96Q2@|YAwTBNidl^EY)b0;H=bZL>S zk=m+c9!mztPe5y}MLpPg(?gXxj53!*5rgR%%~DaEg<;N0!pL#lAvhI`Q%T|s3Nz{K zpv<_7Fw#Xyr1XJl2{DZ!rYVFTqY5_?MiPH*mBn@pwmm72sw;-Yrx#x&dAuvkoo;B~ry2-Kg0UUgw%{ziYzlT@#8CRo=nMJ`ctEHfMxlWt=lYu}0b%p;*D^ zjPQ_aFTKe%;mxiI54$Ej;+pUl*MzsaCcMox;q9&o?{H0cr)$Eat_kmQO?bC!!h2j3 z-s_t1KG%fz>x6E?nX`4$tiu^&kTd(9=>xic*%LnKn(!glgb%wWJm#A45!ZyrHNsv0 zGX7rNZ4=pxyASW=EW!>P_y~vCbw@A71)53aD*@)n+vNKZuqjm~!go{VPsL^|MyIxq zF}{4G4oieYY(m8wk|2U*=!+t@7$E_CV}i&WYoa3k2GhaQ_A-3Zgr(*eBd0F+Lh`YS zFy2P3C$2_LPZwj)(6nTG*2w89tKC+;)(Tgnc!8!?*CS)h(lov1S|f_LD-d>LWR&u1 z3%(>0b|BA0cs4?w&GljbF38_miyH9`N{;U!cJ7^qmdZhm2WdXa=I~=_YM6c%+pFX~ zI={XS%T;Tuo1GPi+qL`{8G*n|!6t*%2Kg_7#i|5O-0hRQ{YcK&3n|bHKTT?;O50LK zDTo^p0UkTD9o0#LS)@^D2H3h`YKIHH#6E&mEU6k%q*6#GJ|ouGY+W$ zHICg1>`j%+up}6uDzFW3E2@E@Y57fZ$Fv+LSBZz2P^+zw0k!*7crtorD+bz|0}*sP zRUmI^jmh>+lU>jj7a>ZDs+FbRCezg-*hgniroANmiK-!mSO*hJ33S!?W`5R;L95C3 z+|x^|32=*nEP!uo+*?p8Yousx>AXUJoNzcEV2Pr-MJtn((nd_l+c;qa;I}GdHUdZp z^AUj>r)Zh!hd~Qb-(cY)N~7kDpph*Zv}$ApPnDH4XHsvV8l06tw9A#bG+3IBp#7&1Ft5wql#rMY)DbfrY ziARHxpiiapnRKx1t|an^)gZhJuR1vRHqpL7_f^7Y;W~A)!IJ*aO))N@u&Lf583B1~>Xco0K8s8o_=1~6z@fJP$wAXJKV42yNl zB37cQr$VMy-Fq?K-lU~i#mZYO)Gw3*ka^~<>~T}U(`!A{@Q^p5Y0kRSFLk~Z>wIf61BZ3fB@j6uL|s^nmy(eXSf)gn z674z+eVqk*)ZrkB_%KF%IH`4_6b!2|_G7^PN<^bl9X9AI1-uR8-KOwl^&#_xI*c4H zGGb(jcR$9vU*Q?@r45TpoVzg2T?$9aW$UWF$r9pa%t>l|C)W5*s%NIIsOM@5@x1jqXf}ywU71-GEj}-EH zEb;Y8(_(>#+4T{O^@v6LM3E*c@o|K#V|1zU?O4j&EmG>m8f65vGHVvZT|A7CG9pRy z)=ZT)bVMme8IzalT!q!SDw%;~2F)J(O9*l-uDY;T1UmuIeE2Ycgu4f0-;)&2Y|e+t zdIiS2Lg9&R{j9Sic7fV@J9l7&JCwFY=~pA=gXYPkp0{AsTT)T2vv#Cs2UbnroHuxe z*URR-bF^veoSPLZ#&=cRkQ(1saYJf+U&Rfn@r@NXq+Y(W;`}=#kLtxG`?W}qxh8yE zCv@XH#^+t!kb3#Vi}48*H<}usL2*NBd!W5G`l;j^G)-BPsETeETqd`*6zJ zV&WygNT%@66%DRndO-HKJY^<6o{wnC{XouA#63bn!Vaaue%cVjnin^$`|7mViM~EZ7m5mp(QOlD#>_Y zS&k}{&o7eMGIS-ZfX&K`Ol^t-cdOwF8%_9-c@_RA%ZI4Vm_F*PSQrggFL6iKRx=vn z69HMo85>X3`S7qXQfwo8gHYmY0%CI*^K6!*1})7bx{$(1 zxR{8RA+-sK5Bbtml#s_UCb!~xSAPhhAgl2F9TLq=h&2!UX+@p0j9F1Tt`XH_PAl1Jy z=g7Rq8Axs%$@w`*&*}M@eeChPYr+#cp__d4a=esrRF4}?ul8YHFGuzmN7`gPDqx&6 zmwm~6LC?wT314(g_>x9g`r!DZ0t*uv6)5r8iF`jXF7x15MYiIO*7X-5Xkqb2GRKN9 zhj&uvNT`|qO-KP|4Om5JseNAwZ+)?*D)P@dkw3#*78_h+(JP-$$aj^%!Qai z#GAy0^gH4jOtN0K3ElAZWBQ#AXF2$)Yr;S$bW<9|fiF&LSaD*BQ&KTi=9E;-9Wv75lj8^A+bse3 zdtt#V{TI(~kTW~rOqXD}+oPWXpNi)@-A=b29_@4+T{!WCPTEZ4=xO)Tp`~Eurkj@P zhYu{>7%nY`ONVYav~qZLb>)VemkzEjtFUV5t1_DB&P#bj^a-6i{3T#=sdeq{)dMCMJ(NhbjsES`niT zeK#&Of=;~^#*`R`p)80T3P6lM;;Xhml;yPSlfQ)fX6Y3Ym;aNUp^r{KCFHBQ2HLu#Cc>xR@g5!Vf=aVoAGQsZP?H>AetxNb=A z@zD)7PRVtnsc}-S8&WT)I;M%d9N`y;zm6#DHh7Z!F4^lBp<8NIO1AyFN%ae2{*I57M3w(!LMU z$Oq}B57O6skj6epw|tPoDt^2y$xUbbGkuVLmJia;_CfkNK1e^;2kAR}kba&I($Dun z`UO5n-|2(&3w@A&kq^=@_CfkCAEdAQApH^_q+jZT^visZzS{@sdwh_-*9YmB`yl-a zAEaODgY>I>kbbof(y#GB`aU0|@ApCawLVC{&Ijq&`yl-WAEe*tgY*MFNI&R<^qYK; zezOnKZ}CC;tv*P<%?Ig+e2{*-57O`OLHeCONWaSm>391e{jd+xkN6<{9v`IN>x157Hm-LHdI}NPox&=@0uL{ShCeKk9?@$9$0fxDV2g`XK!YAEc*!kp83((x37{ z`qMs0f5r#t&-x(!IUl4y?}PLge31U457J-qLHf%+NPoo#>96`A{WTw?AM-)_>pn<- z!w2ba`XK$d57OWALHgT1NPou%>F@d={XHL~zwd+e4}6e*!UyRe`XK!yAEbZmgY-{) zkp8I;(m(S-`sY4K|H239U-}^ZD<7nP?Su4he31UF57JNiApJWZq<`;&^dEeXe#!^w LKl&j3Cq(*xx1kUE literal 68572 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 diff --git a/lib/AL.ex b/lib/AL.ex index 0a16d55..ce8718b 100644 --- a/lib/AL.ex +++ b/lib/AL.ex @@ -427,6 +427,14 @@ defmodule AL do } } end + else if is_list(object_pattern) do + %AL{ + state + | active_choicepoint: %AL.Choicepoint{ + state.active_choicepoint + | bindings: AL.Var.unify(:list, class_pattern, state.active_choicepoint.bindings) + } + } else case AL.Objects.scan_class(object_pattern, class_pattern) do [] -> @@ -459,6 +467,7 @@ defmodule AL do } end end + end end def interp({:get_super, object_pattern, super_pattern}, state) do diff --git a/lib/AL/application.ex b/lib/AL/application.ex index f6c8fdb..214281a 100644 --- a/lib/AL/application.ex +++ b/lib/AL/application.ex @@ -5,7 +5,6 @@ defmodule AL.Application do """ use Application - use AL @impl true def start(_type, _args) do @@ -22,155 +21,13 @@ defmodule AL.Application do def bootstrap() do case :mnesia.table_info(:command, :size) do - 0 -> do_bootstrap() - _ -> :ok - end - end - - defp do_bootstrap() do - run do - set_class(:class, :class) - set_class(:object, :class) - set_class(:behaviour, :class) - - set_super(:class, :object) - set_super(:behaviour, :object) - - set_method(:object, :lookup, :lookup) - set_method(:object, :send, :send) - set_method(:object, :meta, :metaclass) - set_method(:object, :defmethod, :defmethod) - - set_class(:metaclass, :behaviour) - - set_oapply(:metaclass, [self, class, meta]) do - class(self, class) - class(class, meta) - end - - set_class(:lookup, :behaviour) - set_oapply( - :lookup, - [self, name, id] - ) do - alternative([method(self, name, id)], - [super(self, super), - lookup(super, name, id)]) - end - - set_class(:send, :behaviour) - - set_oapply( - :send, - [self, method, args] - ) do - class(self, class) - implies( - [method(self, method, id)], - [ - print(["calling", id, "from", self, "with args", [self | args]]), - oapply(id, [self | args]) - ], - [implies( - [lookup(class, method, id)], - [ - print(["calling", id, "from", class, "with args", [self | args]]), - oapply(id, [self | args]) - ], - [:fail] - )]) - end - - set_class(:defmethod, :behaviour) - set_oapply(:defmethod, [self, method_name, head, body]) do - fresh_id(impl) - set_method(self, method_name, impl) - set_class(impl, :behaviour) - set_oapply(impl, head, body) - end - - set_class(:map_get, :behaviour) - set_method(:map, :map_get, :map_get) - - defmethod(:class, :construct, [self, %{class: self}]) do - end - - set_method(:class, :allocate, :allocate_class) - set_class(:allocate_class, :behaviour) - - set_oapply( - :allocate_class, - [self, args, name] - ) do - map_get(args, :name, name) - map_get(args, :super, super) - map_get(args, :slots, slots) - - class(self, meta) - - set_class(name, meta) - set_super(name, super) - set_slots(name, slots) - end - - defmethod(:object, :allocate, [self, _, self]) do - print(["allocate", self]) - end - - defmethod(:object, :init, [self, _, self]) do - print(["initialise", self]) - end - - defmethod(:class, :new, [self, args, new]) do - send(self, :construct, [construct]) - send(construct, :allocate, [args, alloc]) - send(alloc, :init, [args, new]) - end - - defmethod(:object, :examine, [self, %{classes: classes, - objects: objects, - supers: supers, - subs: subs, - methods: methods, - clauses: clauses}]) do - findall(c, [class(self, c)], classes) - findall(c, [class(c, self)], objects) - findall(s, [super(self, s)], supers) - findall(sub, [super(sub, self)], subs) - findall([n, id], [method(self, n, id)], methods) - findall([head, body], [clause(self, head, body)], clauses) - end - - send(:class, :new, [%{name: :elixir_process, super: :object, slots: []}, _]) - defmethod(:elixir_process, :allocate, [self, args, new_obj]) do - class(self, meta) - map_get(args, :name, new_obj) - set_class(new_obj, meta) - set_super(new_obj, :elixir_process) - end - defmethod(:elixir_process, :init, [self, args, self]) do - map_get(args, :pid, pid) - set_slots(self, %{pid: pid}) - end - - send(:class, :new, [%{name: :process, super: :object, slots: []}, _]) - defmethod(:process, :allocate, [self, args, new_obj]) do - class(self, meta) - - map_get(args, :method, method_name) - map_get(args, :head, head) - map_get(args, :body, body) - - gensym(new_obj) - - set_class(new_obj, meta) - set_super(new_obj, :object) - - fresh_id(impl) - set_method(new_obj, method_name, impl) - set_class(impl, :behaviour) - set_oapply(impl, head, body) - end + 0 -> + AL.Bootstrap.Core.setup() + AL.Bootstrap.Lists.setup() + AL.Bootstrap.ElixirProcess.setup() + AL.Bootstrap.Process.setup() + _ -> + :ok end end end diff --git a/lib/AL/bootstrap/core.ex b/lib/AL/bootstrap/core.ex new file mode 100644 index 0000000..9d82d82 --- /dev/null +++ b/lib/AL/bootstrap/core.ex @@ -0,0 +1,109 @@ +defmodule AL.Bootstrap.Core do + use AL + + def setup() do + run do + set_class(:class, :class) + set_class(:object, :class) + set_class(:behaviour, :class) + + set_super(:class, :object) + set_super(:behaviour, :object) + + set_method(:object, :lookup, :lookup) + set_method(:object, :send, :send) + set_method(:object, :meta, :metaclass) + set_method(:object, :defmethod, :defmethod) + + set_class(:metaclass, :behaviour) + + set_oapply(:metaclass, [self, class, meta]) do + class(self, class) + class(class, meta) + end + + set_class(:lookup, :behaviour) + set_oapply(:lookup, [self, name, id]) do + alternative([method(self, name, id)], + [super(self, super), + lookup(super, name, id)]) + end + + set_class(:send, :behaviour) + set_oapply(:send, [self, method, args]) do + class(self, class) + implies( + [method(self, method, id)], + [ + print(["calling", id, "from", self, "with args", [self | args]]), + oapply(id, [self | args]) + ], + [implies( + [lookup(class, method, id)], + [ + print(["calling", id, "from", class, "with args", [self | args]]), + oapply(id, [self | args]) + ], + [:fail] + )]) + end + + set_class(:defmethod, :behaviour) + set_oapply(:defmethod, [self, method_name, head, body]) do + fresh_id(impl) + set_method(self, method_name, impl) + set_class(impl, :behaviour) + set_oapply(impl, head, body) + end + + set_class(:map_get, :behaviour) + set_method(:map, :map_get, :map_get) + + defmethod(:class, :construct, [self, %{class: self}]) do + end + + set_method(:class, :allocate, :allocate_class) + set_class(:allocate_class, :behaviour) + set_oapply(:allocate_class, [self, args, name]) do + map_get(args, :name, name) + map_get(args, :super, super) + map_get(args, :slots, slots) + + class(self, meta) + + set_class(name, meta) + set_super(name, super) + set_slots(name, slots) + end + + defmethod(:object, :allocate, [self, _, self]) do + print(["allocate", self]) + end + + defmethod(:object, :init, [self, _, self]) do + print(["initialise", self]) + end + + defmethod(:class, :new, [self, args, new]) do + send(self, :construct, [construct]) + send(construct, :allocate, [args, alloc]) + send(alloc, :init, [args, new]) + end + + defmethod(:object, :examine, [self, %{classes: classes, + objects: objects, + supers: supers, + subs: subs, + methods: methods, + clauses: clauses}]) do + findall(c, [class(self, c)], classes) + findall(c, [class(c, self)], objects) + findall(s, [super(self, s)], supers) + findall(sub, [super(sub, self)], subs) + findall([n, id], [method(self, n, id)], methods) + findall([head, body], [clause(self, head, body)], clauses) + end + + end + end +end diff --git a/lib/AL/bootstrap/elixir_process.ex b/lib/AL/bootstrap/elixir_process.ex new file mode 100644 index 0000000..596c363 --- /dev/null +++ b/lib/AL/bootstrap/elixir_process.ex @@ -0,0 +1,19 @@ +defmodule AL.Bootstrap.ElixirProcess do + use AL + + def setup() do + run do + send(:class, :new, [%{name: :elixir_process, super: :object, slots: []}, _]) + defmethod(:elixir_process, :allocate, [self, args, new_obj]) do + class(self, meta) + map_get(args, :name, new_obj) + set_class(new_obj, meta) + set_super(new_obj, :elixir_process) + end + defmethod(:elixir_process, :init, [self, args, self]) do + map_get(args, :pid, pid) + set_slots(self, %{pid: pid}) + end + end + end +end diff --git a/lib/AL/bootstrap/lists.ex b/lib/AL/bootstrap/lists.ex new file mode 100644 index 0000000..df2c289 --- /dev/null +++ b/lib/AL/bootstrap/lists.ex @@ -0,0 +1,69 @@ +defmodule AL.Bootstrap.Lists do + use AL + + def setup() do + run do + send(:class, :new, [%{name: :list, super: :object, slots: []}, _]) + + defmethod(:list, :hd, [[h | _t], h]) do end + defmethod(:list, :tl, [[_h | t], t]) do end + + set_method(:list, :concat, :list_concat) + set_class(:list_concat, :behaviour) + set_oapply(:list_concat, [[], second, second]) do end + set_oapply(:list_concat, [[fh | ft], second, [fh | inner]]) do + send(ft, :concat, [second, inner]) + end + + set_method(:list, :member, :list_member) + set_class(:list_member, :behaviour) + set_oapply(:list_member, [[x | _t], x]) do end + set_oapply(:list_member, [[_h | t], x]) do + send(t, :member, [x]) + end + + set_method(:list, :reverse, :list_reverse) + set_class(:list_reverse, :behaviour) + set_oapply(:list_reverse, [[], []]) do end + set_oapply(:list_reverse, [[h | t], reversed]) do + send(t, :reverse, [reversed_tl]) + send(reversed_tl, :concat, [[h], reversed]) + end + + set_method(:list, :map, :list_map) + set_class(:list_map, :behaviour) + set_oapply(:list_map, [[], _func, []]) do end + set_oapply(:list_map, [[fh | ft], func, [sh | st]]) do + send(fh, func, [sh]) + send(ft, :map, [func, st]) + end + + set_method(:list, :fold_left, :list_fold_left) + set_class(:list_fold_left, :behaviour) + set_oapply(:list_fold_left, [[], _func, acc, acc]) do end + set_oapply(:list_fold_left, [[h | t], func, acc, result]) do + send(acc, func, [h, next_acc]) + send(t, :fold_left, [func, next_acc, result]) + end + + set_method(:list, :fold_right, :list_fold_right) + set_class(:list_fold_right, :behaviour) + set_oapply(:list_fold_right, [[], _func, acc, acc]) do end + set_oapply(:list_fold_right, [[h | t], func, acc, result]) do + send(t, :fold_right, [func, acc, next_acc]) + send(next_acc, func, [h, result]) + end + + defmethod(:list, :flatten, [lists, result]) do + send(lists, :fold_left, [:concat, [], result]) + end + + set_method(:list, :same_length, :list_same_length) + set_class(:list_same_length, :behaviour) + set_oapply(:list_same_length, [[], []]) do end + set_oapply(:list_same_length, [[_fh | ft], [_sh | st]]) do + send(ft, :same_length, [st]) + end + end + end +end diff --git a/lib/AL/bootstrap/process.ex b/lib/AL/bootstrap/process.ex new file mode 100644 index 0000000..bface60 --- /dev/null +++ b/lib/AL/bootstrap/process.ex @@ -0,0 +1,26 @@ +defmodule AL.Bootstrap.Process do + use AL + + def setup() do + run do + send(:class, :new, [%{name: :process, super: :object, slots: []}, _]) + defmethod(:process, :allocate, [self, args, new_obj]) do + class(self, meta) + + map_get(args, :method, method_name) + map_get(args, :head, head) + map_get(args, :body, body) + + gensym(new_obj) + + set_class(new_obj, meta) + set_super(new_obj, :object) + + fresh_id(impl) + set_method(new_obj, method_name, impl) + set_class(impl, :behaviour) + set_oapply(impl, head, body) + end + end + end +end diff --git a/lib/examples/e_AL.ex b/lib/examples/e_AL.ex index c7927ef..869bb6d 100644 --- a/lib/examples/e_AL.ex +++ b/lib/examples/e_AL.ex @@ -290,57 +290,15 @@ defmodule Examples.AL do example list_tests() do {:atomic, {bindings, result}} = run do - set_oapply(:hd, [[hd | tl], hd]) do - end - set_oapply(:tl, [[hd | tl], tl]) do - end - set_class(:concat_list, :behaviour) - set_oapply(:concat_list, [[], second, second]) do - end - set_oapply(:concat_list, [[first_hd | first_tl], second, [first_hd | inner]]) do - oapply(:concat_list, [first_tl, second, inner]) - end - set_oapply(:reverse_list, [[], []]) do - end - set_oapply(:reverse_list, [[first_hd | first_tl], reversed]) do - oapply(:reverse_list, [first_tl, reversed_tl]) - oapply(:concat_list, [reversed_tl, [first_hd], reversed]) - end - set_oapply(:map_list, [func, [], []]) do - end - set_oapply(:map_list, [func, [first_hd | first_tl], [second_hd | second_tl]]) do - oapply(func, [first_hd, second_hd]) - oapply(:map_list, [func, first_tl, second_tl]) - end - set_oapply(:fold_left, [func, acc, [], acc]) do - end - set_oapply(:fold_left, [func, acc, [hd | tl], result]) do - oapply(func, [acc, hd, next_acc]) - oapply(:fold_left, [func, next_acc, tl, result]) - end - set_oapply(:fold_right, [func, acc, [], acc]) do - end - set_oapply(:fold_right, [func, acc, [hd | tl], result]) do - oapply(:fold_right, [func, acc, tl, next_acc]) - oapply(func, [next_acc, hd, result]) - end - set_oapply(:flatten_list, [lists, result]) do - oapply(:fold_left, [:concat_list, [], lists, result]) - end - set_oapply(:same_length, [[], []]) do - end - set_oapply(:same_length, [[first_hd | first_tl], [second_hd | second_tl]]) do - oapply(:same_length, [first_tl, second_tl]) - end - oapply(:hd, [[:w, :x, :y, :z], head]) - oapply(:tl, [[:w, :x, :y, :z], tail]) - oapply(:concat_list, [[:a, :b, :c], [:d, :e, :f], sum]) - oapply(:reverse_list, [[:b, :c, :d, :e, :f], reversed]) - oapply(:map_list, [:reverse_list, [[:a, :b], [:c, :d, :e]], mapped]) - oapply(:fold_left, [:concat_list, [:starter], [[:a], [:b], [:c], [:d]], folded_left]) - oapply(:fold_right, [:concat_list, [:starter], [[:a], [:b], [:c], [:d]], folded_right]) - oapply(:flatten_list, [[[:a, :b], [:c, :d, :e]], flattened]) - oapply(:same_length, [[:c, :d, :e, :f], of_same_length]) + send([:w, :x, :y, :z], :hd, [head]) + send([:w, :x, :y, :z], :tl, [tail]) + send([:a, :b, :c], :concat, [[:d, :e, :f], sum]) + send([:b, :c, :d, :e, :f], :reverse, [reversed]) + send([[:a, :b], [:c, :d, :e]], :map, [:reverse, mapped]) + send([[:a], [:b], [:c], [:d]], :fold_left, [:concat, [:starter], folded_left]) + send([[:a], [:b], [:c], [:d]], :fold_right, [:concat, [:starter], folded_right]) + send([[:a, :b], [:c, :d, :e]], :flatten, [flattened]) + send([:c, :d, :e, :f], :same_length, [of_same_length]) end assert Map.get(bindings, :"$sum") == [:a, :b, :c, :d, :e, :f] assert Map.get(bindings, :"$reversed") == [:f, :e, :d, :c, :b] From 56e8bb22c9f88daddb8a5e9fa91651e7b881d705 Mon Sep 17 00:00:00 2001 From: l4e21 Date: Tue, 9 Jun 2026 15:17:34 +0100 Subject: [PATCH 4/9] Add Sussman constraint propagators --- .mnesiastore/LATEST.LOG | Bin 65658 -> 65694 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL.ex | 55 ++++++++++++++++++++ lib/AL/application.ex | 1 + lib/AL/bootstrap/constraints.ex | 74 +++++++++++++++++++++++++++ lib/AL/bootstrap/core.ex | 8 +-- lib/examples/e_AL.ex | 40 +++++++++++++++ lib/examples/e_AL_constraints.ex | 84 +++++++++++++++++++++++++++++++ test/al_test.exs | 4 ++ 9 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 lib/AL/bootstrap/constraints.ex create mode 100644 lib/examples/e_AL_constraints.ex diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index fc274acde6dcd13748bfadea57439b2f29a69854..ef042fe45e4424271a84268b467d23de35205a0c 100644 GIT binary patch literal 65694 zcmd^|cYNE%6~{%9k|o)aXZCWNG#weWwbhlRD;=qm&ZJX4fRII*B2|$x>dv%D_e}Rr zy3=Owz4zXGdGEcqdjN63-4W!;vT#3tO8+5~5^&%5-o1NwxbYCHjwfoz)YVs2Rh?>_ zdDFmtzUW+8HLAyRbNHV!@`kPr*@zxVtW>qCxPi$_r8#CEVHff!l$@XS* z`QEruRaLu*{^&ZM{y4jb7MCg%S7+Cn+5Y}iUb@9@8W^;4L;Y5F;m>_(dQYr3-dk-a z`mH>%$L8kb@>YL$KGkpa#zTHd=Gis&U@C9*(SKTi^F`pmK5E#QGqVGZ{zI9(HR$Uo z{+gPz`XUhbVi5NdksG}yEf9g)D)v$k_c9RoauD|l5cf(D_bL$gY7qAt5cgV<8-1W! zs2jLQ1ZsM4+~cy%)s255&D6#C-t7eGtTb2*iCD#C-(B zeN^N|AE=h=2Cfi++9Km)AnxNJ?h_(6dQVy@0<~4_lOXO>Anwy3?lU0nvmowsAnx-Z z?h7F9iy-bxAnwZ`?kgbft03-cA~*UDaFqzu)&b51aiukJZTd<}H6U&+h&u+v ztrNM?2P$dI7(Ea)Sd7<;mem#+8$jH#A~*Vq1&zkz<3vTZRcs@O+XUi{2XQBWxD!F# zNg(cI5O)fQdjN=gAc%VqhI)&=b8~r5VfgKDG>H zvJ;vHBqq(}X#kA5?r52_%O{s+`O;KuLO4E~CnBvsrp_h~o3B5IT zUD7gBxl}eoQ~$*CsU(?kmXsukHv02uW+V5X-N+Iq5hFX8cheBQ=4GmMTlv&XdBO+q?@Qk0k4 z4FiMO0Wy>1vx7XUmZo*;%_i-sEy=dF&YtG3X3{z>*>1FSkrvbFNE%7f)@8Q$BzqI~ zu&=f2&1@z|lT@WLcKqn(b^(|r0|V(f?(dy4xv!4(;&$yZ-8n1W<9w;6Ur6_ClBWNg z&i{S%gW-Pg5>xs2SEq994)+3T8dh}d1ojuV3|1!1PsPjSRR7tq3ATJ;IX&D9%IZ-tg{TM9nu^{g%GDMs7qf4nEMGw9 zTAKJPOfJ`o+@E5{G5d;TiE?@NoocjMJ*}O|W~ z@s*dB^68pfI-6&$jD5~ig%WlmL;5Y+{_Gm+O+%JlON^YA$wX?sy1gEC{4gU0O~Wlat0QEzNK|#uHq1X{a=XdkZm{eGa?O4}Z$A%!NwTwNj{T z6KbvCyTfx1*X5O$I<27E9-GPx4CT9>ZpZU>>J2fY&mNl{%Co=NF;ftx;6iR#@jEZ) zTZ#C{8lmfQULzzzBP7FUgi7~PYCMca=vS=DefU05b*)sf9fB>ridAXnw)#TFsxB>8 zv8G(HH2k?a8odxyh?Z_^^eW19ilgZr3(=DL!6-yal?Ekx-JXvuNya2D0>v(&D8A@(bO{6Sf8f*A*!tW;itwi>iy}>dhBv zCoj^@KqRJgnXu=VF`qAEeo%(5mKzNHHdZzm3QfZ&Y~u;rLI`dv9--k>E*UD4GanM; zq;kKzf;BikQ+U*%&a8GC@c}{zo=ktI@EQqL!(f?YH_6S(INnaXv7oeB^d7#cvckwJ zt|YQ5#F!<#Fs2^6g_?@2;el%cAoH;iw{%~nMLc0qK=3`#Rmu0jvSZ?K9X9iX%^bmr zFT)BBA!Aiv{)K$$3yVvqqxf>SFXwMxUVOW9wV33@i*^s zZ+06<;gOR1tP^0MPiwxYD|t~@273BVSCbT0*Yeo4oa!u&sCuKaO?5S2`sy&H zln-emQEC~FT^12EI=+l`K^ZL6D&s=QMnpU3qS*vC zKjXbaEHO#623sJqE;m1BiPp zi2Ftm_e~(~n?-vQ#j6U2R2g>g>; z>3cGWdkTpA?h50+2c++NLEQI&xbFvXPX%#50OEcS#QhM6`{DhAtL52OO3$w#y_q+3>mg+;7VEH9D>4JT$XY76GJv}7!p1+TwSAA50)Ql|jsy1e@C|4rLHb~K_VKk$YNop9rD7uf8 zz++2*Rr-a7#d6>=5_k*)i_zWNP~}B;wgp@F#NQc$H&>Mo?&X~p72H26imxrWf2_i| z9|!6C2@v;_AnvCsjC&?XUljK&kiMS=aX$m%eip?29EkgQ5cdlpZa0YgMG%*sg>=vI z=pG)TxYBlCZR0u}sfw=6cTz9;h1=1JAWjV5R+qoZP=M0FO z1#t&J+%JK+ET5Qef6jsQMR8dwEZue<0@-;sh--tmUj}jKR2cUwAbr0I;(iUp{W^&I z4G{O6Anw^9?zcePZ-cn!R2cVMkiOpmalcz(-0y+({XU5M0}%IzAnuPq+#iFuKLK%n z3gZ3@#Qiyldmf1U3lR60Anva~++TyZzo{_pZ$bM04#fTa{=tp<1y@IiYF<=+kE`9p z-uO|-uEgHpKzGt7Xo4?7|F5HO)JhY>N~?3j(p;Y-xQj?{#?iK7{0Grg+PHrNasLG3 z{u#vmi^z?-07NR!AMojB4c#gWGSJC6KQ zw1Kt}jKtMa-{V2L{;R^c{|4!MA&C1Q5cj_z?nNN(#USn_Anv6g?qwkET9>dB_^ufi*Fq`65N-!(boyGA<8 zB_8ngUNzwz@MTYlMdy^qOQnrxrAg@p&+E1ZN)AEPmsV9N`Oa{{(=L0dkjk- zEiUg;sH4SK3Qa{{-iS)$UInsqrE#wY+4&j}_gWD5IuQ4I5cdWU_eK!+CJ^^#5cd`k z_tpyI-Uib5b`bXt5cf_H_bw3k?h51H1Jd_i5cfV1_kIxf0TA~=5ceSv_hAtCkqYBJ z3exv65chEq_lXMQJ_*wIDG>K*5ce4n_gN74IS}`G5cdTT_eBu*r3&M|4AS=%5cgFO z_caiAE{Ho1#GMb~E&y>Cg1C!7+{Ga7k_zK41?jsC#9h9BaJ6i`nFP{xg~%P9?Zy+O zeH;^=eH;^uUSkq7#A6F_Q5J36epiCHt3ce<6~Z_$aPZpWle0eR1yH4beP8^ve z#gR$-cN|$S+CW>KZxFd!{CkSX)TZl3kvlqmHd*qs$tC>E6#J_hQ5J1B*aYHk264B5 zxLZNoZ6NM;5O)WNyA#CS1>)`oara=ji7F5`2I8^}{hCwgCP2?^i3G^bpngA53(|KC zh+7BZ)`PeWAnsTYcN~Z-rRvZ%Zcj)@`?YaFN6Zt_aYAkSibo2yos&v{=5Q0BxuyhY z4j^#=C~h2BVcdg2`W_789#Uc4LqYl;2I3wL;!5erwDp5h<~A+!!cuG=o%z`*(){cc XXMT1{@p|I^kp?Ys6v)q}a@_v`A41Q` literal 65658 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 LKl&j3Cq(*xx1kUE diff --git a/.mnesiastore/schema.DAT b/.mnesiastore/schema.DAT index 0ce1fe84a4ccd90a359460091b0468a751e5376b..1f4b40a16ec59efdc97135c6c0de9360e508d610 100644 GIT binary patch delta 193 zcmdnwv&m;ew;0o#4<$6Zm@)k#!@Xnt=Vg1ctRcsjn$HPy% delta 197 zcmdnwv&m;ew;1o=IXr)NXl$3r<6zwLbMgg=tceL0o2|q<8QE5z;A7mWF?pg`;btqz zLPnl59UVYESY_qp^Agz;6C^fUNtZM7E^glmli2weVua9UD_NjS backtrack(state) + bindings -> %AL{state | active_choicepoint: %AL.Choicepoint{state.active_choicepoint | bindings: bindings}} + end + end + + def interp({:not, condition}, state) do + case collect_all_solutions(condition, state.active_choicepoint.bindings, state.tx_id) do + [] -> state + _ -> backtrack(state) + end + end + def interp(:fail, state) do backtrack(state) end diff --git a/lib/AL/application.ex b/lib/AL/application.ex index 214281a..6dd384b 100644 --- a/lib/AL/application.ex +++ b/lib/AL/application.ex @@ -26,6 +26,7 @@ defmodule AL.Application do AL.Bootstrap.Lists.setup() AL.Bootstrap.ElixirProcess.setup() AL.Bootstrap.Process.setup() + AL.Bootstrap.Constraints.setup() _ -> :ok end diff --git a/lib/AL/bootstrap/constraints.ex b/lib/AL/bootstrap/constraints.ex new file mode 100644 index 0000000..bf1c094 --- /dev/null +++ b/lib/AL/bootstrap/constraints.ex @@ -0,0 +1,74 @@ +defmodule AL.Bootstrap.Constraints do + use AL + + def setup() do + run do + send(:class, :new, [%{name: :cell, super: :object, slots: [:subscribers, :value, :name]}, _]) + + defmethod(:cell, :allocate, [self, args, cell_name]) do + class(self, meta) + map_get(args, :name, cell_name) + + set_class(cell_name, meta) + set_super(cell_name, :object) + end + + defmethod(:cell, :init, [self, args, self]) do + map_get(args, :name, cell_name) + set_slots(cell_name, %{name: cell_name, subscribers: [], value: :absent}) + end + + defmethod(:cell, :constrain, [self, value]) do + get_slot(self, :value, :absent) + set_slots(self, %{value: value}) + + forall([get_slot(self, :subscribers, subscribers), + send(subscribers, :member, [subscriber])], + [send_async(subscriber, :cell_updated, [self, value])]) + cut + end + + defmethod(:cell, :subscribe, [self, subscriber]) do + get_slot(self, :subscribers, subscribers) + set_slots(self, %{subscribers: [subscriber | subscribers]}) + end + + send(:class, :new, [%{name: :propagator, super: :object, slots: [:input_cells, :output_cell]}, _]) + + defmethod(:propagator, :allocate, [self, args, propagator]) do + class(self, meta) + gensym(propagator) + + set_class(propagator, meta) + set_super(propagator, :object) + end + + defmethod(:propagator, :init, [self, args, self]) do + map_get(args, :input_cells, input_cells) + map_get(args, :output_cell, output_cell) + + set_slots(self, %{input_cells: input_cells, output_cell: output_cell}) + + forall([send(input_cells, :member, [input_cell])], + [send(input_cell, :subscribe, [self])]) + + send_async(self, :cell_updated, [:none, :none]) + end + + defmethod(:propagator, :cell_updated, [self, _cell_name, _value]) do + get_slot(self, :input_cells, input_cells) + get_slot(self, :output_cell, output_cell) + + findall(input_cell_value, + [send(input_cells, :member, [input_cell]), + get_slot(input_cell, :value, input_cell_value)], + input_cell_values) + forall([send(input_cell_values, :member, [input_cell_value])], + [not([unify(input_cell_value, :absent)])]) + + send(self, :constrain, [input_cell_values, output_value]) + send_async(output_cell, :constrain, [output_value]) + end + end + end +end diff --git a/lib/AL/bootstrap/core.ex b/lib/AL/bootstrap/core.ex index 9d82d82..3f8fdec 100644 --- a/lib/AL/bootstrap/core.ex +++ b/lib/AL/bootstrap/core.ex @@ -77,11 +77,11 @@ defmodule AL.Bootstrap.Core do end defmethod(:object, :allocate, [self, _, self]) do - print(["allocate", self]) + # print(["allocate", self]) end defmethod(:object, :init, [self, _, self]) do - print(["initialise", self]) + # print(["initialise", self]) end defmethod(:class, :new, [self, args, new]) do @@ -95,13 +95,15 @@ defmodule AL.Bootstrap.Core do supers: supers, subs: subs, methods: methods, - clauses: clauses}]) do + clauses: clauses, + slots: slots}]) do findall(c, [class(self, c)], classes) findall(c, [class(c, self)], objects) findall(s, [super(self, s)], supers) findall(sub, [super(sub, self)], subs) findall([n, id], [method(self, n, id)], methods) findall([head, body], [clause(self, head, body)], clauses) + findall([slot_name, slot_value], [get_slot(self, slot_name, slot_value)], slots) end end diff --git a/lib/examples/e_AL.ex b/lib/examples/e_AL.ex index 869bb6d..5a5750c 100644 --- a/lib/examples/e_AL.ex +++ b/lib/examples/e_AL.ex @@ -288,6 +288,46 @@ defmodule Examples.AL do result end + example not_succeeds_when_goal_fails() do + {:atomic, {_bindings, _}} = + run do + not([class(:nonexistent_xyz, c)]) + end + :ok + end + + example not_fails_when_goal_succeeds() do + {:aborted, _} = + run do + not([class(:object, c)]) + end + :ok + end + + example unify_binds_variable() do + {:atomic, {bindings, _}} = + run do + unify(x, :hello) + end + assert Map.get(bindings, :"$x") == :hello + :ok + end + + example unify_checks_equality() do + {:aborted, _} = run do unify(:foo, :bar) end + {:atomic, _} = run do unify(:foo, :foo) end + :ok + end + + example call_lambda() do + {:atomic, {bindings, _}} = + run do + call([x, result], [unify(result, x)], [:hello, out]) + end + assert Map.get(bindings, :"$out") == :hello + :ok + end + example list_tests() do {:atomic, {bindings, result}} = run do send([:w, :x, :y, :z], :hd, [head]) diff --git a/lib/examples/e_AL_constraints.ex b/lib/examples/e_AL_constraints.ex new file mode 100644 index 0000000..f189376 --- /dev/null +++ b/lib/examples/e_AL_constraints.ex @@ -0,0 +1,84 @@ +defmodule Examples.ALConstraints do + @moduledoc """ + """ + + use ExExample + use AL + import ExUnit.Assertions + + example constant() do + {:atomic, {_bindings, _state}} = run do + send(:cell, :new, [%{name: :x}, cell]) + send(:propagator, :new, [%{input_cells: [], output_cell: cell}, propagator]) + defmethod(propagator, :constrain, [_self, [], 2]) do end + cut + end + + Process.sleep(50) + + {:atomic, {bindings, _state}} = run do + get_slot(:x, :value, value) + end + + assert Map.get(bindings, :"$value") == 2 + + bindings + end + + example inc() do + constant() + + {:atomic, {bindings, state}} = run do + send(:cell, :new, [%{name: :y}, y_cell]) + send(:propagator, :new, [%{input_cells: [:x], output_cell: y_cell}, propagator]) + defmethod(propagator, :constrain, [_self, [x_val], y_val]) do + is(y_val, 1 + x_val) + end + end + + Process.sleep(50) + + {:atomic, {bindings, _state}} = run do + get_slot(:y, :value, value) + end + + assert Map.get(bindings, :"$value") == 3 + + bindings + end + + example bidirectional_adder() do + {:atomic, {bindings, state}} = run do + send(:cell, :new, [%{name: :a}, a]) + send(:cell, :new, [%{name: :b}, b]) + send(:cell, :new, [%{name: :c}, c]) + + send(:propagator, :new, [%{input_cells: [a, b], output_cell: c}, propagator_ab]) + send(:propagator, :new, [%{input_cells: [a, c], output_cell: b}, propagator_ac]) + send(:propagator, :new, [%{input_cells: [b, c], output_cell: a}, propagator_bc]) + + defmethod(propagator_ab, :constrain, [_self, [a_val, b_val], c_val]) do + is(c_val, a_val + b_val) + end + defmethod(propagator_ac, :constrain, [_self, [a_val, c_val], b_val]) do + is(b_val, c_val - a_val) + end + defmethod(propagator_bc, :constrain, [_self, [b_val, c_val], a_val]) do + is(a_val, c_val - b_val) + end + + send_async(b, :constrain, [3]) + send_async(c, :constrain, [5]) + end + + Process.sleep(50) + + {:atomic, {bindings, _state}} = run do + get_slot(:a, :value, value) + end + + assert Map.get(bindings, :"$value") == 2 + + bindings + end +end diff --git a/test/al_test.exs b/test/al_test.exs index fa4576a..f7430fd 100644 --- a/test/al_test.exs +++ b/test/al_test.exs @@ -13,3 +13,7 @@ end defmodule ALGenserverTest do use ExExample.ExUnit, for: Examples.ALGenserver end + +defmodule ALConstraintsTest do + use ExExample.ExUnit, for: Examples.ALConstraints +end From 3e61eb85f8cb6b8cb2e6120d72f0cc332edbb789 Mon Sep 17 00:00:00 2001 From: l4e21 Date: Wed, 17 Jun 2026 15:14:48 +0100 Subject: [PATCH 5/9] Add GT integration --- .mnesiastore/LATEST.LOG | Bin 65694 -> 65627 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL.ex | 44 ++++++++++++------ lib/AL/application.ex | 2 +- lib/AL/bootstrap/core.ex | 26 +++++++---- lib/AL/bootstrap/lists.ex | 16 +++++++ lib/AL/command.ex | 37 +++++++++------ lib/AL/{objects.ex => object.ex} | 10 +++- lib/AL/views.ex | 76 +++++++++++++++++++++++++++++++ lib/examples/e_AL.ex | 29 ++++++++++-- lib/examples/e_AL_bootstrap.ex | 8 ++-- lib/examples/e_AL_genserver.ex | 4 +- mix.exs | 3 +- mix.lock | 20 ++++++++ 14 files changed, 226 insertions(+), 49 deletions(-) rename lib/AL/{objects.ex => object.ex} (98%) create mode 100644 lib/AL/views.ex diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index ef042fe45e4424271a84268b467d23de35205a0c..2f4f6136ef37703af3bc25cd468b7331315cd929 100644 GIT binary patch literal 65627 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 XXMT1{@p|I^kp?Ys6v)q}a@_v`A41Q` diff --git a/.mnesiastore/schema.DAT b/.mnesiastore/schema.DAT index 1f4b40a16ec59efdc97135c6c0de9360e508d610..3afb06c5809a8de8ee39a340ad3240d9aecb0a27 100644 GIT binary patch delta 202 zcmdnwv&m;ew;1npvnSi{KHe#j$HBPu|Ktl2SrZd1Hd~2zGV;dvgk6Tp{Q1B6g2Zh` z-iF03SD->QP{k6Pt)$BtdDSfU!(@JagBT;U*-92D!?cq9I#kbRs2-q delta 201 zcmdnwv&m;ew;0bMjf$7?`P(J(I2bJ_pO?sz6_K3`5P`H50rVh>Jv=H s5+S45&B*JmEc*^>b>%Oho{Y^_%Jq!Agd;hjH$!up%Ps@O6D0QbsMAOHXW diff --git a/lib/AL.ex b/lib/AL.ex index 8efa9b5..32102b1 100644 --- a/lib/AL.ex +++ b/lib/AL.ex @@ -448,7 +448,7 @@ defmodule AL do } } else - case AL.Objects.scan_class(object_pattern, class_pattern) do + case AL.Object.scan_class(object_pattern, class_pattern) do [] -> backtrack(state) @@ -483,7 +483,7 @@ defmodule AL do end def interp({:get_super, object_pattern, super_pattern}, state) do - case AL.Objects.scan_super(object_pattern, super_pattern) do + case AL.Object.scan_super(object_pattern, super_pattern) do [] -> backtrack(state) @@ -516,7 +516,7 @@ defmodule AL do end def interp({:get_method, object_pattern, method_name_pattern, method_id_pattern}, state) do - case AL.Objects.scan_method(object_pattern, method_name_pattern, method_id_pattern) do + case AL.Object.scan_method(object_pattern, method_name_pattern, method_id_pattern) do [] -> backtrack(state) @@ -549,7 +549,7 @@ defmodule AL do end def interp({:get_oapply, object_pattern, head_pattern, body_pattern}, state) do - case AL.Objects.scan_oapply(object_pattern, head_pattern, body_pattern) do + case AL.Object.scan_oapply(object_pattern, head_pattern, body_pattern) do [] -> backtrack(state) @@ -628,6 +628,21 @@ defmodule AL do end end + def interp({:oapply, :map_put, [m1, k_pattern, v_pattern, m2]}, state) do + case AL.Var.unify(m2, Map.put(m1, k_pattern, v_pattern), state.active_choicepoint.bindings) do + nil -> backtrack(state) + choice -> + %AL{ + state + | active_choicepoint: %AL.Choicepoint{ + state.active_choicepoint + | bindings: choice + }, + choicepoint_stack: state.choicepoint_stack + } + end + end + def interp({:oapply, :is, [a, b]}, state) do a_deref = AL.Var.deref(state.active_choicepoint.bindings, a) expr = interp_is(b, state.active_choicepoint.bindings) @@ -640,7 +655,7 @@ defmodule AL do end def interp({:oapply, method_id_pattern, bind_head_pattern}, state) do - case AL.Objects.scan_oapply(method_id_pattern, :"$head", :"$body") do + case AL.Object.scan_oapply(method_id_pattern, :"$head", :"$body") do [] -> backtrack(state) @@ -774,28 +789,28 @@ defmodule AL do def interp({:set_class, object, _class}, state) when is_map(object), do: state def interp({:set_class, object_pattern, class_pattern}, state) do AL.Command.set_class(state.tx_id, object_pattern, class_pattern) - AL.Objects.set_class(object_pattern, class_pattern) + AL.Object.set_class(object_pattern, class_pattern) state end def interp({:set_super, object, _super}, state) when is_map(object), do: state def interp({:set_super, object_pattern, super_pattern}, state) do AL.Command.set_super(state.tx_id, object_pattern, super_pattern) - AL.Objects.set_super(object_pattern, super_pattern) + AL.Object.set_super(object_pattern, super_pattern) state end def interp({:set_method, object, _name, _id}, state) when is_map(object), do: state def interp({:set_method, object_pattern, method_name_pattern, method_id_pattern}, state) do AL.Command.set_method(state.tx_id, object_pattern, method_name_pattern, method_id_pattern) - AL.Objects.set_method(object_pattern, method_name_pattern, method_id_pattern) + AL.Object.set_method(object_pattern, method_name_pattern, method_id_pattern) state end def interp({:set_oapply, object, _head, _body}, state) when is_map(object), do: state def interp({:set_oapply, object_pattern, head_pattern, body_pattern}, state) do AL.Command.set_oapply(state.tx_id, object_pattern, head_pattern, body_pattern) - AL.Objects.set_oapply(object_pattern, head_pattern, body_pattern) + AL.Object.set_oapply(object_pattern, head_pattern, body_pattern) state end @@ -842,35 +857,35 @@ defmodule AL do def interp({:set_slots, object, _slots}, state) when is_map(object), do: state def interp({:set_slots, object_pattern, slots_pattern}, state) do AL.Command.set_slots(state.tx_id, object_pattern, slots_pattern) - AL.Objects.set_slots(object_pattern, slots_pattern) + AL.Object.set_slots(object_pattern, slots_pattern) state end def interp({:retract_class, object, _class}, state) when is_map(object), do: state def interp({:retract_class, object, class}, state) do AL.Command.retract_class(state.tx_id, object, class) - AL.Objects.retract_class(object, class) + AL.Object.retract_class(object, class) state end def interp({:retract_super, object, _super}, state) when is_map(object), do: state def interp({:retract_super, object, super}, state) do AL.Command.retract_super(state.tx_id, object, super) - AL.Objects.retract_super(object, super) + AL.Object.retract_super(object, super) state end def interp({:retract_method, object, _name, _id}, state) when is_map(object), do: state def interp({:retract_method, object, name, id}, state) do AL.Command.retract_method(state.tx_id, object, name, id) - AL.Objects.retract_method(object, name, id) + AL.Object.retract_method(object, name, id) state end def interp({:retract_oapply, object, _head}, state) when is_map(object), do: state def interp({:retract_oapply, object, head}, state) do AL.Command.retract_oapply(state.tx_id, object, head) - AL.Objects.retract_oapply(object, head) + AL.Object.retract_oapply(object, head) state end @@ -1061,3 +1076,4 @@ defimpl Inspect, for: AL do "#AL<>" end end + diff --git a/lib/AL/application.ex b/lib/AL/application.ex index 6dd384b..cb1c613 100644 --- a/lib/AL/application.ex +++ b/lib/AL/application.ex @@ -9,7 +9,7 @@ defmodule AL.Application do @impl true def start(_type, _args) do AL.Command.setup() - AL.Objects.setup() + AL.Object.setup() opts = [strategy: :one_for_one, name: Al.Supervisor] {:ok, pid} = Supervisor.start_link([AL.Scheduler], opts) diff --git a/lib/AL/bootstrap/core.ex b/lib/AL/bootstrap/core.ex index 3f8fdec..21ac607 100644 --- a/lib/AL/bootstrap/core.ex +++ b/lib/AL/bootstrap/core.ex @@ -56,9 +56,15 @@ defmodule AL.Bootstrap.Core do set_oapply(impl, head, body) end + set_class(:map, :class) + set_super(:map, :object) + set_class(:map_get, :behaviour) - set_method(:map, :map_get, :map_get) + set_method(:map, :get, :map_get) + set_class(:map_put, :behaviour) + set_method(:map, :put, :map_put) + defmethod(:class, :construct, [self, %{class: self}]) do end @@ -90,18 +96,22 @@ defmodule AL.Bootstrap.Core do send(alloc, :init, [args, new]) end - defmethod(:object, :examine, [self, %{classes: classes, - objects: objects, - supers: supers, - subs: subs, - methods: methods, - clauses: clauses, - slots: slots}]) do + defmethod(:object, :examine, [self, %{ + id: self, + classes: classes, + objects: objects, + supers: supers, + subs: subs, + methods: methods, + providers: providers, + clauses: clauses, + slots: slots}]) do findall(c, [class(self, c)], classes) findall(c, [class(c, self)], objects) findall(s, [super(self, s)], supers) findall(sub, [super(sub, self)], subs) findall([n, id], [method(self, n, id)], methods) + findall([provider, n], [method(provider, n, self)], providers) findall([head, body], [clause(self, head, body)], clauses) findall([slot_name, slot_value], [get_slot(self, slot_name, slot_value)], slots) end diff --git a/lib/AL/bootstrap/lists.ex b/lib/AL/bootstrap/lists.ex index df2c289..595fbcb 100644 --- a/lib/AL/bootstrap/lists.ex +++ b/lib/AL/bootstrap/lists.ex @@ -33,26 +33,42 @@ defmodule AL.Bootstrap.Lists do set_method(:list, :map, :list_map) set_class(:list_map, :behaviour) set_oapply(:list_map, [[], _func, []]) do end + set_oapply(:list_map, [[], _head, _body, []]) do end set_oapply(:list_map, [[fh | ft], func, [sh | st]]) do send(fh, func, [sh]) send(ft, :map, [func, st]) end + set_oapply(:list_map, [[fh | ft], head, body, [sh | st]]) do + call(head, body, [fh, sh]) + send(ft, :map, [head, body, st]) + end set_method(:list, :fold_left, :list_fold_left) set_class(:list_fold_left, :behaviour) set_oapply(:list_fold_left, [[], _func, acc, acc]) do end + set_oapply(:list_fold_left, [[], _head, _body, acc, acc]) do end set_oapply(:list_fold_left, [[h | t], func, acc, result]) do send(acc, func, [h, next_acc]) send(t, :fold_left, [func, next_acc, result]) end + set_oapply(:list_fold_left, [[h | t], head, body, acc, result]) do + print(acc) + call(head, body, [acc, h, next_acc]) + send(t, :fold_left, [head, body, next_acc, result]) + end set_method(:list, :fold_right, :list_fold_right) set_class(:list_fold_right, :behaviour) set_oapply(:list_fold_right, [[], _func, acc, acc]) do end + set_oapply(:list_fold_right, [[], _head, _body, acc, acc]) do end set_oapply(:list_fold_right, [[h | t], func, acc, result]) do send(t, :fold_right, [func, acc, next_acc]) send(next_acc, func, [h, result]) end + set_oapply(:list_fold_right, [[h | t], head, body, acc, result]) do + send(t, :fold_right, [head, body, acc, next_acc]) + call(head, body, [next_acc, h, result]) + end defmethod(:list, :flatten, [lists, result]) do send(lists, :fold_left, [:concat, [], result]) diff --git a/lib/AL/command.ex b/lib/AL/command.ex index 2aa1035..a1b94e0 100644 --- a/lib/AL/command.ex +++ b/lib/AL/command.ex @@ -13,6 +13,8 @@ defmodule AL.Command do | :retract_super | :retract_method | :retract_oapply + | :send_async + | :send_elixir @type command() :: {:set_class, {AL.Var.t(), AL.Var.t()}} @@ -27,6 +29,7 @@ defmodule AL.Command do | {:send_async, {AL.Var.t(), AL.Var.t(), AL.Var.t()}} | {:send_elixir, {AL.Var.t(), AL.Var.t()}} + @doc """ Initialise the event log, or re-use the one on disc. """ @@ -186,7 +189,7 @@ defmodule AL.Command do def send_elixir(tx_id, pid, message) do write_command(tx_id, {:send_elixir, {pid, message}}) end - + @spec write_command(non_neg_integer(), command()) :: :ok def write_command(tx_id, command) do {t1, _t2} = inc_system_time() @@ -204,10 +207,12 @@ defmodule AL.Command do @spec fresh_id() :: atom() def fresh_id() do - label = case :mnesia.dirty_read(:meta, :id_counter) do - [{:meta, :id_counter, n}] -> n - [] -> 0 - end + label = + case :mnesia.dirty_read(:meta, :id_counter) do + [{:meta, :id_counter, n}] -> n + [] -> 0 + end + :mnesia.dirty_write({:meta, :id_counter, label + 1}) :"##{label}" end @@ -219,10 +224,12 @@ defmodule AL.Command do @spec fresh_scope() :: String.t() def fresh_scope() do - n = case :mnesia.dirty_read(:meta, :scope_counter) do - [{:meta, :scope_counter, n}] -> n - [] -> 0 - end + n = + case :mnesia.dirty_read(:meta, :scope_counter) do + [{:meta, :scope_counter, n}] -> n + [] -> 0 + end + :mnesia.dirty_write({:meta, :scope_counter, n + 1}) Integer.to_string(n) end @@ -234,14 +241,18 @@ defmodule AL.Command do defp meta_label(namespace, counter_key, key) do meta_key = {namespace, key} + case :mnesia.dirty_read(:meta, meta_key) do [{:meta, ^meta_key, label}] -> label + [] -> - label = case :mnesia.dirty_read(:meta, counter_key) do - [{:meta, ^counter_key, n}] -> n - [] -> 0 - end + label = + case :mnesia.dirty_read(:meta, counter_key) do + [{:meta, ^counter_key, n}] -> n + [] -> 0 + end + :mnesia.dirty_write({:meta, counter_key, label + 1}) :mnesia.dirty_write({:meta, meta_key, label}) label diff --git a/lib/AL/objects.ex b/lib/AL/object.ex similarity index 98% rename from lib/AL/objects.ex rename to lib/AL/object.ex index 8480908..c6cef11 100644 --- a/lib/AL/objects.ex +++ b/lib/AL/object.ex @@ -1,14 +1,20 @@ -defmodule AL.Objects do +defmodule AL.Object do @moduledoc """ I am the in-memory store for AL objects """ + use TypedStruct + @type class_record() :: {:class, AL.Var.t(), AL.Var.t()} @type super_record() :: {:super, AL.Var.t(), AL.Var.t()} @type slots_record() :: {:slots, AL.Var.t(), AL.Var.t()} @type method_record() :: {:method, AL.Var.t(), AL.Var.t(), AL.Var.t()} @type oapply_record() :: {:oapply, AL.Var.t(), AL.Var.t(), [AL.goal()]} + typedstruct enforce: true do + field(:id, any(), enforce: true) + end + def setup() do case :mnesia.create_table(:class, attributes: [:object, :class], @@ -210,7 +216,7 @@ defmodule AL.Objects do end :mnesia.write({:slots, object, merged}) - + :send_async -> :ok diff --git a/lib/AL/views.ex b/lib/AL/views.ex new file mode 100644 index 0000000..9c806e3 --- /dev/null +++ b/lib/AL/views.ex @@ -0,0 +1,76 @@ +defmodule AL.Views do + @doc """ + I define GlamorousToolkit views for AL + """ + + + use AL + use GtBridge.View + + alias GtBridge.Phlow.Mondrian + + defview object_examine_view(self = %AL.Object{}, builder) do + {:atomic, {bindings, _program_state}} = AL.run do + send(^self.id, :examine, [info]) + end + + GtBridge.Views.MapGraph.graph(Map.get(bindings, :"$info"), builder) + |> Mondrian.title("Examine") + end + + defview cell_view(self = %AL.Object{}, builder) do + result = AL.run do + class(^self.id, :cell) + get_slot(^self.id, :subscribers, subscribers) + get_slot(^self.id, :value, value) + unify(nodes, [^self.id | subscribers]) + unify(children, %{^self.id => subscribers}) + end + case result do + {:atomic, {bindings, _program_state}} -> + builder.mondrian() + |> Mondrian.title("Cell View") + |> Mondrian.nodes(Enum.map(Map.get(bindings, :"$nodes"), + fn x -> %AL.Object{id: x} end)) + |> Mondrian.node_label(fn node -> Atom.to_string(node.id) end) + |> Mondrian.edges(fn node -> case Map.get(Map.get(bindings, :"$children"), node.id) do + nil -> [] + children -> Enum.map(children, fn c -> %AL.Object{id: c} end) + end + end) + |> Mondrian.layout(:horizontal_tree) + _e -> builder.empty() + end + end + + defview propagator_view(self = %AL.Object{}, builder) do + result = AL.run do + class(^self.id, :propagator) + get_slot(^self.id, :input_cells, input_cells) + get_slot(^self.id, :output_cell, output_cell) + unify(nodes, [^self.id | [output_cell | input_cells]]) + send(input_cells, :fold_left, [[acc, h, result], + [send(acc, :put, [h, [^self.id], result])], + %{^self.id => [output_cell]}, children]) + end + + IO.inspect(result) + + case result do + {:atomic, {bindings, _program_state}} -> + + builder.mondrian() + |> Mondrian.title("Propagator View") + |> Mondrian.nodes(Enum.map(Map.get(bindings, :"$nodes"), + fn x -> %AL.Object{id: x} end)) + |> Mondrian.node_label(fn node -> Atom.to_string(node.id) end) + |> Mondrian.edges(fn node -> case Map.get(Map.get(bindings, :"$children"), node.id) do + nil -> [] + children -> Enum.map(children, fn c -> %AL.Object{id: c} end) + end + end) + |> Mondrian.layout(:horizontal_tree) + _e -> builder.empty() + end + end +end diff --git a/lib/examples/e_AL.ex b/lib/examples/e_AL.ex index 5a5750c..8077244 100644 --- a/lib/examples/e_AL.ex +++ b/lib/examples/e_AL.ex @@ -189,10 +189,11 @@ defmodule Examples.AL do assert slots == %{a: 99, b: 2} slots end + example map_get() do {:atomic, {bindings, program_state}} = run do - send(%{a: 3, b: 4, c: 3}, :map_get, [k, 3]) + send(%{a: 3, b: 4, c: 3}, :get, [k, 3]) end assert Map.get(bindings, :"$k") == :c or Map.get(bindings, :"$k") == :a @@ -204,6 +205,17 @@ defmodule Examples.AL do program_state end + example map_put() do + {:atomic, {bindings, program_state}} = + run do + send(%{a: 3, b: 4, c: 3}, :put, [:c, 4, m2]) + end + + assert bindings|> Map.get(:"$m2") |> Map.get(:c) == 4 + + program_state + end + example gensym() do {:atomic, {bindings, _}} = run do @@ -242,7 +254,7 @@ defmodule Examples.AL do Process.sleep(50) {:atomic, results} = - :mnesia.transaction(fn -> AL.Objects.scan_slots(:test_object, :"$slots") end) + :mnesia.transaction(fn -> AL.Object.scan_slots(:test_object, :"$slots") end) assert Enum.any?(results, fn {:slots, _, slots} -> Map.get(slots, :processed) == true end) end @@ -258,7 +270,7 @@ defmodule Examples.AL do Process.sleep(50) {:atomic, results} = - :mnesia.transaction(fn -> AL.Objects.scan_slots(:test_object_2, :"$slots") end) + :mnesia.transaction(fn -> AL.Object.scan_slots(:test_object_2, :"$slots") end) assert Enum.any?(results, fn {:slots, _, slots} -> Map.get(slots, :processed) == true end) end @@ -327,7 +339,7 @@ defmodule Examples.AL do assert Map.get(bindings, :"$out") == :hello :ok end - + example list_tests() do {:atomic, {bindings, result}} = run do send([:w, :x, :y, :z], :hd, [head]) @@ -351,4 +363,13 @@ defmodule Examples.AL do assert length(Map.get(bindings, :"$of_same_length")) == 4 result end + + example call_lambda_map() do + {:atomic, {bindings, _}} = + run do + send([:a, :b, :c], :map, [[x, %{id: x}], [], out]) + end + assert Map.get(bindings, :"$out") == [%{id: :a}, %{id: :b}, %{id: :c}] + :ok + end end diff --git a/lib/examples/e_AL_bootstrap.ex b/lib/examples/e_AL_bootstrap.ex index 84e0c06..83e7edc 100644 --- a/lib/examples/e_AL_bootstrap.ex +++ b/lib/examples/e_AL_bootstrap.ex @@ -9,7 +9,7 @@ defmodule Examples.ALBootstrap do example bootstrapped_classes() do :mnesia.transaction(fn -> - class_results = AL.Objects.scan_class(:"$object", :"$class") + class_results = AL.Object.scan_class(:"$object", :"$class") Enum.take(class_results, 3) end) @@ -17,7 +17,7 @@ defmodule Examples.ALBootstrap do example bootstrapped_supers() do :mnesia.transaction(fn -> - super_results = AL.Objects.scan_super(:"$object", :"$super") + super_results = AL.Object.scan_super(:"$object", :"$super") Enum.take(super_results, 3) end) @@ -25,7 +25,7 @@ defmodule Examples.ALBootstrap do example bootstrapped_methods() do :mnesia.transaction(fn -> - method_results = AL.Objects.scan_method(:"$object", :"$method_name", :"$method_id") + method_results = AL.Object.scan_method(:"$object", :"$method_name", :"$method_id") Enum.take(method_results, 1) end) @@ -33,7 +33,7 @@ defmodule Examples.ALBootstrap do example bootstrapped_oapply() do :mnesia.transaction(fn -> - oapply_results = AL.Objects.scan_oapply(:"$object", :"$head", :"$body") + oapply_results = AL.Object.scan_oapply(:"$object", :"$head", :"$body") Enum.take(oapply_results, 1) end) diff --git a/lib/examples/e_AL_genserver.ex b/lib/examples/e_AL_genserver.ex index 2e7bf91..ddb19eb 100644 --- a/lib/examples/e_AL_genserver.ex +++ b/lib/examples/e_AL_genserver.ex @@ -62,7 +62,7 @@ defmodule Examples.ALGenserver do {:ok, pid} = CounterService.start_link(:my_counter) {:atomic, results} = - :mnesia.transaction(fn -> AL.Objects.scan_class(:my_counter, :"$class") end) + :mnesia.transaction(fn -> AL.Object.scan_class(:my_counter, :"$class") end) assert Enum.any?(results, fn {:class, _, c} -> c == :elixir_process end) @@ -79,7 +79,7 @@ defmodule Examples.ALGenserver do Process.sleep(50) {:atomic, after_stop} = - :mnesia.transaction(fn -> AL.Objects.scan_class(:my_counter, :"$class") end) + :mnesia.transaction(fn -> AL.Object.scan_class(:my_counter, :"$class") end) assert after_stop == [] diff --git a/mix.exs b/mix.exs index 2300f85..dc2f8a0 100644 --- a/mix.exs +++ b/mix.exs @@ -24,7 +24,8 @@ defmodule AL.MixProject do defp deps do [ {:typed_struct, "~> 0.3.0"}, - {:ex_example, "~> 0.1.1"} + {:ex_example, "~> 0.1.1"}, + {:gt_bridge, git: "https://github.com/mariari/ElixirGtBridge.git"} ] end end diff --git a/mix.lock b/mix.lock index e4a9aac..0fe955b 100644 --- a/mix.lock +++ b/mix.lock @@ -1,11 +1,31 @@ %{ "cachex": {:hex, :cachex, "4.1.1", "574c5cd28473db313a0a76aac8c945fe44191659538ca6a1e8946ec300b1a19f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:ex_hash_ring, "~> 6.0", [hex: :ex_hash_ring, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "d6b7449ff98d6bb92dda58bd4fc3189cae9f99e7042054d669596f56dc503cd8"}, + "cowboy": {:hex, :cowboy, "2.16.1", "fa04080b602ff25c40a7700f2dc0152dbc1ba26b42093ae0fa9bb7a337d5a242", [:make, :rebar3], [{:cowlib, ">= 2.16.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "b8ea4dd317a043e3177ec840cfa3bcb47cfb41035d3abb24d954dc7d51def399"}, + "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, + "cowlib": {:hex, :cowlib, "2.17.1", "3e6053016d1ab245730f0af688755476dcedb1c25ed8fb5751f59a2bfdc0c9af", [:make, :rebar3], [], "hexpm", "ff08bd17e6dd931445b18af77315b9b5fe052407110964ad2588c686b57b5e3f"}, "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, + "event_broker": {:hex, :event_broker, "1.1.1", "40788d9131e5ff87ed97765dd8818186ada9b0cc816af9811a9270b0bfeff5a8", [:make, :mix], [{:ex_example, "~> 0.1.1", [hex: :ex_example, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "15c9d400542f5f544eeea90979cf108b5b3220172d925eb33143b14af44b106b"}, "ex_example": {:hex, :ex_example, "0.1.1", "6651636401262149399420415b97b3dbcbe034aaf84e2336696eef4a780a6ee8", [:mix], [{:cachex, "~> 4.1.1", [hex: :cachex, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16.0", [hex: :libgraph, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "df2730873fa1ce4e2650693a67026517aadd4ac1a0011afd4e17b348474bbd23"}, "ex_hash_ring": {:hex, :ex_hash_ring, "6.0.4", "bef9d2d796afbbe25ab5b5a7ed746e06b99c76604f558113c273466d52fa6d6b", [:mix], [], "hexpm", "89adabf31f7d3dfaa36802ce598ce918e9b5b33bae8909ac1a4d052e1e567d18"}, + "finch": {:hex, :finch, "0.22.0", "5c48fa6f9706a78eb9036cacb67b8b996b4e66d111c543f4c29bb0f879a6806b", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b94e83c47780fc6813f746a1f1a34ee65cda42da4c5ea26a68f0acc4498e23dc"}, + "gt_bridge": {:git, "https://github.com/mariari/ElixirGtBridge.git", "97ceed38b42515911a7e01f5a314f6d944e4bed8", []}, + "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, + "jexon": {:hex, :jexon, "0.9.5", "fa4c851c0576b6f6e3d563fdccea6172bc32bc6fac1d7f34b5b47cad6c699359", [:mix], [{:jason, "~> 1.4.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "76e4dea871745d5dc49515c74d3b287fb41fb742aca7d1c4b146cc8b8f70e2ba"}, "jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "mint": {:hex, :mint, "1.9.0", "d6f534c2a3e98b2a8cc749b4796eb77e9e3af79a76f96e4c74035a827de0d318", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "007154c7d8c43916aed3c93afd1f11aebbaa9c5ff4b7ba55ebe0d17ee0296042"}, + "msgpax": {:hex, :msgpax, "2.4.0", "4647575c87cb0c43b93266438242c21f71f196cafa268f45f91498541148c15d", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "ca933891b0e7075701a17507c61642bf6e0407bb244040d5d0a58597a06369d2"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, + "plug": {:hex, :plug, "1.19.2", "e4950525b22c6789dfb38a3f95d47171ba159da3fc5a33be9643b43d5e8adb98", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b6fce20a56af5e60fa5dfecf3f907bb98ec981be43c79a3809a499bc3d133de0"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.7.5", "261f21b67aea8162239b2d6d3b4c31efde4daa22a20d80b19c2c0f21b34b270e", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "20884bf58a90ff5a5663420f5d2c368e9e15ed1ad5e911daf0916ea3c57f77ac"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, + "ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"}, + "req": {:hex, :req, "0.5.18", "48e6431cb4135e8a7815e745177485369a9b4a9924d5fe68ca00eb09ceaed1ef", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.21.0 or ~> 0.22.0", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "fa03812c440a9754bf34355e0c5d4f3ed316458db62e3284b7a352ef8dc0b996"}, "sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"}, + "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, "unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"}, } From d0274657f47415798b858f4485f7503e8df94b56 Mon Sep 17 00:00:00 2001 From: l4e21 Date: Thu, 18 Jun 2026 12:21:09 +0100 Subject: [PATCH 6/9] Replace oapply default with send default --- .mnesiastore/LATEST.LOG | Bin 65627 -> 65696 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL.ex | 15 ++++++++++++ lib/AL/bootstrap/constraints.ex | 16 ++++++------- lib/AL/bootstrap/core.ex | 6 ++--- lib/AL/bootstrap/elixir_process.ex | 2 +- lib/AL/bootstrap/lists.ex | 26 ++++++++++---------- lib/AL/bootstrap/process.ex | 2 +- lib/AL/views.ex | 8 +++---- lib/examples/e_AL.ex | 28 +++++++++++----------- lib/examples/e_AL_constraints.ex | 22 ++++++++--------- lib/examples/e_AL_genserver.ex | 2 +- lib/examples/e_AL_objects.ex | 37 ++++++++++++----------------- 13 files changed, 86 insertions(+), 78 deletions(-) diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index 2f4f6136ef37703af3bc25cd468b7331315cd929..306c99a3442365878686d72fd27bcbb9eda44619 100644 GIT binary patch literal 65696 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 literal 65627 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 AGynhq diff --git a/lib/AL.ex b/lib/AL.ex index 32102b1..4422f28 100644 --- a/lib/AL.ex +++ b/lib/AL.ex @@ -95,6 +95,9 @@ defmodule AL do end end + @arithmetic_ops [:+, :-, :*, :/, :**] + @oapply_primitives [:is, :map_get, :map_put, :lookup, :fresh_id] + def ast_to_pattern([{:do, {:__block__, _, goals}}]), do: ast_to_pattern(goals) def ast_to_pattern([{:do, nil}]), do: nil @@ -187,6 +190,9 @@ defmodule AL do def ast_to_pattern({:call, _, [head, body, args]}), do: {:call, ast_to_pattern(head), ast_to_pattern(body), ast_to_pattern(args)} + def ast_to_pattern({:send, _, [receiver, method, args]}), + do: {:oapply, :send, [ast_to_pattern(receiver), ast_to_pattern(method), ast_to_pattern(args)]} + def ast_to_pattern({:send_async, _, [object, method, args]}), do: {:send_async, ast_to_pattern(object), ast_to_pattern(method), ast_to_pattern(args)} @@ -202,6 +208,15 @@ defmodule AL do } end + def ast_to_pattern({op, _, args}) when op in @arithmetic_ops and is_list(args), + do: {:oapply, op, Enum.map(args, &ast_to_pattern/1)} + + def ast_to_pattern({fun, _, args}) when fun in @oapply_primitives and is_list(args), + do: {:oapply, fun, Enum.map(args, &ast_to_pattern/1)} + + def ast_to_pattern({method, _, [receiver | args]}) when is_atom(method) and is_list(args), + do: {:oapply, :send, [ast_to_pattern(receiver), method, Enum.map(args, &ast_to_pattern/1)]} + def ast_to_pattern({fun, _, args}) when is_atom(fun) and is_list(args), do: {:oapply, fun, Enum.map(args, &ast_to_pattern/1)} diff --git a/lib/AL/bootstrap/constraints.ex b/lib/AL/bootstrap/constraints.ex index bf1c094..27260f3 100644 --- a/lib/AL/bootstrap/constraints.ex +++ b/lib/AL/bootstrap/constraints.ex @@ -3,7 +3,7 @@ defmodule AL.Bootstrap.Constraints do def setup() do run do - send(:class, :new, [%{name: :cell, super: :object, slots: [:subscribers, :value, :name]}, _]) + new(:class, %{name: :cell, super: :object, slots: [:subscribers, :value, :name]}, _) defmethod(:cell, :allocate, [self, args, cell_name]) do class(self, meta) @@ -23,7 +23,7 @@ defmodule AL.Bootstrap.Constraints do set_slots(self, %{value: value}) forall([get_slot(self, :subscribers, subscribers), - send(subscribers, :member, [subscriber])], + member(subscribers, subscriber)], [send_async(subscriber, :cell_updated, [self, value])]) cut end @@ -33,7 +33,7 @@ defmodule AL.Bootstrap.Constraints do set_slots(self, %{subscribers: [subscriber | subscribers]}) end - send(:class, :new, [%{name: :propagator, super: :object, slots: [:input_cells, :output_cell]}, _]) + new(:class, %{name: :propagator, super: :object, slots: [:input_cells, :output_cell]}, _) defmethod(:propagator, :allocate, [self, args, propagator]) do class(self, meta) @@ -49,8 +49,8 @@ defmodule AL.Bootstrap.Constraints do set_slots(self, %{input_cells: input_cells, output_cell: output_cell}) - forall([send(input_cells, :member, [input_cell])], - [send(input_cell, :subscribe, [self])]) + forall([member(input_cells, input_cell)], + [subscribe(input_cell, self)]) send_async(self, :cell_updated, [:none, :none]) end @@ -60,13 +60,13 @@ defmodule AL.Bootstrap.Constraints do get_slot(self, :output_cell, output_cell) findall(input_cell_value, - [send(input_cells, :member, [input_cell]), + [member(input_cells, input_cell), get_slot(input_cell, :value, input_cell_value)], input_cell_values) - forall([send(input_cell_values, :member, [input_cell_value])], + forall([member(input_cell_values, input_cell_value)], [not([unify(input_cell_value, :absent)])]) - send(self, :constrain, [input_cell_values, output_value]) + constrain(self, input_cell_values, output_value) send_async(output_cell, :constrain, [output_value]) end end diff --git a/lib/AL/bootstrap/core.ex b/lib/AL/bootstrap/core.ex index 21ac607..1e6ff99 100644 --- a/lib/AL/bootstrap/core.ex +++ b/lib/AL/bootstrap/core.ex @@ -91,9 +91,9 @@ defmodule AL.Bootstrap.Core do end defmethod(:class, :new, [self, args, new]) do - send(self, :construct, [construct]) - send(construct, :allocate, [args, alloc]) - send(alloc, :init, [args, new]) + construct(self, construct) + allocate(construct, args, alloc) + init(alloc, args, new) end defmethod(:object, :examine, [self, %{ diff --git a/lib/AL/bootstrap/elixir_process.ex b/lib/AL/bootstrap/elixir_process.ex index 596c363..ee3597f 100644 --- a/lib/AL/bootstrap/elixir_process.ex +++ b/lib/AL/bootstrap/elixir_process.ex @@ -3,7 +3,7 @@ defmodule AL.Bootstrap.ElixirProcess do def setup() do run do - send(:class, :new, [%{name: :elixir_process, super: :object, slots: []}, _]) + new(:class, %{name: :elixir_process, super: :object, slots: []}, _) defmethod(:elixir_process, :allocate, [self, args, new_obj]) do class(self, meta) map_get(args, :name, new_obj) diff --git a/lib/AL/bootstrap/lists.ex b/lib/AL/bootstrap/lists.ex index 595fbcb..2ae5f6b 100644 --- a/lib/AL/bootstrap/lists.ex +++ b/lib/AL/bootstrap/lists.ex @@ -3,7 +3,7 @@ defmodule AL.Bootstrap.Lists do def setup() do run do - send(:class, :new, [%{name: :list, super: :object, slots: []}, _]) + new(:class, %{name: :list, super: :object, slots: []}, _) defmethod(:list, :hd, [[h | _t], h]) do end defmethod(:list, :tl, [[_h | t], t]) do end @@ -12,22 +12,22 @@ defmodule AL.Bootstrap.Lists do set_class(:list_concat, :behaviour) set_oapply(:list_concat, [[], second, second]) do end set_oapply(:list_concat, [[fh | ft], second, [fh | inner]]) do - send(ft, :concat, [second, inner]) + concat(ft, second, inner) end set_method(:list, :member, :list_member) set_class(:list_member, :behaviour) set_oapply(:list_member, [[x | _t], x]) do end set_oapply(:list_member, [[_h | t], x]) do - send(t, :member, [x]) + member(t, x) end set_method(:list, :reverse, :list_reverse) set_class(:list_reverse, :behaviour) set_oapply(:list_reverse, [[], []]) do end set_oapply(:list_reverse, [[h | t], reversed]) do - send(t, :reverse, [reversed_tl]) - send(reversed_tl, :concat, [[h], reversed]) + reverse(t, reversed_tl) + concat(reversed_tl, [h], reversed) end set_method(:list, :map, :list_map) @@ -36,11 +36,11 @@ defmodule AL.Bootstrap.Lists do set_oapply(:list_map, [[], _head, _body, []]) do end set_oapply(:list_map, [[fh | ft], func, [sh | st]]) do send(fh, func, [sh]) - send(ft, :map, [func, st]) + map(ft, func, st) end set_oapply(:list_map, [[fh | ft], head, body, [sh | st]]) do call(head, body, [fh, sh]) - send(ft, :map, [head, body, st]) + map(ft, head, body, st) end set_method(:list, :fold_left, :list_fold_left) @@ -49,12 +49,12 @@ defmodule AL.Bootstrap.Lists do set_oapply(:list_fold_left, [[], _head, _body, acc, acc]) do end set_oapply(:list_fold_left, [[h | t], func, acc, result]) do send(acc, func, [h, next_acc]) - send(t, :fold_left, [func, next_acc, result]) + fold_left(t, func, next_acc, result) end set_oapply(:list_fold_left, [[h | t], head, body, acc, result]) do print(acc) call(head, body, [acc, h, next_acc]) - send(t, :fold_left, [head, body, next_acc, result]) + fold_left(t, head, body, next_acc, result) end set_method(:list, :fold_right, :list_fold_right) @@ -62,23 +62,23 @@ defmodule AL.Bootstrap.Lists do set_oapply(:list_fold_right, [[], _func, acc, acc]) do end set_oapply(:list_fold_right, [[], _head, _body, acc, acc]) do end set_oapply(:list_fold_right, [[h | t], func, acc, result]) do - send(t, :fold_right, [func, acc, next_acc]) + fold_right(t, func, acc, next_acc) send(next_acc, func, [h, result]) end set_oapply(:list_fold_right, [[h | t], head, body, acc, result]) do - send(t, :fold_right, [head, body, acc, next_acc]) + fold_right(t, head, body, acc, next_acc) call(head, body, [next_acc, h, result]) end defmethod(:list, :flatten, [lists, result]) do - send(lists, :fold_left, [:concat, [], result]) + fold_left(lists, :concat, [], result) end set_method(:list, :same_length, :list_same_length) set_class(:list_same_length, :behaviour) set_oapply(:list_same_length, [[], []]) do end set_oapply(:list_same_length, [[_fh | ft], [_sh | st]]) do - send(ft, :same_length, [st]) + same_length(ft, st) end end end diff --git a/lib/AL/bootstrap/process.ex b/lib/AL/bootstrap/process.ex index bface60..d6082c2 100644 --- a/lib/AL/bootstrap/process.ex +++ b/lib/AL/bootstrap/process.ex @@ -3,7 +3,7 @@ defmodule AL.Bootstrap.Process do def setup() do run do - send(:class, :new, [%{name: :process, super: :object, slots: []}, _]) + new(:class, %{name: :process, super: :object, slots: []}, _) defmethod(:process, :allocate, [self, args, new_obj]) do class(self, meta) diff --git a/lib/AL/views.ex b/lib/AL/views.ex index 9c806e3..f5dd6ec 100644 --- a/lib/AL/views.ex +++ b/lib/AL/views.ex @@ -11,7 +11,7 @@ defmodule AL.Views do defview object_examine_view(self = %AL.Object{}, builder) do {:atomic, {bindings, _program_state}} = AL.run do - send(^self.id, :examine, [info]) + examine(^self.id, info) end GtBridge.Views.MapGraph.graph(Map.get(bindings, :"$info"), builder) @@ -49,9 +49,9 @@ defmodule AL.Views do get_slot(^self.id, :input_cells, input_cells) get_slot(^self.id, :output_cell, output_cell) unify(nodes, [^self.id | [output_cell | input_cells]]) - send(input_cells, :fold_left, [[acc, h, result], - [send(acc, :put, [h, [^self.id], result])], - %{^self.id => [output_cell]}, children]) + fold_left(input_cells, [acc, h, result], + [put(acc, h, [^self.id], result)], + %{^self.id => [output_cell]}, children) end IO.inspect(result) diff --git a/lib/examples/e_AL.ex b/lib/examples/e_AL.ex index 8077244..b6c8100 100644 --- a/lib/examples/e_AL.ex +++ b/lib/examples/e_AL.ex @@ -48,7 +48,7 @@ defmodule Examples.AL do {:atomic, {bindings, result}} = run do method(:object, :init, init_method) - send(init_method, :meta, [:"$class", :"$metaclass"]) + meta(init_method, :"$class", :"$metaclass") end assert Map.get(bindings, :"$class") == :behaviour @@ -193,7 +193,7 @@ defmodule Examples.AL do example map_get() do {:atomic, {bindings, program_state}} = run do - send(%{a: 3, b: 4, c: 3}, :get, [k, 3]) + get(%{a: 3, b: 4, c: 3}, k, 3) end assert Map.get(bindings, :"$k") == :c or Map.get(bindings, :"$k") == :a @@ -208,7 +208,7 @@ defmodule Examples.AL do example map_put() do {:atomic, {bindings, program_state}} = run do - send(%{a: 3, b: 4, c: 3}, :put, [:c, 4, m2]) + put(%{a: 3, b: 4, c: 3}, :c, 4, m2) end assert bindings|> Map.get(:"$m2") |> Map.get(:c) == 4 @@ -233,7 +233,7 @@ defmodule Examples.AL do {:atomic, {bindings, _}} = run do - send(:process, :new, [%{method: :handle, head: ^head, body: ^body}, new_proc]) + new(:process, %{method: :handle, head: ^head, body: ^body}, new_proc) cut end @@ -342,15 +342,15 @@ defmodule Examples.AL do example list_tests() do {:atomic, {bindings, result}} = run do - send([:w, :x, :y, :z], :hd, [head]) - send([:w, :x, :y, :z], :tl, [tail]) - send([:a, :b, :c], :concat, [[:d, :e, :f], sum]) - send([:b, :c, :d, :e, :f], :reverse, [reversed]) - send([[:a, :b], [:c, :d, :e]], :map, [:reverse, mapped]) - send([[:a], [:b], [:c], [:d]], :fold_left, [:concat, [:starter], folded_left]) - send([[:a], [:b], [:c], [:d]], :fold_right, [:concat, [:starter], folded_right]) - send([[:a, :b], [:c, :d, :e]], :flatten, [flattened]) - send([:c, :d, :e, :f], :same_length, [of_same_length]) + hd([:w, :x, :y, :z], head) + tl([:w, :x, :y, :z], tail) + concat([:a, :b, :c], [:d, :e, :f], sum) + reverse([:b, :c, :d, :e, :f], reversed) + map([[:a, :b], [:c, :d, :e]], :reverse, mapped) + fold_left([[:a], [:b], [:c], [:d]], :concat, [:starter], folded_left) + fold_right([[:a], [:b], [:c], [:d]], :concat, [:starter], folded_right) + flatten([[:a, :b], [:c, :d, :e]], flattened) + same_length([:c, :d, :e, :f], of_same_length) end assert Map.get(bindings, :"$sum") == [:a, :b, :c, :d, :e, :f] assert Map.get(bindings, :"$reversed") == [:f, :e, :d, :c, :b] @@ -367,7 +367,7 @@ defmodule Examples.AL do example call_lambda_map() do {:atomic, {bindings, _}} = run do - send([:a, :b, :c], :map, [[x, %{id: x}], [], out]) + map([:a, :b, :c], [x, %{id: x}], [], out) end assert Map.get(bindings, :"$out") == [%{id: :a}, %{id: :b}, %{id: :c}] :ok diff --git a/lib/examples/e_AL_constraints.ex b/lib/examples/e_AL_constraints.ex index f189376..495b963 100644 --- a/lib/examples/e_AL_constraints.ex +++ b/lib/examples/e_AL_constraints.ex @@ -8,8 +8,8 @@ defmodule Examples.ALConstraints do example constant() do {:atomic, {_bindings, _state}} = run do - send(:cell, :new, [%{name: :x}, cell]) - send(:propagator, :new, [%{input_cells: [], output_cell: cell}, propagator]) + new(:cell, %{name: :x}, cell) + new(:propagator, %{input_cells: [], output_cell: cell}, propagator) defmethod(propagator, :constrain, [_self, [], 2]) do end cut end @@ -29,8 +29,8 @@ defmodule Examples.ALConstraints do constant() {:atomic, {bindings, state}} = run do - send(:cell, :new, [%{name: :y}, y_cell]) - send(:propagator, :new, [%{input_cells: [:x], output_cell: y_cell}, propagator]) + new(:cell, %{name: :y}, y_cell) + new(:propagator, %{input_cells: [:x], output_cell: y_cell}, propagator) defmethod(propagator, :constrain, [_self, [x_val], y_val]) do is(y_val, 1 + x_val) end @@ -49,13 +49,13 @@ defmodule Examples.ALConstraints do example bidirectional_adder() do {:atomic, {bindings, state}} = run do - send(:cell, :new, [%{name: :a}, a]) - send(:cell, :new, [%{name: :b}, b]) - send(:cell, :new, [%{name: :c}, c]) - - send(:propagator, :new, [%{input_cells: [a, b], output_cell: c}, propagator_ab]) - send(:propagator, :new, [%{input_cells: [a, c], output_cell: b}, propagator_ac]) - send(:propagator, :new, [%{input_cells: [b, c], output_cell: a}, propagator_bc]) + new(:cell, %{name: :a}, a) + new(:cell, %{name: :b}, b) + new(:cell, %{name: :c}, c) + + new(:propagator, %{input_cells: [a, b], output_cell: c}, propagator_ab) + new(:propagator, %{input_cells: [a, c], output_cell: b}, propagator_ac) + new(:propagator, %{input_cells: [b, c], output_cell: a}, propagator_bc) defmethod(propagator_ab, :constrain, [_self, [a_val, b_val], c_val]) do is(c_val, a_val + b_val) diff --git a/lib/examples/e_AL_genserver.ex b/lib/examples/e_AL_genserver.ex index ddb19eb..fd837dc 100644 --- a/lib/examples/e_AL_genserver.ex +++ b/lib/examples/e_AL_genserver.ex @@ -19,7 +19,7 @@ defmodule Examples.ALGenserver do def init(object_id) do pid = self() run do - send(:elixir_process, :new, [%{name: ^object_id, pid: ^pid}, _]) + new(:elixir_process, %{name: ^object_id, pid: ^pid}, _) defmethod(^object_id, :increment, [self, amount]) do get_slot(self, :pid, p) send_elixir(p, {:increment, amount}) diff --git a/lib/examples/e_AL_objects.ex b/lib/examples/e_AL_objects.ex index cb2fde3..7121b67 100644 --- a/lib/examples/e_AL_objects.ex +++ b/lib/examples/e_AL_objects.ex @@ -10,13 +10,13 @@ defmodule Examples.ALObjects do example defmethod() do {:atomic, {bindings, _}} = run do - send(:class, :new, [%{name: :greeter, super: :object, slots: []}, _]) + new(:class, %{name: :greeter, super: :object, slots: []}, _) defmethod(:greeter, :greet, [self, name]) do end - send(:greeter, :new, [_, instance]) - send(instance, :greet, [:world]) + new(:greeter, _, instance) + greet(instance, :world) end assert Map.get(bindings, :"$instance") == %{class: :greeter} @@ -26,8 +26,8 @@ defmodule Examples.ALObjects do example make_point_object() do {:atomic, {bindings, result}} = run do - send(:class, :new, [%{name: :point, super: :object, slots: []}, new_point_class]) - send(new_point_class, :new, [_, new_point_object]) + new(:class, %{name: :point, super: :object, slots: []}, new_point_class) + new(new_point_class, _, new_point_object) cut end @@ -40,10 +40,7 @@ defmodule Examples.ALObjects do example metaclass_init_override() do {:atomic, {b, program_state}} = run do - send(:class, :new, [ - %{name: :counter_meta, super: :class, slots: []}, - _ - ]) + new(:class, %{name: :counter_meta, super: :class, slots: []}, _) set_slots(:counter_meta, %{count: []}) defmethod(:counter_meta, :init, [self, args, self]) do @@ -56,24 +53,22 @@ defmodule Examples.ALObjects do set_class(:initialise_counted_object, :behaviour) set_oapply(:initialise_counted_object, [self, _, self]) do - send(self, :meta, [meta, metaclass]) + meta(self, meta, metaclass) get_slot(metaclass, :count, count) set_slots(metaclass, %{count: ["new object!" | count]}) # TODO find a good way to do call next method cut end - send(:counter_meta, :new, [ + new(:counter_meta, %{name: :example_counter_meta_instance, super: :object, slots: []}, - counter_example_meta_instance - ]) + counter_example_meta_instance) - send(:counter_meta, :new, [ + new(:counter_meta, %{name: :example_counter_meta_instance_2, super: :object, slots: []}, - counter_example_meta_instance_2 - ]) + counter_example_meta_instance_2) - send(:example_counter_meta_instance, :new, [_, example_ii]) + new(:example_counter_meta_instance, _, example_ii) cut get_slot(:counter_meta, :count, c) @@ -87,9 +82,7 @@ defmodule Examples.ALObjects do example metaclass_alloc_override() do {:atomic, {b, program_state}} = run do - send(:class, :new, [ - %{name: :durable_meta, super: :object, slots: []}, - _]) + new(:class, %{name: :durable_meta, super: :object, slots: []}, _) defmethod(:durable_meta, :allocate, [self, args, name]) do map_get(args, :name, name) map_get(args, :slots, slots) @@ -101,7 +94,7 @@ defmodule Examples.ALObjects do set_slots(name, slots) end - send(:durable_meta, :new, [%{slots: [], name: :alloc_overriden}, obj]) + new(:durable_meta, %{slots: [], name: :alloc_overriden}, obj) class(obj, obj_class) end @@ -114,7 +107,7 @@ defmodule Examples.ALObjects do example examine() do {:atomic, {bindings, program_state}} = run do - send(:class, :examine, [info]) + examine(:class, info) map_get(info, :methods, methods) map_get(info, :classes, classes) map_get(info, :supers, supers) From 5bcbefe7e8ff2c74716afb9929ab5e55d1349461 Mon Sep 17 00:00:00 2001 From: l4e21 Date: Thu, 18 Jun 2026 13:17:53 +0100 Subject: [PATCH 7/9] Provide proper tracing rather than prints --- .mnesiastore/LATEST.LOG | Bin 65696 -> 65663 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL.ex | 60 +++++++++++++++++------- lib/AL/bootstrap/core.ex | 2 - lib/AL/trace.ex | 88 +++++++++++++++++++++++++++++++++++ lib/examples/e_AL_objects.ex | 6 ++- lib/examples/e_AL_trace.ex | 29 ++++++++++++ test/al_test.exs | 4 ++ 8 files changed, 167 insertions(+), 22 deletions(-) create mode 100644 lib/AL/trace.ex create mode 100644 lib/examples/e_AL_trace.ex diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index 306c99a3442365878686d72fd27bcbb9eda44619..796857cf00c23f3e5be64d25e7d3380d5ec12ce4 100644 GIT binary patch literal 65663 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

Wiwu9SPJ zjSI@ZOo4I(Q;0mDT|}Ru#Xs&UGPT)LCx{Emw@FF4AX?gXH;|5{#B_(KR&oCRAH}K{I11>DfSAeL>F$DOBt#jYHb>ER984eB|z;kJQ54Lu6{xuQVP^cV6H= zVl}m>b`gNN7{J_9V78w-@k#R&V{gO*F-F#|Wrn_!x8%yY0V)T=t zzTSa>LDHScSSIPt_4Ey7dir}!bEtcu&wc$=kPoK1G%h74$~vQR@kwZzZXx-~{gQCf z%hqIRI^78UFwQ=(pQ#8zF|B#q{i!v$fcsP8Aodp$l_F-fg*kPqYPezsxq7N=xur;& z&_k8Y<=^>gON>VfhykS+C$tN78q=F{+g|D3@^3DP|$c0muU$2FE z)ItfhP(X$0WRta99<`H%+DSzDDVgzIjxT2qshmB6a-`%wRbOf33wYk{5^r~oSIlLJ zJzjLPx7E!t$qdT{5`RI!7m^F9-fGgP`C2TNN?9C~!nDR^*=l$6yp+32Qtlc^DJ=Oa zRXuSZP}BQ&ktEqglq5(XRvMWi?RAlaStMYBuFi(4nv{2C!e--m1+>JSk{%zlh4QFJ z2r0hSR$nQ`*2Wd%Y<*KZdj-Qe?H6=F*H&L?FPAp1^sK0jy9A`?eL>v)K-{Gu?lKT} He~$Zq2z>5L literal 65696 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 diff --git a/.mnesiastore/schema.DAT b/.mnesiastore/schema.DAT index 013eb48107ceb37c93f4725e948b616c733e0449..f6a4ac571a127f4d6610ce91628dbb6449fed31a 100644 GIT binary patch delta 199 zcmdnwv&m;ew;1o@AKViJbaqMPaWMY+F!_Q+*2Dyh%~s-_jJzg~tCvD$YW{7$AaR?K zN8ps(GN{n@$rqI}CniX3wvsMqc0Wa%GhkBT+hfW`l}iyy#FguSby_Z6R(+Dz=OOw^&W9 diff --git a/lib/AL.ex b/lib/AL.ex index 4422f28..14cde74 100644 --- a/lib/AL.ex +++ b/lib/AL.ex @@ -87,6 +87,8 @@ defmodule AL do field(:tx_id, non_neg_integer(), enforce: true, default: 0) field(:trace, [goal()], enforce: true, default: []) field(:program, [goal()], enforce: true, default: []) + field(:tracepoints, MapSet.t(), enforce: true, default: %MapSet{}) + field(:traced_calls, %{optional(scope()) => tuple()}, default: %{}) end defmacro __using__(_opts) do @@ -95,6 +97,11 @@ defmodule AL do end end + defdelegate trace(point), to: AL.Trace + defdelegate untrace(point), to: AL.Trace + defdelegate notrace(), to: AL.Trace + defdelegate tracepoints(), to: AL.Trace + @arithmetic_ops [:+, :-, :*, :/, :**] @oapply_primitives [:is, :map_get, :map_put, :lookup, :fresh_id] @@ -307,7 +314,8 @@ defmodule AL do choicepoint_stack: [{:mark, 0}], tx_id: tx_id, trace: [], - program: program + program: program, + tracepoints: AL.Trace.tracepoints() }) if result.active_choicepoint.bindings == nil do @@ -369,8 +377,8 @@ defmodule AL do } } - [{:mark, _} | rest_choices] -> - backtrack(%AL{state | choicepoint_stack: rest_choices}) + [{:mark, f} | rest_choices] -> + backtrack(%AL{trace_fail(state, f) | choicepoint_stack: rest_choices}) [:implies_mark | rest_choices] -> backtrack(%AL{state | choicepoint_stack: rest_choices}) @@ -670,6 +678,8 @@ defmodule AL do end def interp({:oapply, method_id_pattern, bind_head_pattern}, state) do + trace_info = trace_call(state, method_id_pattern, bind_head_pattern) + case AL.Object.scan_oapply(method_id_pattern, :"$head", :"$body") do [] -> backtrack(state) @@ -717,6 +727,7 @@ defmodule AL do goal_pointer: 0, scope_pointer: freshener }, + traced_calls: record_traced_call(state.traced_calls, freshener, trace_info), choicepoint_stack: alternative_choicepoints ++ [{:mark, freshener} | state.choicepoint_stack] } @@ -1021,7 +1032,8 @@ defmodule AL do choicepoint_stack: [], tx_id: tx_id, trace: [], - program: condition + program: condition, + tracepoints: AL.Trace.tracepoints() } do_collect(continue(initial), []) @@ -1041,31 +1053,43 @@ defmodule AL do end defp format_failure(trace) do - steps = trace |> Enum.reverse() |> Enum.map(&normalize_term/1) + steps = trace |> Enum.reverse() |> Enum.map(&AL.Trace.pretty/1) %{failed_on: List.last(steps), trace: steps} end - defp normalize_term(a) when is_atom(a) do - s = Atom.to_string(a) + defp trace_call(state, method_id, bind_head) do + {receiver, args} = + case bind_head do + [r | rest] -> {r, rest} + other -> {other, []} + end - cond do - Regex.match?(~r/^[0-9a-f]{32}$/, s) -> - :"##{AL.Command.id_label(a)}" + traced? = + MapSet.member?(state.tracepoints, method_id) or + (method_id != :send and MapSet.member?(state.tracepoints, receiver)) - true -> - a + if traced? do + depth = length(state.active_choicepoint.continuations) + AL.Trace.call(depth, receiver, method_id, args) + {depth, receiver, method_id} end end - defp normalize_term(t) when is_tuple(t), - do: t |> Tuple.to_list() |> Enum.map(&normalize_term/1) |> List.to_tuple() + defp record_traced_call(traced_calls, _freshener, nil), do: traced_calls - defp normalize_term(l) when is_list(l), do: Enum.map(l, &normalize_term/1) + defp record_traced_call(traced_calls, freshener, info), + do: Map.put(traced_calls, freshener, info) - defp normalize_term(m) when is_map(m), - do: Map.new(m, fn {k, v} -> {normalize_term(k), normalize_term(v)} end) + defp trace_fail(state, freshener) do + case Map.pop(state.traced_calls, freshener) do + {nil, _} -> + state - defp normalize_term(x), do: x + {{depth, receiver, method}, rest} -> + AL.Trace.fail(depth, receiver, method) + %AL{state | traced_calls: rest} + end + end def interp_is({:oapply, :+, [a, b]}, bindings), do: interp_is(a, bindings) + interp_is(b, bindings) diff --git a/lib/AL/bootstrap/core.ex b/lib/AL/bootstrap/core.ex index 1e6ff99..6c47b45 100644 --- a/lib/AL/bootstrap/core.ex +++ b/lib/AL/bootstrap/core.ex @@ -35,13 +35,11 @@ defmodule AL.Bootstrap.Core do implies( [method(self, method, id)], [ - print(["calling", id, "from", self, "with args", [self | args]]), oapply(id, [self | args]) ], [implies( [lookup(class, method, id)], [ - print(["calling", id, "from", class, "with args", [self | args]]), oapply(id, [self | args]) ], [:fail] diff --git a/lib/AL/trace.ex b/lib/AL/trace.ex new file mode 100644 index 0000000..02042f7 --- /dev/null +++ b/lib/AL/trace.ex @@ -0,0 +1,88 @@ +defmodule AL.Trace do + @moduledoc """ + I am the tracing module. I provide tracepoint functionality and readable + rendering of AL terms. + """ + + @spec trace(atom()) :: :ok + def trace(point) do + Application.put_env(:al, :tracepoints, MapSet.put(tracepoints(), point)) + end + + @spec untrace(atom()) :: :ok + def untrace(point) do + Application.put_env(:al, :tracepoints, MapSet.delete(tracepoints(), point)) + end + + @spec notrace() :: :ok + def notrace() do + Application.put_env(:al, :tracepoints, MapSet.new()) + end + + @spec tracepoints() :: MapSet.t() + def tracepoints() do + Application.get_env(:al, :tracepoints, MapSet.new()) + end + + @spec call(non_neg_integer(), term(), term(), [term()]) :: :ok + def call(depth, receiver, method, args) do + IO.puts([ + String.duplicate(" ", depth), + "Call: ", + inspect(pretty(receiver)), + " <- ", + inspect(pretty(method)), + "(", + args |> Enum.map(&inspect(pretty(&1))) |> Enum.join(", "), + ")" + ]) + end + + @spec fail(non_neg_integer(), term(), term()) :: :ok + def fail(depth, receiver, method) do + IO.puts([ + String.duplicate(" ", depth), + "Fail: ", + inspect(pretty(receiver)), + " ", + inspect(pretty(method)) + ]) + end + + @spec pretty(term()) :: term() + def pretty(a) when is_atom(a) do + s = Atom.to_string(a) + + cond do + hash?(s) -> :"##{AL.Command.id_label(a)}" + AL.Var.var?(a) -> :"#{strip_freshener(s)}" + true -> a + end + end + + def pretty(t) when is_tuple(t), + do: t |> Tuple.to_list() |> Enum.map(&pretty/1) |> List.to_tuple() + + def pretty(l) when is_list(l), do: Enum.map(l, &pretty/1) + + def pretty(m) when is_map(m), + do: Map.new(m, fn {k, v} -> {pretty(k), pretty(v)} end) + + def pretty(x), do: x + + defp hash?(s) do + byte_size(s) == 32 and Enum.all?(String.to_charlist(s), &(&1 in ?0..?9 or &1 in ?a..?f)) + end + + defp strip_freshener(s) do + s + |> String.split("_") + |> Enum.reverse() + |> Enum.drop_while(&integer_segment?/1) + |> Enum.reverse() + |> Enum.join("_") + end + + defp integer_segment?(""), do: false + defp integer_segment?(s), do: Enum.all?(String.to_charlist(s), &(&1 in ?0..?9)) +end diff --git a/lib/examples/e_AL_objects.ex b/lib/examples/e_AL_objects.ex index 7121b67..1808e12 100644 --- a/lib/examples/e_AL_objects.ex +++ b/lib/examples/e_AL_objects.ex @@ -74,8 +74,10 @@ defmodule Examples.ALObjects do get_slot(:counter_meta, :count, c) end - assert Map.get(b, :"$c") == ["new object!", "new class!", "new class!"] - + c = Map.get(b, :"$c") + assert List.first(c) == "new object!" + assert "new class!" in c + program_state end diff --git a/lib/examples/e_AL_trace.ex b/lib/examples/e_AL_trace.ex new file mode 100644 index 0000000..e0fbd59 --- /dev/null +++ b/lib/examples/e_AL_trace.ex @@ -0,0 +1,29 @@ +defmodule Examples.ALTrace do + @moduledoc """ + I provide tracing examples for AL + """ + + use ExExample + use AL + import ExUnit.Assertions + import ExUnit.CaptureIO + + example trace_object() do + AL.trace(:cell) + output = capture_io(fn -> run do new(:cell, %{name: :traced}, c) end end) + AL.notrace() + + assert String.contains?(output, "Call: :cell") + output + end + + example trace_clause_fail() do + AL.trace(:list_member) + output = capture_io(fn -> run do member([:a, :b], :z) end end) + AL.notrace() + + assert String.contains?(output, "Call:") + assert String.contains?(output, "Fail:") + output + end +end diff --git a/test/al_test.exs b/test/al_test.exs index f7430fd..1ba0420 100644 --- a/test/al_test.exs +++ b/test/al_test.exs @@ -17,3 +17,7 @@ end defmodule ALConstraintsTest do use ExExample.ExUnit, for: Examples.ALConstraints end + +defmodule ALTraceTest do + use ExExample.ExUnit, for: Examples.ALTrace +end From ba7db35789895d561afb8bede9f39524861f4b33 Mon Sep 17 00:00:00 2001 From: l4e21 Date: Thu, 18 Jun 2026 15:00:48 +0100 Subject: [PATCH 8/9] Add users --- .mnesiastore/LATEST.LOG | Bin 65663 -> 65746 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes lib/AL/application.ex | 1 + lib/AL/bootstrap/users.ex | 50 +++++++++++++++++++++++++ lib/examples/e_AL_users.ex | 74 +++++++++++++++++++++++++++++++++++++ test/al_test.exs | 4 ++ 6 files changed, 129 insertions(+) create mode 100644 lib/AL/bootstrap/users.ex create mode 100644 lib/examples/e_AL_users.ex diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index 796857cf00c23f3e5be64d25e7d3380d5ec12ce4..7d5f84a0b5e33c3fd7eed73607aef25bc4346b82 100644 GIT binary patch literal 65746 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

Wiwu9SPJ zjSI@ZOo4I(Q;0mDT|}Ru#Xs&UGPT)LCx{Emw@FF4AX?gXH;|5{#B_(KR&oCRAH}K{I11>DfSAeL>F$DOBt#jYHb>ER984eB|z;kJQ54Lu6{xuQVP^cV6H= zVl}m>b`gNN7{J_9V78w-@k#R&V{gO*F-F#|Wrn_!x8%yY0V)T=t zzTSa>LDHScSSIPt_4Ey7dir}!bEtcu&wc$=kPoK1G%h74$~vQR@kwZzZXx-~{gQCf z%hqIRI^78UFwQ=(pQ#8zF|B#q{i!v$fcsP8Aodp$l_F-fg*kPqYPezsxq7N=xur;& z&_k8Y<=^>gON>VfhykS+C$tN78q=F{+g|D3@^3DP|$c0muU$2FE z)ItfhP(X$0WRta99<`H%+DSzDDVgzIjxT2qshmB6a-`%wRbOf33wYk{5^r~oSIlLJ zJzjLPx7E!t$qdT{5`RI!7m^F9-fGgP`C2TNN?9C~!nDR^*=l$6yp+32Qtlc^DJ=Oa zRXuSZP}BQ&ktEqglq5(XRvMWi?RAlaStMYBuFi(4nv{2C!e--m1+>JSk{%zlh4QFJ z2r0hSR$nQ`*2Wd%Y<*KZdj-Qe?H6=F*H&L?FPAp1^sK0jy9A`?eL>v)K-{Gu?lKT} He~$Zq2z>5L diff --git a/.mnesiastore/schema.DAT b/.mnesiastore/schema.DAT index f6a4ac571a127f4d6610ce91628dbb6449fed31a..905ffaba0043d1a8992167182c7e941f1a1739c3 100644 GIT binary patch delta 199 zcmdnwv&m;ew-^s^^ZZ4&`nx3ZI2h|DpO?syE&u=k delta 205 zcmdnwv&m;ew;1o@AKViJbaqMPaWMY+F!_Q+*2Dyh%~s-_j65cftCvD$Y9?P)%G_)r zS AL.Bootstrap.Core.setup() + AL.Bootstrap.Users.setup() AL.Bootstrap.Lists.setup() AL.Bootstrap.ElixirProcess.setup() AL.Bootstrap.Process.setup() diff --git a/lib/AL/bootstrap/users.ex b/lib/AL/bootstrap/users.ex new file mode 100644 index 0000000..215d30b --- /dev/null +++ b/lib/AL/bootstrap/users.ex @@ -0,0 +1,50 @@ +defmodule AL.Bootstrap.Users do + use AL + + def setup() do + run do + new(:class, %{name: :owned, super: :object, slots: [:owner]}, _) + + defmethod(:owned, :allocate, [self, args, new]) do + class(self, meta) + gensym(new) + set_class(new, meta) + set_super(new, :object) + end + + defmethod(:owned, :init, [self, args, self]) do + set_slots(self, args) + end + + defmethod(:owned, :update, [self, slots]) do + set_slots(self, slots) + end + + defmethod(:owned, :may, [self, caller, _method, _args]) do + get_slot(self, :owner, owner) + unify(caller, owner) + end + + set_class(:guarded_send, :behaviour) + set_oapply(:guarded_send, [caller, self, method, args]) do + may(self, caller, method, args) + send(self, method, args) + end + + new(:class, %{name: :user, super: :owned, slots: [:name]}, _) + + new(:class, %{name: :owned_class, super: :class, slots: []}, _) + + defmethod(:owned_class, :allocate, [self, args, name]) do + map_get(args, :name, name) + map_get(args, :super, super) + map_get(args, :owner, owner) + + class(self, meta) + set_class(name, meta) + set_super(name, super) + set_slots(name, %{owner: owner}) + end + end + end +end diff --git a/lib/examples/e_AL_users.ex b/lib/examples/e_AL_users.ex new file mode 100644 index 0000000..fd825d5 --- /dev/null +++ b/lib/examples/e_AL_users.ex @@ -0,0 +1,74 @@ +defmodule Examples.ALUsers do + @moduledoc """ + I provide user and ownership examples for AL + """ + + use ExExample + use AL + import ExUnit.Assertions + + example owner_is_a_slot() do + {:atomic, {b, _}} = + run do + new(:user, %{name: :alice}, alice) + new(:owned, %{owner: alice, label: :thing}, obj) + get_slot(obj, :owner, owner) + class(obj, c) + end + + assert Map.get(b, :"$owner") == Map.get(b, :"$alice") + assert Map.get(b, :"$c") == :owned + :ok + end + + example owner_gated_update() do + {:atomic, {b, _}} = + run do + new(:user, %{name: :alice}, alice) + new(:user, %{name: :bob}, bob) + new(:owned, %{owner: alice, label: :secret}, obj) + end + + alice = Map.get(b, :"$alice") + bob = Map.get(b, :"$bob") + obj = Map.get(b, :"$obj") + + {:atomic, _} = + run do + oapply(:guarded_send, [^alice, ^obj, :update, [%{label: :updated}]]) + end + + {:atomic, {b2, _}} = + run do + get_slot(^obj, :label, l) + end + + assert Map.get(b2, :"$l") == :updated + + {:aborted, _} = + run do + oapply(:guarded_send, [^bob, ^obj, :update, [%{label: :hacked}]]) + end + + :ok + end + + example owned_class() do + {:atomic, {b, _}} = + run do + new(:user, %{name: :alice}, alice) + gensym(widget) + new(:owned_class, %{name: widget, super: :owned, owner: alice}, _) + new(widget, %{owner: alice, color: :red}, w) + get_slot(widget, :owner, class_owner) + get_slot(w, :owner, instance_owner) + class(w, wc) + end + + alice = Map.get(b, :"$alice") + assert Map.get(b, :"$class_owner") == alice + assert Map.get(b, :"$instance_owner") == alice + assert Map.get(b, :"$wc") == Map.get(b, :"$widget") + :ok + end +end diff --git a/test/al_test.exs b/test/al_test.exs index 1ba0420..8b3bd39 100644 --- a/test/al_test.exs +++ b/test/al_test.exs @@ -21,3 +21,7 @@ end defmodule ALTraceTest do use ExExample.ExUnit, for: Examples.ALTrace end + +defmodule ALUsersTest do + use ExExample.ExUnit, for: Examples.ALUsers +end From b98a1a6d1557848c2532875b17c1123a01b3a08f Mon Sep 17 00:00:00 2001 From: l4e21 Date: Mon, 22 Jun 2026 11:20:08 +0100 Subject: [PATCH 9/9] Add proper README --- .mnesiastore/LATEST.LOG | Bin 65746 -> 66040 bytes .mnesiastore/schema.DAT | Bin 9778 -> 9778 bytes README.md | 56 +++++++++++++++++++++++++--------------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/.mnesiastore/LATEST.LOG b/.mnesiastore/LATEST.LOG index 7d5f84a0b5e33c3fd7eed73607aef25bc4346b82..12da9bad5ec3d4749711199bb020c2cd1c3bb613 100644 GIT binary patch literal 66040 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 HB{BaGUk~c5 literal 65746 zcmeI5cYGVS6~{$UqBSf#iL;&I6=!%vqGXM(#7*0@nN6EC>14s<$)YTgsz~{)?$+s^ z=^oj8@4ffld+)u|HjSNG+yIFO9tlZ~@ayM8`;SB<@9@QYczD1);P4`~(Yn}_hQ^wj znq8Co@3C}MU(9J5$SzCt*(_xbJB|5amdPdfH_L0H9gEwS*VJ_^j(0dMnQSJTvUkp8 z`?9%wUo=@$6MHjRGqqu9vZi^}LAVDRyLF4-ZY(AUK6RMRWRl&j{$Ly1#yp~+{w4CH56#9VUGuK zPXKXGB-}_-&^WNM< zxx8PRvDD`VviThE<-G91-+5jVpS5JzpzZem`W0+o#AdZlb8aM=vxd`2dpOrG#3;^w z3Vvj@>{M2>)!eBx=IngJWAP7>l`bJtESOA#cJehcZo#>ma%XFKJ7sn@b&}A8;aS|t zAnqw3?x`T|X&~i3TH3Gh_7^wj; zc{C5QmU#>1E?Z0eHl04V)GI?d!W~ku*c@DOz<#yKmT^r z`y&X87w)S~=iIMq#Ra_ciXtNmykA&^|K^v{FQV&SP3m(aLwrL+WYmk%2Y>Usll``p z4|L{VXbrF&59WIpiocyY%N`hzFN^VA3Vqp>GpnaH$&!q9TV0)2Z@i0jc6Y|(an=>L z>|WNBYInsR>$TDn;wPsuY4@=e>FmgG$o2aA6$;Z{>NE`vXNTA_ zmd{dDu>Rre)?W)5CF>uf!7_ytV@bX;nh#wC3EF9bYbm}7X=MmA3hhlro6%kxYEr&_ zwD)=#PkimAu6FraQaUp2-_be1YW(8t|e}EN8yNczxUZIg0HUU6Mm=SI@8y(xcFYu ztptp{Cz05D#hQA4V@$dTaVDa4x9YVL!24We2z|SqyG(Z$C9!v;V%R&yG79U06YY6| z;MT21f_R^W+NDhmdbLk=4Kpz^lwy26(RbW+eB+qSS4H`MNlz4T_P#`B@0W_FrH;R6 zkUrr`8+iNd{^a`c~m94X;Aq2IAfh;@$z`-U;H~1>)Wf;@$(| z-V5U12jboj;ywW4J_zDI1mZpn;ywc6J__PKM!7};HT9HvVC6tfg_x$8*SRy*I%#U> zefGaB*^fLbMH={iWF=5#Y(404ssm%(CqUdMDc6Xd#VFI5j-LW?p9XQCA>7D}%KbSk z@Va+M;B^=Ni0SRks@&mS7;-efNZ!B;c< zP0xyZDgRVhSp%r2!e>d#ntQYDIS}`G5cdTT_eBu*B@p*z5cd@j_f^U@GU`qNFdHb- z*hv2xi2FK-`v&D2v8P7LG}gBN0&)Kh;{FH3eG|m}FNpgVh^xH(Ypfrw0m-YpCS$}` zHBniOFqO)b`uE(gdCD53>N;t$A zmu?L-7RQJxk#1x1Dv?hkzN&@#DkIEklxa-A$}_GpF6cQosyu@kY1`?bwpHeE#@ZG% zkBfrl7*WMv7}GP#XQ1qPJy1VdAH>~&a*g;ZMaM>%DE-c$`Zd;XHw1AvqFf_wtJttH z9dAr!HOAcp#N8Cc-3-Lt9K@Xo;%))rZVBQluNNBYzgvUkodx2~265+rxN|Aj$QX#4 zXUwCr8tX&bfVlH1*GStcb2%eSWgcg&UALwBHOAcz#N8gm-2ud10OBqLaTkHOi$UBS zDc6XvQl7(%Fj3E7tyI6p{9_x48>d_&ZHs!&ZKtvtYugSGcL|8w3F3BvxZNOb4~Y8_ z5ci`X?#DpfkAt|M0C9H$ad)O%BV!;c=DL*1YHSSrB#65U; z89z^DHP*KKfVf`(alZ)S?hE4X2jYGS#N8jnJpjc0GKl*X5cjJf?$$s1-o9Uh6F0z3{=eD=eW0sxjZtaY>b|*WM$+q~CF4mDs^|bbMEa~lB5^uM<+zb$7ytG{E1#$8pbIS@6 zb1;&OgU@xmbNND(arnpd&q{V$c2B&6wWjPI*4^5{vzjC=JK5FQ(H*yX8P9vt)|Kk* z?r2-mZnbwWX>0H5<&xN4OS<@-ab~Al`vYI*w~NStqPxp&w6R_*ongw4<8K#Rs61ex z#YnQK(MYm1)kGnUN_$)JLS9x|nJf%J78$MZp6k+-r2#D!oQ*03=PVE5kEyKT(bD(n?KT zVs~N+EjRVpI)UHUbe*6=dveo=7n{ueRL%ti{#hq@u`sRD*gUTc zG2Ze-9b}v-z74CFcR|H^`Evm>vO}?Nd%O7m&xtP^XYd<8b9ewDiim(B#zb!kd(1rr!$}}mJX`+>JA09k| z44V?lyvhtYDfcMzqH`=Gt#A1)>ziXIa!|i8C@weAXPLyg8ygGQ{w?duy3^gHItx(Huct8CNv^THAO*9A*g}@Fr5KxuavXBD5qpHi47*mvTv-^d1Fy$e@GKf zLF*$mePb&HvlYb9j@#8!4EL2&Q1rT1}6^ zpf^`gn+p{6A^MSTY|~n8vfj!obfC0M(G*=QH?{=~#kE25_JX*}K-@kMHx1$n#~Qc+ zBfADr$EPd@>39Id9RzVRAZ`}K9RhK`MY%@$F6s!PVJfS!@gBv^f#l7DxFaC$3J}); zaaU5V5uc$PA7-rYjsj#A`_TOPRPA2l-x0ZA$d1U}$GszxCIZ&)JYe;cyLjiVlt)Vf zTnm+l-G%PM?m~5?JQ6G~1{sCRhRYT!r7aevDUZ(DctpsfbHrlxVzOK!#*PYj$1-4T zd_V)%KCD+Z9@NA7w`oT-<^a9};(iyz{T_(>eGvBtAnp%A+#i9sKL&AEfw%{PxIY1L ze+uIM48;98i2Dl=_m?2igSfw`GVX6d^8OCQ{XK|#5QzH+$~6)!5Ia$WM;&9& z3V)=s8sq*6#Qigf`xnYJVpC!ZscD<~E0xulP5rIPxPJ%9`wzmMNU(3;kYL|#YZ>g* zzZ}D&6ASj~cF~CVSeanoA|*<;NJPo#4%}cDa}ey~uWvfT>AYPMb8}xklQ%=PiPDr{ zpRUqjZ(>A}EGmR40lV*(%iz{B;0Z+;C9r94^-p5W<}!pI4B{RF;vQOM+`~Zf9{&Hq z-N|19s&^Vvww2DMvl;#ZX*8csvC7UL6|6r3q&5`yNRYfofw)J5xW|CF>-4zy#;|3Zux+H55c w)M9aY5=>?@T#I5iBkz)h`(P5EK`zSJY^7Y!$kW>L0w!DvRBy2PtBNfn011*!TL1t6 diff --git a/README.md b/README.md index 5771e2e..d8a4775 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,49 @@ # AL -**TODO: Add description** +AL is a live, ACID, (eventually) bitemporal, relational-object operating system built around an append-only command log. It combines inspiration from: -## Installation +- XTDB -If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `al` to your list of dependencies in `mix.exs`: +- GlamorousToolkit/Pharo -```elixir -def deps do - [ - {:al, "~> 0.1.0"} - ] -end -``` +- Git -Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) -and published on [HexDocs](https://hexdocs.pm). Once published, the docs can -be found at . +- PROLOG +- LISP -TODO +- BEAM -- Re-evaluate this list! +- Urbit -- Loads more docs -- concurrency +The goal of the system is to be the first truly principled object-oriented PROLOG, and the personal computing environment of the future. +This runtime is the first version of AL, written in Elixir. The irony of the first Erlang interpreter having been written in PROLOG is not lost on us. -- constraint processing +## Features -- more bootstrapping for diff kinds of objects +- Live Smalltalk-style objects, defined relationally. No more faux-ADTs. Define protocols and their implementations. Mix and match at your leisure. +- Bidirectional Execution thanks to WAM semantics. +- Shutdown your system, continue later. All transactions are backed up by an on-disk database, hydrated at startup. +- ACID transactions ensure your work is safe and easy to reason about. +- Constraint Processing -- system wipes and rollbacks +And to come: + +- Bitemporality features: Model temporal systems. Spin off new branches of your system at different points in time and move between them easily. + +For discussion of the design philosophy of AL and resources that were consulted during its design, please see: +https://forum.anoma.net/t/design-philosophy-of-al-bibliography/2698 + +## Getting Started + +Install from terminal using `iex -S mix` or as a mix dependency. +From IEx, you can run `require AL`. + +`lib/examples` contains examples. +`lib/AL/bootstrap` contains the bootstrap code. +`lib/AL` contains the runtime code. + +Tips: + +- Use `examine(:my_object_id_here, info)` in order to get quick information about an object via its ID, such as its class(es!), superclass(es!), methods, and in the case of method objects, relevant clauses.