DmitriyChernyavskiy

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

kokolegorille

Yes I can.

This video (and all videos made by Scott Wlaschin)

I cannot tell You how much impact they had on me :slight_smile:

Exadra37

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 :slight_smile:

al2o3cr

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 @impl doesn’t appear anywhere in the posted code
  • I think you’re looking for defoverridable which sounds very OO, but is slightly different
kokolegorille

kokolegorille

… and techniques, which does not suit FP.

You don’t use inheritance, but composition in FP.

Laetitia

Laetitia

Very good video!
Thanks for sharing

Where Next?

Popular in Challenges Top

ehayun
I have 2 arrays: a1 can be any combination of value or nil like that a1 = [1,nil,3] and array 2 the same a2 = [4,2, nil] How do I com...
New
LostKobrakai
This one has been quite the ride. Struggled at first to find a good data format to suite the problem. I really like how that turned out b...
New
QuinnWilton
Note: This topic is to talk about Day 7 of the Advent of Code 2019 . There is a private leaderboard for elixirforum members. You can joi...
New
kwando
Phew, this one took a while to get right. My naive attempts was way to slow so I reached for Dijkstras shortest path algorithm… and that ...
New
lud
At first I was scared but I found is a simple way to compute the sides. defmodule AdventOfCode.Solutions.Y24.Day12 do alias AdventOfCo...
New
Aetherus
This topic is about Day 5 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
igorb
I found today a bit tedious: advent-of-code-2024/lib/advent_of_code2024/day15.ex at main · ibarakaiev/advent-of-code-2024 · GitHub.
New
groovyda
Today’s challenge for me was about using reduce: defmodule Prob5 do def move([[h1 | rest] = _list1, list2]) do [rest, [h1 | list2]...
New
connorlay
Note by the Moderators: This topic is for general discussion about the Advent of Code 2018. To prevent people from being spoiled about s...
New
bjorng
Note: This topic is to talk about Day 4 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can join...
New

Other popular topics Top

stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

We're in Beta

About us Mission Statement