Comprehensive functional programming monads for Python - Option, Either, Try, Result types with full MyPy support
Project description
monadc
Functional programming monads for Python with first-class pattern matching support.
Installation
pip install monadc
Option - Handle Missing Data Safely
Example: Extracting nested data from API responses without crashes.
from monadc import Option, Some, Nil
# Instead of this brittle code:
def get_user_avatar(api_response):
if (api_response and "data" in api_response and
api_response["data"] and "user" in api_response["data"] and
api_response["data"]["user"] and "profile" in api_response["data"]["user"]):
profile = api_response["data"]["user"]["profile"]
return profile.get("avatar_url", "/default.png")
return "/default.png"
# Write this:
def get_user_avatar(api_response):
return (Option(api_response.get("data"))
.flat_map(lambda data: Option(data.get("user")))
.flat_map(lambda user: Option(user.get("profile")))
.flat_map(lambda profile: Option(profile.get("avatar_url")))
.unwrap_or("/default.png"))
# Pattern matching for different cases
def handle_user_data(api_response):
user_profile = (Option(api_response.get("data"))
.flat_map(lambda data: Option(data.get("user")))
.flat_map(lambda user: Option(user.get("profile"))))
match user_profile:
case Some(profile) if profile.get("verified"):
return f"✓ Verified user: {profile['name']}"
case Some(profile):
return f"User: {profile.get('name', 'Anonymous')}"
case Nil():
return "Please log in"
Try - Exception-Safe Operations
Example: File I/O and parsing operations that can fail in multiple ways.
Note: You can also use Result/Ok/Err for Rust-style syntax with identical functionality.
from monadc import Try, Success, Failure, try_
import json
@try_
def load_user_config(username: str):
with open(f"users/{username}/config.json") as f:
return json.load(f)
@try_
def validate_theme(config: dict):
theme = config["ui"]["theme"]
if theme not in ["light", "dark", "auto"]:
raise ValueError(f"Invalid theme: {theme}")
return theme
# Chain operations that can each fail
def get_user_theme(username: str):
return (load_user_config(username)
.and_then(validate_theme)
.unwrap_or("light"))
# Pattern matching handles different failure types
def load_config_with_feedback(username: str):
result = load_user_config(username).and_then(validate_theme)
match result:
case Success(theme):
return f"Loaded theme: {theme}"
case Failure(FileNotFoundError()):
return "No config found, using defaults"
case Failure(json.JSONDecodeError()):
return "Config file corrupted, using defaults"
case Failure(KeyError()):
return "Config missing theme setting"
case Failure(ValueError() as e):
return f"Invalid config: {e}"
Either - Validation with Error Messages
Example: Form validation that collects specific error messages.
from monadc import Either, Left, Right
def validate_email(email: str) -> Either[str, str]:
if not email:
return Left("Email is required")
if "@" not in email or "." not in email:
return Left("Please enter a valid email address")
return Right(email.lower())
def validate_age(age_str: str) -> Either[str, int]:
try:
age = int(age_str)
if age < 13:
return Left("Must be at least 13 years old")
if age > 120:
return Left("Please enter a valid age")
return Right(age)
except ValueError:
return Left("Age must be a number")
# Pattern matching for comprehensive error handling
def create_user_account(form_data):
email_result = validate_email(form_data.get("email", ""))
age_result = validate_age(form_data.get("age", ""))
match (email_result, age_result):
case (Right(email), Right(age)):
return create_account(email, age)
case (Left(email_error), Right(_)):
return {"error": f"Email: {email_error}"}
case (Right(_), Left(age_error)):
return {"error": f"Age: {age_error}"}
case (Left(email_error), Left(age_error)):
return {"error": f"Email: {email_error}; Age: {age_error}"}
Key Benefits
Four functional primitives for safer code:
Option/Some/Nil- Handle missing data without None checksResult/Ok/ErrandTry/Success/Failure- Exception handling with explicit error typesEither/Left/Right- Type-safe unions for validation and error messaging
Enhanced Python integration:
- Function decorators (
@try_,@option,@result) for automatic wrapping - First-class support for
match/casepattern matching (Python 3.10+) - Full MyPy compatibility with generic type annotations
Contributing
See CLAUDE.md for development setup.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file monadc-0.1.0.tar.gz.
File metadata
- Download URL: monadc-0.1.0.tar.gz
- Upload date:
- Size: 207.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2774ccac2dcd6c1f92719a54cd062ffc2df0dc6e4749f68a69800b3ace4d2f0
|
|
| MD5 |
492cf1477a4317128529690dbf8e4ee7
|
|
| BLAKE2b-256 |
185b98bfe4688c9b197a6b4d9bf1a5a933f015ad3edffd1c50ba98431c7130f5
|
File details
Details for the file monadc-0.1.0-py3-none-any.whl.
File metadata
- Download URL: monadc-0.1.0-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3e21997702d13997c97c77bb272ae985a0ce9132d52a2a93ca171f118887fca
|
|
| MD5 |
559b8c213de54a5955cdb96ab85e78c7
|
|
| BLAKE2b-256 |
8f8f1523328c4bdfdce9123889c9eecd0780779cb31c1ba0ecf37d5d3d8b24f6
|