Elixir

It’s not high on any popularity list, but you’ll really like it.

What and Why and Who

Elixir, a cool programming language:

Do you need to learn Erlang first to learn Elixir?

Nah, you got this. But if you already know Erlang, you might find this Elixir crash course for Erlang developers useful to get started. In either case, it’s important to know that Elixir programs can always, and sometimes have to, invoke functions from Erlang libraries. Elixir did not set out to rewrite the entire Erlang ecosystem; rather, it set out to modernize Erlang the language.

Jeff’s intro:

Getting Started

The Hello World program in Elixir is:

hello.exs
IO.puts "Hello, world"

You can run Elixir scripts on repl.it or TIO.

You can also install Elixir on your own machine. This will create several executables: (1) iex, an interactive shell, (2) elixir, to run scripts, (3) elixirc, to compile files, and (4) mix, the build tool.

Run your script now:

$ elixir hello.exs
Hello, world
Filename extensions

Use the extension .exs for scripts (files you intend to run as programs), and .ex for modules (containing objects and services you export to other modules and scripts).

Now, check out this little script:

triple.exs
Enum.each 1..40, fn c -> 
  Enum.each 1..c, fn b -> 
    Enum.each 1..b, fn a -> 
      if a * a + b * b == c * c do
        IO.puts "#{a}, #{b}, #{c}"
      end
    end
  end
end
$ elixir triple.exs
3, 4, 5
6, 8, 10
5, 12, 13
9, 12, 15
8, 15, 17
12, 16, 20
15, 20, 25
7, 24, 25
10, 24, 26
20, 21, 29
18, 24, 30
16, 30, 34
21, 28, 35
12, 35, 37
15, 36, 39
24, 32, 40

Here’s on more script, this time with a command line argument:

collatz.exs
defmodule Collatz do
  def steps(n, count \\ 0) do
    cond do
      n == 1 -> count
      rem(n, 2) == 0 -> steps div(n, 2), count + 1
      true -> steps 3 * n + 1, count + 1
    end
  end
end

if length(System.argv) != 1 do
  IO.puts "Exactly one command line argument required"
else
  case System.argv |> hd |> Integer.parse do
    :error -> IO.puts "Integer command line required"
    {n, _} -> IO.puts Collatz.steps(n)
  end
end

Neat, huh?

Okay, time to go deeper.

The Basics

Let’s illustrate a few things with IEx:

$ iex
Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
Interactive Elixir (1.11.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 9 * (3 - 2) < 89
true
iex(2)> 3 <= 5 and 8 > 13 or not true
false
iex(3)> "dog" <> "house"
"doghouse"
iex(4)> [h | t] = [1, 2, 3, 4]
[1, 2, 3, 4]
iex(5)> h
1
iex(6)> t
[2, 3, 4]
iex(7)> [x, y, z] = [10, 20, 30]
[10, 20, 30]
iex(8)> y * z
600
iex(9)> first..last = 2..89
2..89
iex(10)> last
89
iex(11)> colors = %{red: "rojo", green: "verde", blue: "azul"}
%{blue: "azul", green: "verde", red: "rojo"}
iex(12)> colors[:green]
"verde"
iex(13)> colors.green
"verde"

Types

These are the basic types (as far as I know, anyway, there may be more):

TypeDescriptionExamples
IntegerWhole number. Unbounded, yay. 3
9853212977530319128888123123
0x3ffa
0b01101001
FloatFloating-point number. 3.15
27.88E-200
AtomA (named) symbol. Starts with a colon. :ok
:blue
:empty
:true
ListA sequence of values stored as a linked list in memory. [1, :ok, "hello"]
[]
[[1, 2], [3, 4]]
TupleA sequence of values stored contiguously in memory. {1, :ok, "hello"}
{}
{:rectangle 30.0 22.95}
BitStringA sequence of bits stored contiguously in memory. <<7::3, 89::24, 1::8>>
<<97, 98, 99>>
"Hello 🙌🧇🦊🥑"
MapKey-value pairs. Note the shorthand syntax when the keys are atoms. %{:a => 1, 2 => :b}
%‍{"z" => true, 1 => 0, 0 => 1}
%‍{:red => "rojo", :blue => "azul"}
%{red: "rojo", blue: "azul"}
FunctionA function. fn x -> not x end
fn -> 0 end
fn x, y -> 2 * x + y end
fn f, x -> f.(f.(x)) end
&"Good #{&1}, #{&2}"
&(&1 * 2 + &2)
&String.reverse/1
PIDIdentifier for an Elixir process. The pid of the currently executing process is obtained by calling self/0. self
spawn(fn -> 0 end)
PortA thing you use to communicate with external processes. Port.open({:spawn, "ls"}, [])
ReferenceA unique identifier, generated on demand with make_ref/0.make_ref

In Elixir, types are not objects, and in fact, you can’t cleanly “get” the type of an expression; instead, you have functions that return whether or not an expression has a type:

type_checks.exs
# Checks on the basic types
true = is_integer 8919821982888885323298
false = is_integer 5.0
true = is_float 2.0
true = is_atom :ok
true = is_atom :false
true = is_atom false
true = false == :false
true = is_atom nil
true = is_tuple {"Hi", 3, :true}
true = is_list ["Hi", 3, :true]
true = is_function(fn x -> x * x end)
true = is_map %{x: 3, y: 5}
true = is_bitstring <<7::3, 199, 8000::31>>
true = is_bitstring <<420>>
true = is_bitstring "Elixir is cool 😎💃🏽😍"
true = is_pid self()
true = is_reference make_ref()

# Other checks (numnber, boolean, nil, binary)
true = is_number(9.8)
true = is_number(5)
true = is_boolean(true)
true = is_boolean(false)
true = is_boolean(:false)
true = is_nil(nil)
false = is_binary <<7::3, 199, 8000::31>>
true = is_binary <<420>>
true = is_binary "Elixir is cool 😎💃🏽😍"

IO.puts("Everything checks out!")
Things that are not exactly types
booleanThe atoms :true and :false. Elixir binds the names true and false to these, respectively, as a convenience.
nilThe atom :nil. Elixir binds the name nil to this as a convenience.
numberAll the integers and floats.
binaryBitstrings in which the number of bits is divisible by 8.
These all have built-in test functions (is_boolean, is_nil, is_number, and is_binary) functions, but the actual datatype for their values is something else.

Technically, there is a hack to get the type, though: if you are using IEx, you can use the inspect function i to get a ton of useful information about a value, including its type!

iex(1)> i "Happy 🎃 Day, いずみ"
Term
  "Happy 🎃 Day, いずみ"
Data type
  BitString
Byte size
  25
Description
  This is a string: a UTF-8 encoded binary. It's printed 
  surrounded by "double quotes" because all UTF-8 encoded 
  code points in it are printable.
Raw representation
  <<72, 97, 112, 112, 121, 32, 240, 159, 142, 131, 32, 
  68, 97, 121, 44, 32, 227, 129, 132, 227, 129, 154, 
  227, 129, 191>>
Reference modules
  String, :binary
Implemented protocols
  Collectable, IEx.Info, Inspect, List.Chars, String.Chars
Exercise: Try out the i function in IEx on a number of different values, with the intent about learning more about Elixir types. Try it on each of the examples in the table above.

Numbers

Elixir’s integer type is unbounded, and the float type is probably IEEE binary-64, but the docs don’t seem to require this. In fact, Elixir does not seem to do Infinity and NaN like other languages!

is_float 8

Atoms

Atoms are just atoms, or just symbols, they’re just what they are. So the atom :dog is just the atom :dog. It is not text. It is not a string. It is just :dog.

The atoms :true, :false, and :nil are so special you can write them true, false, and nil. The first two are called booleans but you already knew that. The atom nil is used to represent that something is missing or unknown.

...

Text

Most languages need to manipulate text. Do we need a string type? Not really, the reasons for it aren’t that great. In fact, strings and text are not the same. And in most languages that have string types, their string type is badly broken. Few languages, if any, get text right. Swift and Elixir do text pretty well.

In Elixir, a string is just a binary that is a valid UTF-8 sequence.

    

TODO

Structs and Protocols

You make your own types with structs and protocols:

TODO

There are more types in the Elixir Standard Library (some of which you’ll notice have a neat syntax):

TODO

Functions

Functions come in two flavors: (1) named functions, defined with def or defp inside a module, and (2) anonymous functions of the form fn params -> expression (or a short form). You can call the former with or without parenthesizing the arguments; the latter requires a dot plus parentheses for its arguments. If there are no arguments, you just invoke the function via its name (or the variable it has been assigned to). To refer to the function without calling it, use the & prefix.


Modules

Pattern Matching

Comprehensions

Processes

Macros

The Standard Library

Docs

Using Erlang Modules

Mix

Where to Learn More

Like what you see here? Want to learn more? Want to get really good at this cool language? Check out:

Summary

We’ve covered:

  • ...