Erlang basics · Lesson 23 (open contents)
Describe the data contract
Add named types and function specs, then use Dialyzer to inspect suspicious paths.
Run this first
Run it once, change one input, and compare the new result.
%% 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(3)returns{ok,3}andcheck(0)returns{error,not_positive}.
Read from the first line down
- Read
-type: Name a reusable term shape. - Read
-spec: Declare a function contract. - Read
::: Separate a name or function from its type.
Symbols are not secret signs
-typeName a reusable term shape.
-specDeclare a function contract.
::Separate a name or function from its type.
Match each name to its meaning
type
A named type gives a readable label to an Erlang term shape.
spec
A function spec documents accepted argument types and possible return values.
Dialyzer
Dialyzer compares inferred success types with specs to find some inconsistent paths.
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.
Why this shape?
Why write specs if the code can run without them?
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.
A very broad spec says little, and an incorrect spec does not become runtime validation. Parse and validate data at system boundaries.
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.
Close the answer and try
Write a spec for a binary length function.
-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
-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.
Remember these three lines
- 1
Name repeated shapes.
- 2
Spec public functions.
- 3
Treat Dialyzer as analysis, not a proof or runtime validator.