Some fun programming over the weekend, reading a little bit about Elixir, a very beautiful and succinct language. I hope, Elixir, someday we will meet for an exciting project together.

The Basics

Starting the REPL


Atoms are type in Elixir for which their value as string is also their representation in code. E.g. :test has the string value of test.

All atoms are true except :false and :nil. Operator || will return the first true value. For example, the expression false || :helloworld || true will return :helloworld. Comparing atoms is fast and space efficient. They are similar to comparing enum values in other programming languages.

All strings are UTF encoded. String.length() - length of the string, <> - concatenation operator. String interpolation is built in, with the syntax "Hello #{name}"

Tuples are defined with the syntax t = {:ok, "Hello world"}. Items in a tuple are accessed with elem(t, 0). To change a value in a tuple one can use put_elem(t, 1, "Hello Alexandru"), but this will create a new tuple, as the data is immutable. Elixir supports deconstructing tuples with the syntax {returncode, message} = t.

Lists are composed of heads and tails. l = [1, 2, 3] and then hd(l) will return 1 while tl(l) will return [2, 3]. Deconstructing works with lists as well [h | t] = l.

When counting the elements in a data structure, Elixir also abides by a simple rule: the function is named size if the operation is in constant time (i.e. the value is pre-calculated) or length if the operation is linear (i.e. calculating the length gets slower as the input grows). As a mnemonic, both “length” and “linear” start with “l”. (Elixir Tutorial)

Keyword lists are defined as l = [{:k, :v}] or using the shorthand notation l = [k: :v]. Elements can be accesses as l[:k].

Maps are defined as follows: m = %{:first_key => "this is the value"}. Keys and values can be anything and, if they are atoms, they can be accessed with shortcut notation m.first_key.

Modules are defined as follows:

defmodule ModuleHello do
    def say_hello do
        IO.puts "Hello"

and are reloaded in the REPL with r(ModuleHello).

Functions and pattern matching

defmodule PatternMatching do

    def first([]) do: nill
    def first( [head | _]) do: head


Invoked as PatternMatching.first([]) or PatternMatching.first([1, 2, 3])

An alternative way

defmodule MyCalendar do

    def is_leap_year(yr) when rem(year, 400) do: true
    def is_leap_year(yr) when rem(year, 100) do: false
    def is_leap_year(yr) when rem(year, 4) do: true
    def is_leap_year(yr) do: false


Elixir also supports default parameters using the val \\ default_val syntax, private functions, defined with defp instead of def or discarding parameters which are not used using the _ placeholder.

Function address is taken by using the & operator, for instance when sent as a parameter to another function. Anonymous functions are also supported, e.g.[1, 2, 3, 4], fn(n) -> n * n end). Another shorthand notation is the capture-style anonymous function, specified as Enum.reduce([1, 2, 3, 4], 0, &(&1 + &2)). When submitted as a parameter, the syntax for invoking a function is f.(params).

The cond block:

defmodule MyCalendar do
    def day_abbr(day) do:
        cond do:
            day == :Monday -> "Mon"
            day == :Tuesday -> "Tue
            true -> "Neither Mon nor Tue"

Using pattern matching do execute parts of code:

defmodule MyCalendar do

    def describe_date(date) do:

        case date do:
            {1, _, _} -> "First day of the month!"
            {25, 12, _} -> "Merry Christmas!"
            {25, month, _} -> "Only #{12 - month} months until Christmas"
            {31, 10, _} -> "Happy Halloween!"
            {_, month, _} when month <= 12 -> "Just an average day"
            {_, _, _} -> "Invalid month"

Another useful example of case and pattern matching is handling errors from functions:

def read_file(path) do
    case do:
        {:ok, data} -> process_data(data)
        {:error, err} -> IO.puts "Cannot open file"

To start observing the Erlang VM type iex> :observer.start()

ASCII code of a letter is given by the ? prefix, e.g. ?a.

Pattern matching

<<"alexandru", x::binary>> = "alexandru gris"
"alexandru " <> gris = "alexandru gris"

Two examples

A slightly more involved example of pattern matching, a binary search tree example:

defmodule Tree do

  def insert({:leaf, nil}, nv) do
    {:leaf, nv}

  def insert({:leaf, v}, nv) when v >= nv do
    {:node, v, {:leaf, nv}, {:leaf, nil}}

  def insert({:leaf, v}, nv) when v < nv do
    {:node, v, {:leaf, nil}, {:leaf, nv}}

  def insert({:node, v, left, right}, nv) when v >= nv do
    {:node, v, insert(left, nv), right}

  def insert({:node, v, left, right}, nv) when v < nv do
    {:node, v, left, insert(right, nv)}

  def to_list({:leaf, nil}) do

  def to_list({:leaf, v}) do

  def to_list({:node, v, left, right}) do
    to_list(left) ++ [v] ++ to_list(right)

  def create() do
    {:leaf, nil}


Interesting to note how succinct the code is.

Another one, a small vector class which can be used for maths processing. It uses pattern matching, the pipeline operator and function references to keep the code short.

defmodule Vector do

  def op([h1 | t1], [h2 | t2], f) do
    [f.(h1, h2) | op(t1, t2, f)]

  def op([], [], _) do

  def op([h1 | t1], scalar, f) when is_number(scalar) do
     [f.(h1, scalar) | op(t1, scalar, f)]

  def op([], scalar, _) when is_number(scalar) do

  def add(v1, v2) do
    op(v1, v2, &(&1 + &2))

  def add(v) do
    Enum.reduce(v, &add(&1, &2))

  def subtract(v1, v2) do
    op(v1, v2, &(&1 - &2))

  def negate(v) do
    multiply(v, -1)

  def multiply(v1, v2) do
    op(v1, v2, &(&1 * &2))

  def dot(v1, v2) do
    multiply(v1, v2) |> Enum.sum

  def norm(v) do
    dot(v, v) |> :math.sqrt

  def normalize(v) do
    op(v, norm(v), &(&1 / &2))

  def mean(v) do
    multiply(v, 1 / Enum.sum(v))

  def weighed_mean(v, w) do
    multiply(v, w) |> multiply(1 / dot(v, w))

  def sq_distance(v, w) do
    subtract(v, w) |> (fn x -> dot(x, x) end).()

  def distance(v, w) do
    sq_distance(v, w) |> :math.sqrt