Fault Tolerance Basics

Elixir in Action: Chapter 8

A walk through run-time errors and Supervisors.

Three types of run-time errors:

  • errors: invalid data operation, missing function, unknown pattern match, or raise your own.
  • exits: deliberately terminate a process.
  • throw: to catch up the call stack.

The result of a a try/catch/after block is always from either the try or catch clause, never the after clause.

Processes can be linked and monitored.

Notable Notes and Quotes

[Read More]

Building a Concurrent System

Elixir in Action: Chapter 7

The GenServer handle_call function returns state to the GenServer, which messages it back to the caller. You can also spawn a worker process, return state to the GenServer instructing it not to message back to the caller, then send a message directly from the spawned worker function.

Example provided in the chapter:

def handle_call({:get, key}, caller, state) do
  spawn(fn ->
    data = case File.read(file_name(key)) do
      {:ok, contents} -> :erlang.binary_to_term(contents)
      _ -> nil
    end

    GenServer.reply(caller, data)
  end)

  {:noreply, state}
end

Notable Notes and Quotes

[Read More]

Generic Server Processes

Elixir in Action: Chapter 6

Start by building a simple version of GenServer that supports both synchronous and asynchronous functionality.

A GenServer can maintain a state.

A GenServer has three types of “handles”:

  • handle_call: synchronous message handling
  • handle_cast: asynchronous message handling
  • handle_info: non-GenServer-specific message

The chapter provides a periodic clean-up timer as an example.

On to the GenServer-powered go-do server exercise.

Notable Notes and Quotes

[Read More]

Concurrency Primitives

Elixir in Action: Chapter 5

Though primarily about processes, messages, and servers, another thing I found significant in this chapter is the liberal use of lambda functions. Many places where I would tend to create a new function and then call it are replaced with lambda functions. Need to become more comfortable with this.

Notable Notes and Quotes

[Read More]

Data Abstractions

Elixir in Action: Chapter 4

I found that typing and testing all the small code snippets added to my Elixir “muscle memory” and seeing what standard practices look like. For example, when updating the todo list example, I would not have thought of passing a lambda function for the update vs. just passing new data. The lambda function provides much more flexibility at the cost of some slight complexity increase (at least I’m still seeing it as a complexity increase at this stage).

I need to get more comfortable with updating structures. Spending a lot of time staring at this code before comprehending it.

def delete_entry(todo_list, entry_id) do
    %TodoList{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
  end

CSV Import Exercise

My solution was functionally identical to Saša’s except I tried to do everything inline in a single pipe. Saša broke out the pipelined actions into two functions (read_lines and create_entries). Also used a multi-step anonymous function in a Stream.map. I need to remember that anonymous functions don’t need to be simple one-liners.

Notable Notes and Quotes

[Read More]

Control Flow

Elixir in Action: Chapter 3

Chapter 3 opens with an introduction to pattern matching, with details on tuples, lists, maps, bitstrings, binaries, and of course functions, then delves into function guards to implement extended pattern matching in functions.

Multi-clause anonymous functions were new to me:

test_num =
          fn
            x when is_number(x) and x < 0 -> :negative
            x when x == 0 -> :zero
            x when is_number(x) and x > 0 -> :positive
          end

In Elixir, recursion is efficient. Nothing is pushed on the stack when the last thing a function does is call another function, or itself. This is called tail call optimization.

Notable Notes and Quotes

[Read More]