Elixir is a dynamic, functional language designed for building scalable and maintainable applications. Elixir leverages the Erlang VM, known for running low-latency, distributed and fault-tolerant systems, while also being successfully used in web development and the embedded software domain.


Currently this is intended just to be collection of notes as I attempt to learn elixir. More structure will be added as time goes by.

Following on from some elixir training on Udemy here is the code from a small project to create a identicon (similar to those created by github).

There are lots of good learning resources across the web Here are some that I constantly refer to :-.


I as learning exercise I have created a version of Top5 (a simple task manager) that I originally developed using Pharo (Smalltalk).

This version of Top5 uses the Phoenix web framework..


Ecto is the database wrapper and query generator for Elixir. It provides a standardised API and a set of abstractions for talking to all the different kinds of databases, so that Elixir developers can query whatever database they’re using by employing similar constructs.

A getting started guide can be found here.


Much more detail is given in the Ecto guides but a changeset allows developers to filter, cast, and validate changes before they are applied to some persistence store.

An example of working with a changeset can be seen here based on a User schema with the Accounts context (assumes that within iex - alias Top5.Accounts.User has been run.) :-

  __meta__: #Ecto.Schema.Metadata<:built, "users">,
  email: nil,
  id: nil,
  inserted_at: nil,
  password: nil,
  updated_at: nil,
  username: nil

The changeset indicates that the data supplied is invalid (false) as all fields are currently blank.

User.changeset(%User{}, %{})
  action: nil,
  changes: %{},
  errors: [
    username: {"can't be blank", [validation: :required]},
    email: {"can't be blank", [validation: :required]},
    password: {"can't be blank", [validation: :required]}
  data: #Top5.Accounts.User<>,
  valid?: false                    

The changeset indicates that the data supplied is still invalid (false) but the username field has been supplied.

User.changeset(%User{}, %{:username => "Richard"})
  action: nil,
  changes: %{username: "Richard"},
  errors: [
    email: {"can't be blank", [validation: :required]},
    password: {"can't be blank", [validation: :required]}
  data: #Top5.Accounts.User<>,
  valid?: false                    


The module Ecto.Query provides the query DSL (Domain Specific Language) for a Repo so that it become possible to retrieve and manipulate data.

Here are some examples run within iex (the elixir shell). It should be assumed that modules Ecto.Query, the appropriate Repo module etc have already been imported / aliased :-

Perform a count - "select count(*) from tasks;.

Repo.aggregate( ( from t in Task), :count, :id )
[debug] QUERY OK source="tasks" db=4.3ms queue=0.1ms
SELECT count(t0."id") from tasks AS t0 []
Repo.one( from t in Task, select: count(t.id), where: t.user_id ==1 )
[debug] QUERY OK source="tasks" db=4.9ms
SELECT count(t0."id") from tasks AS t0 where (t0."user_id" = 1) []


Erlang Term Storage, commonly referred to as ETS, is a powerful storage engine built into OTP and available to use in Elixir. For more info (beyond these small examples) try - Elixir School.

Create a table - product.

:ets.new(:product, [:set, :protected, :named_table])                

To add data into the product table.

:ets.insert(:product, {"Apple", [0.22]})
:ets.insert(:product, {"Banana", [0.24]})

Using the Basket example (shown below) it is then possible to retrieve this data and add relevant entries to a virtual shopping card.

{:ok, p1} = Basket.start_link([])
Basket.add(p1, "Apple")
Basket.add(p1, "Banana")

List what is currently in the Basket.

  %{added: "2019-09-11 12:07:22.537378Z", price: 0.24, product: "Banana"},
  %{added: "2019-09-11 12:07:15.317793Z", price: 0.22, product: "Apple"}

Remove an item from the Basket.

Basket.remove(p1, %{added: "2019-09-11 12:07:22.537378Z", price: 0.24, product: "Banana"})
[%{added: "2019-09-11 12:07:15.317793Z", price: 0.22, product: "Apple"}]

The code for Basket.ex.

defmodule Basket do
  use GenServer

  # GenServer Client

  def start_link(default) when is_list(default) do
    GenServer.start_link(__MODULE__, default)

  def add(pid, item) do
    GenServer.cast(pid, {:add, item})

  def list(pid) do
    GenServer.call(pid, :list)

  def remove(pid, item) do
    GenServer.cast(pid, {:remove, item})

  # GenServer callbacks

  def init(basket) do
    {:ok, basket}

  def handle_cast({:add, product}, basket) do
    case :ets.lookup(:product, product) do
      [{_, [price] }] -> 
        {:ok, dt} = DateTime.now("Etc/UTC") 
        {:noreply, [%{product: product, price: price, added: DateTime.to_string(dt) } | basket]}
      _ -> 
        {:noreply, basket}

  def handle_cast({:remove, item}, basket) do
    updated_basket = Enum.reject(basket, fn x -> x == item end)
    {:noreply, updated_basket}

  def handle_call(:list, _, basket) do
    {:reply, basket, basket}