Model a tagged result with dataclasses
Also published in our Blogger archive.
Model a tagged result with dataclasses
A tagged result makes expected failure part of a function’s return value instead of relying on a vague sentinel such as None. This example models success as Ok(port) and a validation failure as Err(message). The parse_port function returns one of those explicit dataclasses after checking that input contains decimal digits and represents a port between 1 and 65535. A caller can branch with isinstance and handle each payload according to its tag.
The two inputs make the output concrete: "8080" becomes an Ok result whose integer payload is printed, while "70000" becomes an Err with a stable explanation. Assertions check both result variants and their contents before printing. This is useful for ordinary, anticipated validation outcomes, but it does not mean every exception should be converted into a result; unexpected programming errors can still be allowed to surface. The union spelling Ok | Err requires Python 3.10. dataclasses itself has been part of the standard library since Python 3.7.
See the official dataclasses documentation. AI assistance disclosure: this article was drafted with AI assistance and checked using a synthetic example.
from dataclasses import dataclass
@dataclass(frozen=True)
class Ok:
port: int
@dataclass(frozen=True)
class Err:
message: str
Result = Ok | Err
def parse_port(text: str) -> Result:
if not text.isdecimal():
return Err("port must contain decimal digits")
port = int(text)
if not 1 <= port <= 65535:
return Err("port must be between 1 and 65535")
return Ok(port)
accepted = parse_port("8080")
rejected = parse_port("70000")
assert accepted == Ok(8080)
assert rejected == Err("port must be between 1 and 65535")
assert isinstance(accepted, Ok)
assert isinstance(rejected, Err)
print(f"ok:{accepted.port}")
print(f"error:{rejected.message}")
ok:8080
error:port must be between 1 and 65535