I would like to propose that we change the base example for GenServer. For
those not familiar with it, here it is:

    defmodule Stack do

      use GenServer



      # Callbacks



      @impl true

      def init(stack) do

        {:ok, stack}

      end



      @impl true

      def handle_call(:pop, _from, [head | tail]) do

        {:reply, head, tail}

      end



      def handle_cast({:push, element}, state) do

        {:noreply, [element | state]}

      end

    end

These are the problems as I teach new Elixir developers OTP.

First, the purpose of init is not clear. It simply returns a stack, and
does not indicate that this is an opportunity to transform the input in
some way:

      def init(stack) do

        {:ok, stack}

      end

As a result, it's not clear what to use init for.

Second, we don't label the state of the GenServer appropriately, and worse,
we do work in the function head, making it harder for users to grasp the
central ideas that GenServers carry state and offer the opportunity to
transform it:

      def handle_call(:pop, _from, [head | tail]) do

        {:reply, head, tail}

      end

As a result, it's too hard for users to understand what this does without
already knowing.

Third, the return tuples are not explicitly labeled, forcing the reader to
work harder to understand what the tuples actually do.

Summarizing the problems:

- The example doesn't use init to transform the initial value to the
genserver state.
- The example doesn't consistently label the last argument of the two
handlers.
- The example does work in the "pop" function head, limiting our
opportunity to label concepts.
- The example doesn't explicitly label the elements of the reply and
noreply tuples.

I would like to propose these changes:

    defmodule Stack do

      use GenServer



      # Callbacks



      @impl true

      def init(string) do

        {:ok, String,split(string, ",", trim: true)}

      end



      @impl true

      def handle_call(:pop, _from, state) do

        [to_client | to_server] = state

        {:reply, head, tail}

      end



      def handle_cast({:push, element}, state) do

        to_server = [element | state]

        {:noreply, to_server}

      end

    end

I don't really care whether we label the state with state or stack. I have
a slight preference for state because we're labeling the genserver concept,
not the domain concept.

Likewise, I don't care whether we use to_server or new_genserver_state on
the server side, or the to_client or to_caller to discuss the results of
the reply and noreply tuples.

I should also point out that we don't handle the error state and it's easy
to do so.

I mainly care that this example communicates with more clarity.

Feedback is welcome.

-bt


Regards,
Bruce Tate
CEO

Groxio, LLC.
512.799.9366
br...@grox.io
grox.io

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/CAFXvW-5%2B3ECDYi76eENLEEfHDCyRfkNjX-A%3DHCKtxqFy%2BCATSQ%40mail.gmail.com.

Reply via email to