Elixir Notes
Structs
- structs are built on top of maps, and they provide compile time checks and ability for default values
- to define a struct we need to make use of the
defstructkeyword
defmodule Pastizz do
defstruct type: "Irkotta", calories: 1000000
end
- only defined fields are allowed to exist in a struct
%Pastizz{oops: :field}
** (KeyError) key :oops not found expanding struct: Pastizz.__struct__/1
- Access with
pastizz.type, update with%{pastizz | type: "Anchovies"} struct/2- silent, ignores invalid keysstruct!/2- strict, raisesKeyErroron invalid keys- Structs are bare maps with a hidden, special
__struct__field, holding the name of the struct
# struct/2 - silently ignores invalid keys
struct(%Pastiz{}, type: "Pizelli", invalid: "field")
#=> %Pastizz{calories: 1000000, type: "Pizelli"}
# struct!/2 - raises on invalid keys
struct!(%Pastizz{}, type: "Pizelli", invalid: "field")
#=> ** (KeyError) key :invalid not found in: %Pastizz{}
# struct!/2 - valid update
struct!(%Pastizz{}, type: "Chicken", calories: 500000)
#=> %Patizz{calories: 500000, type: "Chicken"}
pastizz_irkotta = %Patizz{calories: 2000000, type: "Irkotta"}
iex> pastizz_irkotta.__struct__
Pastizz
- No
EnumerableorAccessprotocols - can’t usepastizz[:type]orEnum
iex> food = %Pastizz{}
%Pastizz{calories: 1000000, type: "Irkotta"}
pastizz[:type]
** (UndefinedFunctionError) function Pastizz.fetch/2 is undefined (User does not implement the Access behaviour)
Pastizz.fetch(%Pastizz{calories: 1000000, type: "Irkotta"}, :type)
iex> Enum.each(john, fn {field, value} -> IO.puts(value) end)
** (Protocol.UndefinedError) protocol Enumerable not implemented for %Pastizz{calories: 1000000, type: "Irkotta"} of type Pastizz (a struct)
- Fields without defaults are
nil; nil-defaulted fields must come before keyword defaults indefstruct
defmodule Pastizz do
defstruct [:pastizzeria, type: "Irkotta", calories: 1000000]
end
@enforce_keysforces keys at creation, not on updates, no value validation
iex> defmodule Pastizz do
...> @enforce_keys [:type]
...> defstruct [:calories, :type]
...> end
%Pastizz{}
** (ArgumentError) the following keys must also be given when building struct Pastizz: [:type]
expanding struct: Pastizz.__struct__/1
Processes
spawn/1- creates isolated process, failures don’t propagate
spawn(fn -> 1 + 2 end)
#PID<0.43.0>
spawn/1 returns a PID (process identifier). The spawned process will execute the given function and exit after the function is done:
iex> pid = spawn(fn -> 1 + 2 end)
#PID<0.44.0>
iex> Process.alive?(pid)
false
- the PID of the current process can be retrieved by calling
self/0:
iex> self()
#PID<0.1.0>
iex> Process.alive?(self())
true
send/2- non-blocking, drops message in recipient’s mailboxreceive/1- pattern matches mailbox, blocks until match (useafterfor timeout)spawn_link/1- links processes, failure in child crashes parent (and vice versa)-
Task.start/1/Task.start_link/1- preferred over spawn, returns{:ok, pid}, better error reports, supervision-tree compatible - Processes are the state primitive - loop recursively, hold state in function args
Process.register(pid, :name)- global name registration for known processes- Prefer abstractions:
Agentfor state,GenServerfor generic servers - don’t hand-rollreceiveloops in production - “Let it crash” - link to supervisors, don’t defensive-catch everything