Skip to content

Instantly share code, notes, and snippets.

@michalmuskala
Last active March 29, 2024 21:49
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save michalmuskala/ab8011adaf54dc33e8018e14a983378c to your computer and use it in GitHub Desktop.
Save michalmuskala/ab8011adaf54dc33e8018e14a983378c to your computer and use it in GitHub Desktop.

Erlang/Elixir syntax reference

This reference is aimed at allowing you to comfortably read erlang documentation and consume terms printed in Erlang format. It does not aim at allowing you to write Erlang code.

This is a modified version of http://elixir-lang.org/crash-course.html

Data types

Erlang and Elixir have the same data types for the most part, but there are a number of differences.

Atoms

In Erlang, an atom is any identifier that starts with a lowercase letter, e.g. ok, tuple, donut. Identifiers that start with a capital letter are always treated as variable names. Elixir, on the other hand, uses the former for naming variables, and the latter are treated as atom aliases. Atoms in Elixir always start with a colon :.

Erlang

im_an_atom.
me_too.

Im_a_var.
X = 10.

Elixir

:im_an_atom
:me_too

im_a_var
x = 10

Module  # this is called an atom alias; it expands to :'Elixir.Module'

It is also possible to create atoms that otherwise wouldn't be allowed (for example with a space, or starting with a number or an uppercase character). The syntax is different between the two languages:

Erlang

is_atom(ok).                %=> true
is_atom('0_ok').            %=> true
is_atom('Multiple words').  %=> true
is_atom('').                %=> true
is_atom('_').               %=> true

Elixir

is_atom :ok                 #=> true
is_atom :'ok'               #=> true
is_atom Ok                  #=> true
is_atom :"Multiple words"   #=> true
is_atom :""                 #=> true
is_atom :_                  #=> true

Module names

Modules are atoms. Elixir uses additional syntactic sugar to distinguish regular module names from other atoms:

iex> :"Elixir.Foo.Bar" == Foo.Bar

Whenever you see reference to module lists in Erlang, it will be module :lists in Elixir.

To write Elixir style module name in Erlang, you need to use the escaping technique described above - so 'Elixir.Foo.Bar' in Erlang is equivalent to :"Elixir.Foo.Bar" and Foo.Bar in Elixir.

Tuples

The syntax for tuples is the same in both languages, but the APIs are different. Elixir attempts to normalize Erlang libraries in a way that:

  1. The subject of the function is always the first argument.
  2. All data structures functions employ zero-based access.

That said, Elixir does not import the default element and setelement functions, but instead provides elem and put_elem:

Erlang

element(1, {a, b, c}).       %=> a
setelement(1, {a, b, c}, d). %=> {d, b, c}

Elixir

elem({:a, :b, :c}, 0)         #=> :a
put_elem({:a, :b, :c}, 0, :d) #=> {:d, :b, :c}

Lists and binaries

Elixir has a shortcut syntax for binaries:

Erlang

is_atom('Hello').        %=> true
is_list("Hello").        %=> true
is_binary(<<"Hello">>).  %=> true

Elixir

is_atom :"Hello"         #=> true
is_list 'Hello'          #=> true
is_binary "Hello"        #=> true
is_binary <<"Hello">>    #=> true
<<"Hello">> === "Hello"  #=> true

In Elixir, the word string means a UTF-8 binary and there is a String module that works on such data. Elixir also expects your source files to be UTF-8 encoded. On the other hand, string in Erlang refers to charlists and there is a :string module, that's not UTF-8 aware and works mostly with what we call in Elixir charlists.

This means "a string" in Erlang, will be 'a string' in Elixir and <<"a binary">> in Erlang will be "a binary" in Elixir.

Binary matching

Both Elixir and Erlang allow for binary matching syntax. For a full reference refer to Elixir docs or Erlang docs accordingly. Here we're going to cover the differences:

Erlang

<<1, 2, 3>>
<<X:N,T/binary>>
<<X:4/little-signed-integer-unit:8>>

Elixir

<<1, 2, 3>>
<<x::size(n), t::binary>>
<<x::n, t::binary>>
<<x::little-signed-integer-size(4)-unit(8)>>
<<x::little-signed-integer-4*8>>

Keyword list

Elixir offers a literal syntax for creating a list of two-item tuples where the first item in the tuple is an atom and calls them keyword lists:

Erlang

Proplist = [{another_key, 20}, {key, 10}].
proplists:get_value(another_key, Proplist).
%=> 20

Elixir

kw = [another_key: 20, key: 10]
kw[:another_key]
#=> 20

Maps

Maps are a key-value store, with no ordering. Keys and values can be any term. Creating, updating and matching maps in both languages is shown below:

Erlang

Map = #{key => 0}.
Updated = Map#{key := 1}.
#{key := Value} = Updated.
Value =:= 1.
%=> true

Elixir

map = %{:key => 0}
map = %{map | :key => 1}
%{:key => value} = map
value === 1
#=> true

If the keys are all atoms, Elixir allows developers to use key: 0 for defining the map as well as using .key for accessing fields:

map = %{key: 0}
map = %{map | key: 1}
map.key === 1

Regular expressions

Elixir supports a literal syntax for regular expressions. Such syntax allows regexes to be compiled at compilation time instead of runtime and does not require you to double escape special regex characters:

Erlang

{ ok, Pattern } = re:compile("abc\\s").
re:run("abc ", Pattern).
%=> { match, ["abc "] }

Elixir

Regex.run ~r/abc\s/, "abc "
#=> ["abc "]

Notable differences

Operators

Some operators are spelled differently. Some are not available in one of the languages and some erlang operators are functions in Elixir.

Erlang Elixir Meaning
and NOT AVAILABLE Logical 'and', evaluates both arguments
andalso and Logical 'and', short-circuits
or NOT AVAILABLE Logical 'or', evaluates both arguments
orelse or Logical 'or', short-circuits
NOT AVAILABLE && Logical 'and' using Elixir's notion of thruthiness
NOT AVAILABLE || Logical 'or' using Elixir's notion of thruthiness
=:= === A match operator
=/= !== A negative match
/= != Not equals
=< <= Less than or equals
rem rem/2 function remainder
div div/2 function integer division

Variable names

Variables in Erlang can only be assigned once. The Erlang shell provides a special command f that allows you to erase the binding of a variable or all variables at once.

Elixir allows you to assign to a variable more than once. If you want to match against the value of a previously assigned variable, you should use ^:

Erlang

Eshell V5.9  (abort with ^G)
1> X = 10.
10
2> X = X + 1.
** exception error: no match of right hand side value 11
3> X1 = X + 1.
11
4> f(X).
ok
5> X = X1 * X1.
121
6> f().
ok
7> X.
* 1: variable 'X' is unbound
8> X1.
* 1: variable 'X1' is unbound

Elixir

iex> a = 1
1
iex> a = 2
2
iex> ^a = 3
** (MatchError) no match of right hand side value: 3

Calling functions

Invoking a function from a module uses different syntax. In Erlang, you would write

lists:last([1, 2]).

to invoke the last function from the :lists module. In Elixir, use the dot . in place of the colon :.

:lists.last([1, 2])

All of the Erlang built-ins reside in the :erlang module.

Modules

Each Erlang module lives in its own file which has the following structure:

-module(hello_module).
-export([some_fun/0, some_fun/1]).

% A "Hello world" function
some_fun() ->
  io:format('~s~n', ['Hello world!']).

% This one works only with lists
some_fun(List) when is_list(List) ->
  io:format('~s~n', List).

% Non-exported functions are private
priv() ->
  secret_info.

Here we create a module named hello_module. In it we define three functions, the first two are made available for other modules to call via the export directive at the top. It contains a list of functions, each of which is written in the format <function name>/<arity>. Arity stands for the number of arguments.

An Elixir equivalent to the Erlang above:

defmodule HelloModule do
  # A "Hello world" function
  def some_fun do
    IO.puts "Hello world!"
  end

  # This one works only with lists
  def some_fun(list) when is_list(list) do
    IO.inspect list
  end

  # A private function
  defp priv do
    :secret_info
  end
end

In Elixir, it is also possible to have multiple modules in one file, as well as nested modules:

defmodule HelloModule do
  defmodule Utils do
    def util do
      IO.puts "Utilize"
    end

    defp priv do
      :cant_touch_this
    end
  end

  def dummy do
    :ok
  end
end

defmodule ByeModule do
end

HelloModule.dummy
#=> :ok

HelloModule.Utils.util
#=> "Utilize"

HelloModule.Utils.priv
#=> ** (UndefinedFunctionError) undefined function: HelloModule.Utils.priv/0

Further reading

Erlang's official documentation site has a nice collection of programming examples. It can be a good exercise to translate them into Elixir. Erlang cookbook offers even more useful code examples.

You can refer to the full Erlang crash course for additional differences in building modules, calling functions, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment