jonnifyelixir
F3 / F3.05 / matching

F3.05 · part 3 of 3

Matching on a struct's type

Because a struct carries its module in the __struct__ key, a pattern can match on the type itself. %User{} = value succeeds only when the value is a User, and a function can have one clause per struct — the runtime picks the clause whose tag fits.

The portal passes many shapes around: a %User{}, a %Session{}, sometimes a plain map from outside. A single describe/1 dispatches on which one arrives. Send a value through and watch the clause light up.

Dispatch by the tag

Three clauses, in order: one for %User{}, one for %Session{}, and a final one for any plain map. Choose an incoming value and see which clause matches and why.

The incoming value · select one

INCOMING VALUE %Portal.Accounts.User{email: "ada@portal.dev"} __struct__ TAG Portal.Accounts.User CLAUSES · FIRST MATCH WINS describe(%User{} = u) match describe(%Session{} = s) match describe(%{} = m) when not is_struct(m) match

      

The tag turns a value's type into something a pattern can read. One function dispatches across shapes with no conditionals — the clause whose struct matches is the one that runs.

Clause order and the guard

Order matters because a struct is a map: the %{} clause would match a %User{} too. Putting the struct clauses first, and guarding the map clause with not is_struct(m), keeps each value with the clause meant for it.

def describe(%User{email: e}), do: "user #{e}"
def describe(%Session{id: id}), do: "session #{id}"
def describe(%{} = m) when not is_struct(m), do: "plain map of #{map_size(m)} keys"

# is_struct/2 asks the tag directly
is_struct(user)         # => true
is_struct(user, User)   # => true
is_struct(%{a: 1})       # => false

The idea

Choose the right behaviour by looking at a value's type — dispatch by what the thing is.

In Elixir

%Struct{} patterns and is_struct/2 dispatch on the tag — and that tag is what protocols use for polymorphism.

That is the whole of structs: a named, checked shape over a map, with a tag you can match on. Next module: F3.06 — Protocols & behaviours, where that tag drives polymorphism across types.

References

Primary sources for this lesson, and where it connects in the course.

Sources

Related in this course