small medium large xlarge

Dave_gnome_head_isolated_pragsmall
15 Jul 2013, 03:42
Dave Thomas (366 posts)
  • Add the API to your stack module (the functions that wrap the gen_server calls).
Generic-user-small
16 Jan 2015, 18:57
Pierre Sugar (57 posts)
defmodule Stack.Stack do
  use GenServer

# API

  def start_link(current_stack) do
    GenServer.start_link(__MODULE__, current_stack, name: __MODULE__)
  end

  def pop do
    GenServer.call(__MODULE__, :pop)
  end

  def push(value) do
    GenServer.cast(__MODULE__, { :push, value })
  end

# Implementation

  def handle_call(:pop, _from, current_stack) when current_stack == [] do
    { :reply, :empty, [] }
  end
  def handle_call(:pop, _from, current_stack) do
    [ top | tail ] = current_stack
    { :reply, top, tail }
  end

  def handle_cast({ :push, value }, current_stack) do
    { :noreply, [ value | current_stack ] }
  end
end
U0x0gcxz_pragsmall
05 Apr 2015, 16:32
John Bohn (5 posts)
defmodule Stack.Server do
  use GenServer

  # Client

  def start_link(stack) do
    :gen_server.start_link({:local, :stack}, __MODULE__, stack, [])
  end

  def push(n) do
    :gen_server.cast(:stack, {:push, n})
  end

  def pop do
    :gen_server.call(:stack, :pop)
  end

  # GenServer

  def init(stack)
  when is_list(stack) do
    { :ok, stack }
  end

  def handle_call(:pop, _from, []), do: raise "Empty stack"
  def handle_call(:pop, _from, [head | tail]) do
    { :reply, head, tail }
  end

  def handle_cast({:push, n}, stack) do
    { :noreply, [n | stack] }
  end
end
Generic-user-small
21 May 2017, 12:19
Matthew Fehskens (11 posts)

I love this OTP stuff so far. Very simple handling of messages.

defmodule Stack.Server do
  use GenServer
  
  # Public API
  def start_link, do: start_link([])
  def start_link(stack) do
    GenServer.start_link(__MODULE__, stack, name: :stack)
  end

  def pop do
    GenServer.call(:stack, :pop)
  end

  def push(value) do
    GenServer.cast(:stack, { :push, value })
  end

  # GenServer callbacks
  def handle_call(:pop, _from, []) do
    { :reply, nil, [] }
  end
  def handle_call(:pop, _from, [head | tail]) do
    { :reply, head, tail }
  end

  def handle_cast({ :push, value }, stack) do
    { :noreply, [ value | stack ]}
  end
end
Generic-user-small
10 Apr 2018, 12:28
Matthew Fehskens (11 posts)

In revisiting this, I really like the abstracted (apparently blasphemous) methodology of implementing the server. I split it up, like the Sequence server, into 3 files:

# lib/stack.ex
defmodule Stack do
  @server Stack.Server
  
  def start_link(initial_stack \\ []) do
    GenServer.start_link @server, initial_stack, name: @server
  end
  
  def pop do
    GenServer.call @server, :pop
  end
  
  def push(value) do
    GenServer.cast @server, { :push, value }
  end
end
# lib/stack/server.ex
defmodule Stack.Server do
  use GenServer
  alias Stack.Impl
  
  def init(initial_stack \\ []) do
    { :ok, initial_stack }
  end
  
  def handle_call(:pop, _from, stack) do
    with { popped, new_stack } = Impl.pop(stack) do
      { :reply, popped, new_stack }
    end
  end
  
  def handle_cast({ :push, value }, stack) do
    { :noreply, Impl.push(value, stack) }
  end
end
# lib/stack/impl.ex
defmodule Stack.Impl do
  def pop([]), do: { :undefined, [] }
  def pop([head | tail ]), do: { head, tail }
  
  def push(value, stack \\ []), do: [value | stack]
end

This approach makes the most sense to me because it keeps the business of stack operations relegated to the impl business logic and the server logic in server while providing a straightforward API into the component/application

You must be logged in to comment