Powered by AppSignal & Oban Pro

Supervised Stack

exercises/supervised_stack.livemd

Supervised Stack

Mix.install([
  {:jason, "~> 1.4"},
  {:kino, "~> 0.8.0", override: true},
  {:youtube, github: "brooklinjazz/youtube"},
  {:hidden_cell, github: "brooklinjazz/hidden_cell"}
])

Navigation

Return Home Report An Issue

Supervised Stack

Previously we created a Stack GenServer process.

We’ve made a slight modification to the Stack process by adding a start_link/1 function so this named Stack can be started under a Supervisor.

defmodule Stack do
  use GenServer

  def start_link(opts) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  @impl true
  def init(state) do
    {:ok, state}
  end

  @impl true
  def handle_call(:pop, _from, state) do
    [head | tail] = state
    {:reply, head, tail}
  end

  @impl true
  def handle_call({:push, element}, _from, state) do
    new_state = [element | state]
    {:reply, new_state, new_state}
  end
end

We’re able to push and pop elements off of the stack.

{:ok, stack_pid} = GenServer.start_link(Stack, [])

GenServer.call(stack_pid, {:push, 1}) |> IO.inspect(label: "push")
GenServer.call(stack_pid, {:push, 2}) |> IO.inspect(label: "push")
GenServer.call(stack_pid, {:push, 3}) |> IO.inspect(label: "push")

GenServer.call(stack_pid, :pop) |> IO.inspect(label: "pop")
GenServer.call(stack_pid, :pop) |> IO.inspect(label: "pop")
GenServer.call(stack_pid, :pop) |> IO.inspect(label: "pop")

# the final state is empty.
:sys.get_state(stack_pid)

However, there’s a bug. If we try to pop/1 an item off of an empty stack, the process will crash due to a function clause error because the handle_call/2 function expects a list with one or more elements.

Uncomment the following code to watch the Stack crash.

# {:ok, pid} = GenServer.start_link(Stack, [])
# GenServer.call(stack_pid, :pop)

Normally, we could handle this by using control flow. For example, we could make another handle_call/3 function clause for when the stack is empty.

# return `nil` when the stack is empty.
@impl true
def handle_call(:pop, _from, []) do
  {:reply, nil, []}
end

Instead, you’re going to start the Stack process under a supervisor so that it will be restarted when it crashes. In the Elixir cell below, start the Stack process under a supervisor so that it will restart with an empty stack when it crashes.

Example Solution

children = [
  {Stack, []}
]

{:ok, supervisor_pid} = Supervisor.start_link(children, strategy: :one_for_one)

Keep in mind, if you have already started a supervisor with the Stack process, your livebook may crash. You can resolve this issue by simply re-running the cell below to start the supervisor again.

You should be able to send a :pop message to the Stack process and the Supervisor will restart the Stack process.

Uncomment and evaluate the code below to test your supervisor.

# GenServer.call(Stack, :pop)

Mark As Completed

file_name = Path.basename(Regex.replace(~r/#.+/, __ENV__.file, ""), ".livemd")

save_name =
  case Path.basename(__DIR__) do
    "reading" -> "supervised_stack_reading"
    "exercises" -> "supervised_stack_exercise"
  end

progress_path = __DIR__ <> "/../progress.json"
existing_progress = File.read!(progress_path) |> Jason.decode!()

default = Map.get(existing_progress, save_name, false)

form =
  Kino.Control.form(
    [
      completed: input = Kino.Input.checkbox("Mark As Completed", default: default)
    ],
    report_changes: true
  )

Task.async(fn ->
  for %{data: %{completed: completed}} <- Kino.Control.stream(form) do
    File.write!(
      progress_path,
      Jason.encode!(Map.put(existing_progress, save_name, completed), pretty: true)
    )
  end
end)

form

Commit Your Progress

Run the following in your command line from the curriculum folder to track and save your progress in a Git commit. Ensure that you do not already have undesired or unrelated changes by running git status or by checking the source control tab in Visual Studio Code.

$ git checkout -b supervised-stack-exercise
$ git add .
$ git commit -m "finish supervised stack exercise"
$ git push origin supervised-stack-exercise

Create a pull request from your supervised-stack-exercise branch to your solutions branch. Please do not create a pull request to the DockYard Academy repository as this will spam our PR tracker.

DockYard Academy Students Only:

Notify your teacher by including @BrooklinJazz in your PR description to get feedback. You (or your teacher) may merge your PR into your solutions branch after review.

If you are interested in joining the next academy cohort, sign up here to receive more news when it is available.

Up Next

Previous Next
Supervisors Dominoes