jonnifyelixir
F3 / F3.03 / functions

F3.03 · part 1 of 3

Defining functions

A named function is written with def, and it can have several clauses — alternative bodies chosen by matching the arguments, refined by guards like when s >= 80. The first clause whose head matches and whose guard holds is the one that runs. The same logic can be written as an anonymous function with fn, or captured as a value with &.

Grading a single score, 82, three ways: as a named function, as an anonymous one, and as a captured value handed to another function.

One function, many clauses

The grade for a score is decided by three guarded clauses, tried top to bottom. For 82 the first guard (>= 90) fails, the second (>= 80) holds, so the answer is :b. Select how the function is written — the dispatch is the same.

The form · select one

82 Portal.grade(82) score ≥ 90 → :a score ≥ 80 → :b true → :c

      

form: named function · def/1

result: :b

Clauses plus guards are how Elixir branches: instead of an if inside one body, you write several heads and let matching choose. The named, anonymous, and captured forms all dispatch the same way — they differ only in how you name and pass the function.

Arity, privacy, and captures

A function is identified by name and arity: grade/1 and grade/2 are two different functions, which is how default arguments work — grade(s, pass \\ @pass) quietly generates both. def exports a function; defp keeps it private to the module, for helpers no caller should reach. And the capture operator & turns a function into a value: &Portal.grade/1 is the function itself, while &(&1 >= 80) is shorthand for fn x -> x >= 80 end.

# name + arity identify a function; defaults generate both arities
def grade(score, pass \\ @pass) when score >= pass, do: :pass
def grade(_score, _pass), do: :fail

defp clamp(s), do: max(0, min(100, s))   # private helper

# capture a named function, or write a tiny anonymous one with &
Enum.map([80, 92, 74], &Portal.grade/1)   # => [:b, :a, :c]
Enum.filter([80, 92, 74], &(&1 >= 80))    # => [80, 92]

F3.02 · a clause head is a pattern

Matching binds the arguments; a guard adds a condition on them.

F3.03.1 · functions are clauses

Each def clause matches its arguments; the first that matches runs.

Next: organising with modules — the namespace these functions live in, and the directives that wire modules together.

References

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