Experiment On Combining OOP With Erlang's Actor Model
Posted on 16 May 2014, tagged erlang
elixir
Erlang’s actor model is good to use, but its syntax is not. Elixir is a very great language, but I don’t think it is enough. So I hacked it a little and did some experiments on turning it into an OOP language in some way.
OOP Suits Actor Model Better
Erlang is a pure functional language. There are some arguments between OOP and FP. The point of pure functional programming is it avoids side effects: the function always gives the same result while the inputs are same. In Erlang or Elixir, it is true with the functions. But when there comes an actor (a gen_server
for example), it is not so true. Let’s look at a gen_server
module for example:
-module(example).
init(_Opts) -> {ok, []}.
handle_call(Msg, _from, State) -> {reply, State, [Msg | State]}.
It is FP for now. The state of the actor is one of the input, so it always give the same result while the inputs are same.
But when you call the methods from the outside, it is not so true. For example:
{ok, Pid} = gen_server:start_link(example, [], []),
%% A and B will not be the same here
A = gen_server:call(Pid, 1)
B = gen_server:call(Pid, 2)
See? In this example, the two calls of gen_server:call
with the same inputs will not give the same outputs. It is because the actors do have state. So I think it is more suitable to think it in an OOP way.
It Is Complex To Define A Good Actor In Erlang
After I write Erlang programs about half a year, I realized that it puts too many works to the programmer which could be done by the compiler or library. For example, in order to define a module with gen_server
, we need to do these things:
- Define a module which behaviour is
gen_server
, and define the callbacks, and an APIstart_link
which will be invoked by the supervisor. - Define a module which behaviour is
supervisor
to supervise these actors. - Define a module with the API, which will invoke
gen_server:call
orgen_server:cast
to sent messages to thegen_server
that is just defined.
Elixir reduce the complex of Erlang’s syntax a lot. But it doesn’t reduce the steps above. I will write a library that do these things for the programmer, in an OOP way.
My Implementation
Things should be done just like this:
#this defines a `gen_server`
defmodule Basic do
use Eroop
init _(init_count) do
# "@" means the state in actor
@counter = init_count
end
async add(num) do
@counter = @counter + num
end
sync get do
@counter
end
end
# this starts a `gen_server`
c = Basic.new 2
# this will be executed asynced
c.add 1
# this will be exeuted synced and get its result
count = c.get
I think I don’t need to explain much about it if you are familiar with Erlang, actor model or Elixir. Thanks for the powerful Elixir and its macro, I’ve implemented it in a clean way. (View the source code on Github).
The Problems
But there are also some problems here. The first one is: I would except the supervisor to work.
For example, I’d like this code to work:
defmodule Crash do
use Eroop
init _(init_count), do: @counter = init_count
async crash, do: 0/0
sync get, do: @counter
end
Crash.start_sup
c = Crash.new 1
# Thought it is crashed, the supervisor should restart it and assign its new pid to `c`
c.crash
count = c.get
The fact is, the supervisor is able to restart it, but the variable c
lost the pid of the new started server, so c.get
will not success. There are some ways to fix it, do a little hack and register c
as the gen_server
’s name is one of them.
But the code will be complex, and it pushes me to think:
- Erlang is not an OOP platform.
- I still think OOP suits actor model better than FP.
You must want to remind me about Scala. But its actor syntax is as worse as Erlang’s. And it is not as easy to extend its syntax as Elixir.
Next Plan
So, what’s my choice? My choice is stop thinking about actor model for a moment and start to learn some Haskell. I’ve heard Haskell solves concurrency problems well and is more clean to build big applications with pure function. I want to have a look at it. So learn Haskell is my next plan. You can wait to see my posts about it!