Erlang basics · Lesson 23 (open contents)
01 · Open erl02 · Meet common terms03 · Tell two kinds of text apart04 · Bind a variable once05 · Split and filter a list06 · Parse a number safely07 · Choose one path with case08 · Let a function keep going09 · Put code in a module10 · Compare terms and Boolean results11 · Read and update nested maps12 · Build and inspect UTF-8 binaries13 · Let clauses choose the function path14 · Solve a list twice15 · Choose with patterns, guards, and errors16 · Describe a module and one record17 · Put one tested module in Rebar318 · Return success or failure as data19 · Read and write one file safely20 · Build a data pipeline with functions21 · Keep records behind a module boundary22 · Define a module contract23 · Describe the data contract24 · Test small rules and whole flows25 · Shape builds and make a command26 · Write the project promise27 · Parse one real line28 · Keep the public API small29 · Make the promise executable30 · Package the edge and leave clues31 · Prove the project is done
ERLANG · INTERMEDIATE · LESSON 2325 minutes

Describe the data contract

Add named types and function specs, then use Dialyzer to inspect suspicious paths.

01 · Start with the whole example

Run this first

Run it once, change one input, and compare the new result.

Erlang
%% Let tools and readers see both result branches
-type result(T) :: {ok, T} | {error, not_positive}.
-spec check(integer()) -> result(integer()).
check(N) when N > 0 -> {ok, N};
check(_N) -> {error, not_positive}.
Check the result first
  • check(3) returns {ok,3} and check(0) returns {error,not_positive}.
02 · Take the code apart

Read from the first line down

  1. Read -type: Name a reusable term shape.
  2. Read -spec: Declare a function contract.
  3. Read ::: Separate a name or function from its type.
03 · Meet the new symbols

Symbols are not secret signs

-type

Name a reusable term shape.

-spec

Declare a function contract.

::

Separate a name or function from its type.

04 · Ideas inside the code

Match each name to its meaning

01

type

A named type gives a readable label to an Erlang term shape.

02

spec

A function spec documents accepted argument types and possible return values.

03

Dialyzer

Dialyzer compares inferred success types with specs to find some inconsistent paths.

05 · Make the idea clear

Why these forms are useful

Run the example first. Predict one result, then change one input and run it again.

A named type gives a readable label to an Erlang term shape.

A function spec documents accepted argument types and possible return values.

DESIGN DECISION

Why this shape?

Why write specs if the code can run without them?

Choice

A spec leaves a function contract for people and Dialyzer. It is good at finding calls that cannot succeed; it does not guard every runtime value.

Cost and boundary

A very broad spec says little, and an incorrect spec does not become runtime validation. Parse and validate data at system boundaries.

A FAMILIAR POINT OF VIEW

Coming from Java, Python, or JavaScript

Java

Familiar starting point
The compiler performs static type checks during a build.
What BEAM changes
Dialyzer uses success typing; its goal is not to prove that every input is safe.
False friend
A clean Dialyzer run cannot prevent a bad runtime message.

Python

Familiar starting point
Type hints checked by mypy or pyright.
What BEAM changes
Specs also serve tools and readers, but the analysis model differs.
False friend
A spec is not an input-validation library.

JavaScript

Familiar starting point
TypeScript checks types before emitting JavaScript.
What BEAM changes
BEAM still runs terms; Dialyzer looks for contradictions among calls that can succeed.
False friend
Files and network boundaries still need real validation.
06 · Change it yourself

Close the answer and try

Write a spec for a binary length function.

Practice starting point
-spec length_of(____) -> non_neg_integer().

Target result: Accept binary().

Stuck? Read one hint

Use Erlang's built-in binary type.

After you run it, see one answer
One answer
-spec length_of(binary()) -> non_neg_integer().
Think about it: Does a spec check values at runtime?

No. Specs support documentation and static analysis; runtime validation still needs code.

Take with you

Remember these three lines

  1. 1

    Name repeated shapes.

  2. 2

    Spec public functions.

  3. 3

    Treat Dialyzer as analysis, not a proof or runtime validator.

Lesson completeRun the code once, then mark this lesson as done.