Containers
Intro
Lists and tuples & “dicts” (maps) exist but are pretty different from the data structures of Python. They are (truly) immutable, which requires pretty different implementations, and therefore some operations that we are accustomed to in Python are costly and therefore hard to do (because they mostly should be avoided).
Lists
Misc.:
-
Linked list implementation info (low-level) explains pretty well what operation are cheap (constant time) and costly (linear … or more).
-
Elixir tries to make cheap operations easy to do (core features, syntax, etc.) and the others harder.
-
Cheap: prepend an item, get head, get tail (and hence incremental destructuring), that’s it!
-
Linear: concatenate two lists (with the size of the first list), random access, get length, etc.
[1, 2, 3]
[1, "Hello!", [42, 3.15], {7, 99}, nil, true]
[]
l = [2, 3]
[1 | l]
[0, 1] ++ [2, 3]
length([0, 1, 2])
l = [0.1, 0.2, 0.3]
# ⚠️ not idiomatic
Enum.at(l, 1)
[head | tail] = [0, 1, 2]
head
tail
Tuples
Easy:
-
Random access (read and “write”)
-
(Full) destructuring
That’s all!
Hard :
- Extend or restrict a tuple, by design. Effectively fixed-size
{1, 2, 3}
{}
t = {1, 3.14, true, :ok, [1, 4]}
tuple_size(t)
elem(t, 1)
{count, pi, alive, status, _} = t
pi
put_elem(t, 0, count + 1)
t
s = put_elem(t, 0, count + 1)
s
Maps
Start with “simple maps”/“structs” (fixed set of atom keys & syntaxic sugar for literals & field access)
# ⚠️ space needed after colon
map = %{a: 1, b: 2, c: 4}
map.a
map.d
%{map | a: 42}
%{map | d: 66}
# Ahah, here we actually have used atom keys
map[:a]
# desugared
%{:a => 1, :b => 2, :c => 4}
map = %{:a => 1, :b => 2, :c => 4, 4 => :c}
# repr also desugared now
map
%{map | 4 => nil}
Map.get(map, :b)
Map.get(map, :z)
Map.put(map, :a, 76)
Map.put(map, "target", "world")
# (partial) pattern matching works
%{a: a, b: bbb} = map
%{a: a, z: zzz} = map