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
Aetherus
This topic is about Day 3 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
sneako
Note by the Moderators: This topic is to talk about the first day of the Advent of Code. For general discussion about the Advent of Code...
New
sukhmeetsd
All in all, from what I understand, it is better not to use GenServer.cast when we want some concurrent operations to happen for sure, be...
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
jkwchui
Monkeys fitted squarely as GenServers in my head. My initial problem was using cast instead of call; I imagine impolite monkeys slinging...
New
Aetherus
This topic is about Day 16 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
bjorng
This topic is about Day 6 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums ): https://adve...
New
mattbaker
I’m having so much fun working on the “Protohackers” challenges, I never got into Advent of Code much but this has been amazing. The chal...
New
igorb
Today is a brute-force day: advent-of-code-2024/lib/advent_of_code2024/day6.ex at main · ibarakaiev/advent-of-code-2024 · GitHub Takes a...
New

Other popular topics Top

chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
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
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement