Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Containers

containers.livemd

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