DmitriyChernyavskiy

DmitriyChernyavskiy

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?

Showing Posts 1 to 7

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
DmitriyChernyavskiy

DmitriyChernyavskiy OP

Thanks for your reply. I added parts of the code that I missed. I known elixir not “OO” language. I only use “OO” terminology.

kokolegorille

kokolegorille

… and techniques, which does not suit FP.

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

ycherniavskyi

ycherniavskyi

Maybe you could suggest some good books or posts about migration from OO to FP paradigm :smiling_face:?

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:

Laetitia

Laetitia

Very good video!
Thanks for sharing

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:

— All posts loaded —

Where Next? Top

Trending in Challenges Top

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews