Coverage for src / ocarina / railway / result.py: 88.24%
17 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-04 16:22 +0200
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-04 16:22 +0200
1"""Result type for Railway Oriented Programming.
3Represents computations that can succeed (Ok) or fail (Fail), making error
4handling explicit in the type system.
6Example:
7 >>> def divide(a: int, b: int) -> Result[float]:
8 ... if b == 0:
9 ... return Fail(error=Exception("Division by zero"))
10 ... return Ok(value=a / b)
11 ...
12 >>> result = divide(10, 2)
13 >>> if is_ok(result):
14 ... print(f"Result: {result.value}")
15 >>> else:
16 ... print(f"Error: {result.error}")
18"""
20from dataclasses import dataclass, field
21from typing import TypeGuard, final
24class _BaseResult:
25 """Base class ensuring both Ok and Fail have error attribute for type narrowing."""
27 error: Exception | None
30@final
31@dataclass(frozen=True)
32class Ok[T](_BaseResult):
33 """Success result containing a value.
35 Attributes:
36 value: The successful result value of type T.
37 error: Always None for Ok results.
39 """
41 value: T
42 error: None = None
45@final
46@dataclass(frozen=True)
47class Fail(_BaseResult):
48 """Failure result containing an error.
50 Attributes:
51 error: The exception that caused the failure.
53 """
55 error: Exception = field(default_factory=lambda: Exception("Unknown error"))
58type Result[T] = Ok[T] | Fail
59"""Discriminated union representing success (Ok[T]) or failure (Fail).
61Forces explicit error handling via type checker.
62"""
65def is_ok[T](result: Result[T]) -> TypeGuard[Ok[T]]:
66 """Check if result is Ok, narrowing type to Ok[T]."""
67 return isinstance(result, Ok)
70def is_fail[T](result: Result[T]) -> TypeGuard[Fail]:
71 """Check if result is Fail, narrowing type to Fail."""
72 return isinstance(result, Fail)