Skip to main content

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 checks
  • Result/Ok/Err and Try/Success/Failure - Exception handling with explicit error types
  • Either/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/case pattern matching (Python 3.10+)
  • Full MyPy compatibility with generic type annotations

Contributing

See CLAUDE.md for development setup.

Metadata

Release files for monadc 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for monadc 0.1.0
File Size Uploaded
monadc-0.1.0.tar.gz 207.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for monadc 0.1.0
File Interpreter ABI Platform
monadc-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 233.5 kB

Release files / monadc-0.1.0.tar.gz

Download URL monadc-0.1.0.tar.gz
Size 207.1 kB
Tags Source
SHA-256 checksum
How to use checksums
f2774ccac2dcd6c1f92719a54cd062ffc2df0dc6e4749f68a69800b3ace4d2f0
BLAKE2b-256 checksum
How to use checksums
185b98bfe4688c9b197a6b4d9bf1a5a933f015ad3edffd1c50ba98431c7130f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.19

Release files / monadc-0.1.0-py3-none-any.whl

Download URL monadc-0.1.0-py3-none-any.whl
Size 26.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d3e21997702d13997c97c77bb272ae985a0ce9132d52a2a93ca171f118887fca
BLAKE2b-256 checksum
How to use checksums
8f8f1523328c4bdfdce9123889c9eecd0780779cb31c1ba0ecf37d5d3d8b24f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.19

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page