defmodule Robot do
def handle_call(:get_state, _from, state) do
something()
end
def get_state() do
GenServer.call(__MODULE__, :get_state)
end
end
robot = Robot.start_link()
robot.get_state()
just let me write (note the new flavor of def)
defstatefulmodule Robot do
def get_state() do
something()
end
end
robot = Robot.new()
robot.get_state()
Possibly add a defsync / defasync flavor of function definition to declare when the caller has to wait for the result of the function.
The idea is that I don't have to do the job of the compiler. It should add the boilerplate during the compilation to BEAM bytecode.
I know that there are a number of other possible cases that the handle_* functions can accommodate and this code does not, but this object-oriented-style state management is the purpose of almost all the occurrences of GenServers in the code bases I saw. Unfortunately it's littered by handle_* boilerplate that hides the purpose of the code and as all code, adds bugs by itself.
So: add handle_* to BEAM languages for maximum control but also add a dumbed down version that's all we need almost anytime.
Ok, I kind of see what you're saying, but IMHO, you're trying to hide the central, enabling abstraction of BEAM environments, which is sending messages to other processes.
If you really don't like the get_state above, I think it'd make more sense to just ditch it, and use GenServer.call(robot, :get_state) in places where you'd call robot.get_state(). Those three lines of definition don't seem to be doing you much good, and calling GenServer directly isn't too hard; I probably wouldn't write the underlying make_ref / monitor / send / receive / demonitor myself in the general case, but it can be useful sometimes.
In my experience with distributed Erlang, we'd have the server in one file, and the client in another; the exports for the client were the public api, and the handle_calls where the implementation. We'd often have a smidge of logic in the client, to pick the right pg to send messages to or whatever, so it useful to have that instead of just a gen_server:call in the calling code.
In the early days of Elixir what you are proposing here was popular[1], but over time the community largely decided it wasn't beneficial and I rarely see it any more.
Instead of
just let me write (note the new flavor of def) Possibly add a defsync / defasync flavor of function definition to declare when the caller has to wait for the result of the function.The idea is that I don't have to do the job of the compiler. It should add the boilerplate during the compilation to BEAM bytecode.
I know that there are a number of other possible cases that the handle_* functions can accommodate and this code does not, but this object-oriented-style state management is the purpose of almost all the occurrences of GenServers in the code bases I saw. Unfortunately it's littered by handle_* boilerplate that hides the purpose of the code and as all code, adds bugs by itself.
So: add handle_* to BEAM languages for maximum control but also add a dumbed down version that's all we need almost anytime.