Data Types
Numbers
Elixir have 2 types to work with numbers: integers and floats.
Notes: Integers automatically expand memory they take to accomodate values (e.g. from int’s to bigInt’s)
# Integer examples
1
+2
-3
1_000_000_000
# binary notation
0b1010
# octal notation
0o777
# hexadecimal notation
0x1F
12.345
-2.71
3.14e-10
123_456.789_1011e23
Arithmetic operations work as in most programming languages (+, -, *, /, div/2, rem/2)
1 + 2
3 - 4
5 * 6
2 * 1.3
8 / 2
div(8, 2)
rem(8, 3)
Atoms
An atom is a constant whose value is its own name. Atoms must be composed of Unicode characters such as letters, numbers, underscore, and @
. If the keyword has a character that does not belong to the category above, such as spaces, you can wrap it in quotes.
:apple
:a_pple
:apple_2
:localhost@me
:"atom with spaces"
:apple == :orange
# :apple == :apple
# :apple == :Apple
# is_atom(:apple)
Booleans
Booleans are in fact just atoms :true and :false. In Elixir boolean logic there is a concept of truthly values. There is no separare value for nil therefore atom :nil is used. You can type nil for simplicity sake. Boolean operators && and || consider nil to be the same as false and anything else is consifered to be true. Operator ! negates the truthly values.
true
# true == :true
# true === :true
# !true
# ! :true
nil
# ! nil
# ! :whatever
# nil || :something
# true && :something
Chars
Chars are stored as their unicode values (integers).
?a
IO.inspect(?a)
is_integer(?a)
?ł
?a == 97
?a - ?b
String
String are delimited by double quotes, and are encoded in UTF-8. They also support string interpolation, escape sequences. Internally represented by contiguous sequences of bytes known as binaries.
a = :world
"hello #{a}"
IO.puts("hello\nworld")
is_binary("hello")
"hello" <> " " <> "world"
Lists
Elixir uses square brackets to specify a list of values. Values can be of any type, and lists can be concatenated or subtracted using the ++
and --
operators. You can create a list by putting all the values in the brackets or use a recursive definition [head | tail]
where tail
is also a list.
[]
# [1]
# [:asdf, 1, "apple", true]
[1, 2, 3] ++ [4, 5, 6]
# [1, 1, 2, 5, 3, 4] -- [4, 1]
[1, 2, 3]
# [1 | [2, 3]]
# [1, 2, 3] == [1 | [2, 3]]
# [1 | [2 | [3]]]
# [1 | [2 | [3 | []]]]
# built-in functions hd/1 tl/1
hd([:a, :b, :c])
# tl([:a, :b, :c])
Tuples
Group of elements with constant size. Fast access time than lists
tuple = {:ok, "hello"}
# tuple_size({:ok, "hello"})
# elem(tuple, 1)
# put_elem(tuple, 1, "world")
# tuple
Keyword lists
List consisting of 2-item tuples where the first element is an atom and the second element can be any value. Keyword list have 3 special characterisitcs:
- keys must be atoms
- keys are ordered
- keys can be given more then once
{:trim, true}
# [{:trim, true}] == [trim: true]
# [a: 1, b: 2] == [b: 2, a: 1]
# usual list operations are available
# list = [a: 1, b: 2]
# list ++ [c: 3]
Maps
Maps are created using %{}
syntax, classic key-value store.
%{}
# Map.new()
# computer = %{
# :type => :pc,
# "graphics card" => ["GTX", 760],
# {:processor, "intel"} => [{8, :cores}, {:type, :x86}]
# }
# computer[:type]
# computer[{:processor, "intel"}]
# computer["graphics card"]
# computer[:not_exixsting_key]
# Map.get(computer, :not_existing_key)
# Map.get(computer, :not_existing_key, "default_value")
# computer.type
# # computer.not_exixsting_key
# # computer."graphics card"
# laptop = %{computer | :type => :laptop}
# # laptop = %{computer | :sound => {:serround, 5.1}}
# laptop = Map.put(computer, :sound, {:serround, 5.1})
# tablet = %{:type => %{device_type: :tablet, os: :android}}
# tablet.type.os
# tablet[:type].os
# tablet.type[:os]
# tablet[:type][:os]
Structs
Structs are extensions built on top of maps that provide compile-time checks and default values.
defmodule Device do
defstruct device_type: nil,
os: :linux,
processor: nil
end
pc = %Device{
device_type: :pc
}
# pc.os
# tablet = %Device{
# device_type: :tablet,
# processor: {:intel, :x86},
# os: :android
# }