Closed bennn closed 3 weeks ago
Although mix run _
finishes without error, iex
prints an error. iex also runs to completion:
>% iex -S mix
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [dtrace]
Interactive Elixir (1.16.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Counter.kickoff()
Got 1
:ok
16:35:21.168 [error] Process #PID<0.142.0> raised an exception
** (UndefinedFunctionError) function MyServer.init/0 is undefined (module MyServer is not available)
MyServer.init()
This gives warnings that variable "value" is not used. Ok I guess the server needs some kind of functional update?
The choreography (roughly) expands to
value = receive, do: m -> m # this variable `value' gets overriden on the very next line
value = receive, do: m -> m
...
which is why Elixir is complaining that value
is never used. Elixir has no† mutation, so you'd want some kind of functional update.
†Every process has a mutable dictionary associated with it, but it's considered very bad form to use that.
I don't know what the output means:
warning: module Server is not a behaviour
The module Server
does not have any local functions that need implementing, so it's not considered a "behaviour". Chorex should be a little smarter about this: when you say use Chor.Chorex, :server
it shouldn't try to expand to @behaviour Server
if Server's interface is empty. This is what is happening now; we'll revisit once v0.2 is out.
Although
mix run _
finishes without error,iex
prints an error. iex also runs to completion
I think I see why this is happening.
In the case where both modules are implemented, both choreographies will finish with a return value of 1
, as that is the last value they each compute. In the case of the Client
actor specifically, its last line looks like:
send(config[Server], incr())
And Kernel.send/2
returns whatever the message was. So, Client
returns with 1. This is why this code:
receive do
{:choreography_return, Client, val} -> IO.puts("Got #{val}")
end
prints Got 1
, because the Client sends the message (to a nonexistent process) and then returns that value, which gets picked up by the parent, printed out, and then the whole thing terminates.
But why are we able to try to send a message in the first place? Why doesn't that immediately kill the Client
process?
Well, that's because we have a valid PID that we're trying to send to. We got this PID when the parent said
ps = spawn(MyServer, :init, [])
MyServer
didn't exist, so it threw a warning. However, it did still return a PID—nothing was running at that PID however. This is why we could pass it to send
without blowing up, and why the Client
process was able to complete.
Since iex
is a long-running process, we got to see that error message. However, running mix run counter.exs
exits too quickly for that error to get printed. Indeed, if you make this change:
receive do
{:choreography_return, Client, val} -> IO.puts("Got #{val}")
end
Process.sleep(1000) # Sleep for one second
then you do see the error message.
Please close if satisfied with answer.
Thanks!
What about the behavior question --- do you want to move that to a separate issue or leave this one open as the reminder?
I'll create a new issue.
Figure 2 of this paper has a Counter example with 2 parties https://doi.org/10.1016/j.jlamp.2023.100891
I'm trying to turn it into a choreography.
First attempt:
variable "value" is not used
. Ok I guess the server needs some kind of functional update?Moving on, I tried just deleting the first two lines. Full code:
[x] I don't know what the output means:
warning: module Server is not a behaviour
[x] Next I deleted the server module and tried putting everything together. This runs. How can it run without a MyServer module???