Erlang basics · Lesson 18 (open contents)
Return success or failure as data
Use tagged tuples so callers can handle every expected result.
Run this first
Run it once, change one input, and compare the new result.
%% Make both outcomes easy to match
ParseAge = fun(Text) ->
case string:to_integer(Text) of
{Age, []} when Age >= 0 -> {ok, Age};
_ -> {error, invalid_age}
end
end,
{ParseAge("12"), ParseAge("old")}.- The result is
{{ok,12},{error,invalid_age}}.
Read from the first line down
- Read
{ok, Value}: Return a successful value. - Read
{error, Reason}: Return an expected failure reason. - Read
case ... of: Handle every result tag explicitly.
Symbols are not secret signs
{ok, Value}Return a successful value.
{error, Reason}Return an expected failure reason.
case ... ofHandle every result tag explicitly.
Match each name to its meaning
tagged result
A tuple beginning with ok or error makes the outcome visible to callers.
total function
A total function returns a documented result for every allowed input shape.
exception boundary
Reserve exceptions for unexpected failures; return ordinary validation errors as data.
Why these forms are useful
Run the example first. Predict one result, then change one input and run it again.
A tuple beginning with ok or error makes the outcome visible to callers.
A total function returns a documented result for every allowed input shape.
Why this shape?
Why return {:ok, value} or {ok, Value} so often?
Expected failure stays visible as ordinary data. A caller can list success, known failure, and fallback branches with patterns.
Unexpected failures that cannot be handled here may still raise and reach a supervision boundary. Turning every exception into a generic error discards evidence.
Coming from Java, Python, or JavaScript
Java
- Familiar starting point
- Checked or unchecked exceptions, plus Optional.
- What BEAM changes
- Tagged results keep expected business outcomes in normal control flow.
- False friend
- Not every error should crash, and not every exception should be caught here.
Python
- Familiar starting point
- Exceptions and
None. - What BEAM changes
- A tag names the failure class instead of asking one empty value to carry every meaning.
- False friend
- Translate third-party exceptions into a stable result at a boundary.
JavaScript
- Familiar starting point
- throw, rejected Promises, or null.
- What BEAM changes
- A tuple is not a Promise. It is data that has already arrived.
- False friend
- Read contract as acceptance criteria, not an asynchronous Promise.
Close the answer and try
Reject ages above 120.
{Age, []} when Age >= 0, Age =< ____ -> {ok, Age};Target result: Use 120 as the upper boundary.
Stuck? Read one hint
Add the number after =<.
After you run it, see one answer
{Age, []} when Age >= 0, Age =< 120 -> {ok, Age};Think about it: Should invalid user input crash the caller?
Usually no. Return a tagged error that the caller can display or recover from.
Remember these three lines
- 1
Make outcomes visible in tuples.
- 2
Match every public result tag.
- 3
Use exceptions for genuinely unexpected failures.