Returns: Meaningful, Typed, and Safe Function Returns
The `returns` library (version 0.26.0) is a functional programming toolkit for Python, enhancing type-safety and explicit error handling. It allows developers to make functions return meaningful, typed, and safe values like `Result`, `Maybe`, and `IO` monads, promoting composable and testable code. The project is actively maintained with frequent releases, often every few months, introducing new features and compatibility updates.
Warnings
- breaking `returns` has specific Python version requirements that have changed over time. Python 3.7 support was dropped in `0.22.0`, and Python 3.9 support was dropped in `0.24.0`. Current versions require Python `>=3.10`.
- breaking The `success_type` and `failure_type` fields were removed from `IOResult`, `Maybe`, and `Result` types. Code that directly accessed these attributes will break.
- gotcha The `Maybe` type now implements a `__bool__` method where only `Nothing` evaluates to `False`. `Some` values, regardless of their content, evaluate to `True`.
- gotcha `returns` is highly reliant on specific `mypy` versions for correct type inference and to prevent `mypy` errors. The compatible `mypy` version often changes with `returns` releases.
- gotcha Attempting to unwrap a `Failure` or `Nothing` value (e.g., using `.unwrap()`, `.value_or()`, or accessing `.value` directly on `Failure`) without handling the error path will raise an `UnwrapFailedError`.
Install
-
pip install returns -
pip install returns[compatible-mypy]
Imports
- Result, Success, Failure
from returns.result import Result, Success, Failure
- Maybe, Some, Nothing
from returns.maybe import Maybe, Some, Nothing
- IO, IOResult
from returns.io import IO, IOResult
- do
from returns.do_notation import do
- safe
from returns.result import safe
- maybe
from returns.maybe import maybe
Quickstart
from returns.result import Result, Success, Failure
from returns.do_notation import do
def divide(numerator: int, denominator: int) -> Result[float, str]:
if denominator == 0:
return Failure('Cannot divide by zero')
return Success(numerator / denominator)
def multiply(value: float, multiplier: int) -> Result[float, str]:
if multiplier < 0:
return Failure('Multiplier cannot be negative')
return Success(value * multiplier)
@do(Result)
def calculate_compound(num: int, den: int, mult: int) -> Result[float, str]:
divided_val = yield divide(num, den)
final_val = yield multiply(divided_val, mult)
return final_val
# Example usage:
assert calculate_compound(10, 2, 5) == Success(25.0)
assert calculate_compound(10, 0, 5) == Failure('Cannot divide by zero')
assert calculate_compound(10, 2, -1) == Failure('Multiplier cannot be negative')
print(calculate_compound(10, 2, 5))