diff --git a/README.md b/README.md index 8069917..0e992c9 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,10 @@ If you find any bugs or would like to suggest a feature, please [open an issue o We will collect change descriptions here until we come up with a more stable format when changes get bigger. + - v0.4.1; 2024-08-01 + + Bugfix: choreographies can now have literal maps in local expressions. + - v0.4.0; 2024-08-01 Functions can take arbitrary number of arguments from different actors. diff --git a/lib/chorex.ex b/lib/chorex.ex index f9723f6..7d13a21 100644 --- a/lib/chorex.ex +++ b/lib/chorex.ex @@ -1120,16 +1120,11 @@ defmodule Chorex do defp do_local_project({funcname, _meta, args} = funcall, acc, _env, label, _ctx) when is_atom(funcname) and is_list(args) do num_args = length(args) + variadics = [:{}, :%{}] builtins = Kernel.__info__(:functions) ++ Kernel.__info__(:macros) cond do - # The function name :{} is variadic: it constructs a tuple from - # all its arguments; since it doesn't have an arity, we have to - # special-case it here. - :{} == funcname -> - return(funcall, acc) - - # Likewise, __aliases__ is a special form and stays as-is. + # __aliases__ is a special form and stays as-is. :__aliases__ == funcname -> return(funcall, acc) @@ -1138,6 +1133,9 @@ defmodule Chorex do match?({:., [{:__aliases__, _, _} | _]}, {funcname, args}) -> return(funcall, acc) + Enum.member?(variadics, funcname) -> + return(funcall, acc) + Enum.member?(builtins, {funcname, num_args}) -> return(funcall, acc) diff --git a/mix.exs b/mix.exs index 676607d..ee3d1f4 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Chorex.MixProject do def project do [ app: :chorex, - version: "0.4.0", + version: "0.4.1", elixir: "~> 1.16", start_permanent: Mix.env() == :prod, description: description(), diff --git a/test/function_test.exs b/test/function_test.exs index 0438857..d206728 100644 --- a/test/function_test.exs +++ b/test/function_test.exs @@ -82,4 +82,50 @@ defmodule FunctionTest do assert_receive {:chorex_return, CounterClient, 55} end + + + defmodule Counter2Test do + defchor [Counter2Server, Counter2Client] do + def loop(Counter2Server.(%{count: i})) do + if Counter2Client.continue?() do + Counter2Client[L] ~> Counter2Server + Counter2Client.bump() ~> Counter2Server.(incr_amt) + loop(Counter2Server.(%{count: incr_amt + i})) + else + Counter2Client[R] ~> Counter2Server + Counter2Server.(i) ~> Counter2Client.(final_result) + Counter2Client.(final_result) + end + end + + def run() do + loop(Counter2Server.(%{count: 0})) + end + + def run(Counter2Server.(start)) do + loop(Counter2Server.(%{count: start})) + end + end + end + + defmodule MyCounter2Server do + use Counter2Test.Chorex, :counter2server + end + + defmodule MyCounter2Client do + use Counter2Test.Chorex, :counter2client + + def continue?() do + # Process dictionary black magic!! Do not do! Testing only! Only + # used to model getting value to continue from external source! + Process.put(:acc, 1 + Process.get(:acc, 0)) + 10 >= Process.get(:acc) + end + + def bump(), do: Process.get(:acc) + end + + test "looping increment with rich state" do + Chorex.start(Counter2Test.Chorex, %{Counter2Server => MyCounter2Server, Counter2Client => MyCounter2Client}, []) + end end