DmitriyChernyavskiy
How to implement extension of functionality as inheritance in object oriented language
Hello everyone,
I’m a new in elexir and functional language. I’m trying to implement Websocket interraction with server.
On first layer I implement module based on GenServer with functionality like WebSockex and use ninenines/gun as Websocket client. This module name egun, below are the code snippets:
defmodule Egun do
use GenServer
@callback handle_connect(state :: term) :: {:ok, new_state :: term}
@callback handle_message(msg :: term, state :: term) :: {:ok, new_state} | ...
@callback handle_send(msg :: term, state :: term) :: {:ok, new_state} | ...
...
defmacro __using__(_opts) do
quote location: :keep do
@behaviour Egun
@doc false
def handle_connect(state) do
{:ok, state}
end
@doc false
def handle_message(message, _state) do
raise "No handle_message/2 clause in #{__MODULE__} provided for #{inspect(message)}"
end
@doc false
def handle_send(message, state) do
{:reply, {:text, message}, state}
end
defoverridable handle_connect: 1,
handle_message: 2,
handle_send: 2
end
def start_link(conn = %Conn{}) do
GenServer.start_link(__MODULE__, %__MODULE__{
conn: conn,
})
end
def init(state) do
...
send(self(), :ws_connected)
{:ok, state}
end
def send_message(client, message) do
GenServer.call(client, {:ws_syncsend, message})
end
def handle_call({:ws_syncsend, message}, sender, state) do
message = compose_msg(message, state.conn)
...
{:reply, message, state}
end
def handle_info(:ws_connected, state) do
common_handle(:handle_connect, state)
end
def handle_info(
{:gun_ws, _gun_pid, _ws_stream, {:text, message}}, state
) do
case Jason.decode(message, keys: :atoms) do
{:ok, msg} ->
common_handle({:handle_message, msg}, state)
error ->
{:stop, error, state}
end
end
defp common_handle({function, msg}, state) when is_atom(function) do
result = apply(state.module, function, [msg, state])
common_handle_ret(result, {function, msg}, state)
end
defp common_handle(function, state) when is_atom(function) do
result = apply(state.module, function, [state])
common_handle_ret(result, {function, nil}, state)
end
defp common_handle_ret(result, {function, msg}, state) do
case result do
{:ok, new_state} ->
{:noreply, new_state}
{:reply, frame, new_state} ->
:gun.ws_send(new_state.gun_pid, frame_encode(frame))
{:noreply, new_state}
....
end
end
This implementation provides messaging through an open Websocket. Next I need extend functionality egun. And create module session which adds business logic methods:
defmodule Session do
@moduledoc nil
use Egun
def create(client) when is_pid(client) do
tr = Egun.send_message(client, %{
session: "create",
transaction: "yes"
})
GenServer.call(client, {:push_transaction, tr, client})
end
@impl true
def handle_call({:push_transaction, transaction, callback_pid}, _from, state) do
trs = Map.put(state.transactions, transaction, callback_pid)
{:noreply, %{state | transactions: trs}}
end
@impl Egun
def handle_message(message, state) do
IO.puts("Handle message: #{inspect(message)}")
IO.puts("State: #{inspect(state)}")
{:ok, state}
end
end
After compile I get warning message:
warning: got "@impl Egun" for function handle_call/3 but this behaviour does not specify such callback. The known callbacks are:
* Egun.handle_connect/1 (function)
* Egun.handle_message/2 (function)
* Egun.handle_send/2 (function)
How can I modify my modules so that the Session module can implement the behavior of the GenServer and Egan without using an additional GenServer. On object oriented programming language Session can get access to all methods from GenServer and Egan?
Most Liked
kokolegorille
Yes I can.
This video (and all videos made by Scott Wlaschin)
I cannot tell You how much impact they had on me ![]()
Exadra37
For me, the only thing that made the click to switch the brain from OOP to FP was the book:
and the video course:
@pragdave spends a great deal of time in both the book and in the video course to get you into the FP mode of thinking, thus I cannot recommend enough that you chose the book or the video to get you in the mindset of FP ![]()
al2o3cr
Couple things:
- Elixir is not “OO”. Especially at first, it’s important to reorient your brain to the new idioms
- I suspect you’ve
...ed something important, because@impldoesn’t appear anywhere in the posted code - I think you’re looking for
defoverridablewhich sounds very OO, but is slightly different
kokolegorille
… and techniques, which does not suit FP.
You don’t use inheritance, but composition in FP.
Laetitia
Very good video!
Thanks for sharing








