monadc


Namemonadc JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryComprehensive functional programming monads for Python - Option, Either, Try, Result types with full MyPy support
upload_time2025-08-20 06:01:34
maintainerNone
docs_urlNone
authorCarl You
requires_python>=3.10
licenseMIT
keywords either functional monads option programming result rust scala try
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # monadc

Functional programming monads for Python with first-class pattern matching support.

## Installation

```bash
pip install monadc
```

## Option - Handle Missing Data Safely

**Example**: Extracting nested data from API responses without crashes.

```python
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.*

```python
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.

```python
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](CLAUDE.md) for development setup.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "monadc",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "either, functional, monads, option, programming, result, rust, scala, try",
    "author": "Carl You",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/18/5b/98bfe4688c9b197a6b4d9bf1a5a933f015ad3edffd1c50ba98431c7130f5/monadc-0.1.0.tar.gz",
    "platform": null,
    "description": "# monadc\n\nFunctional programming monads for Python with first-class pattern matching support.\n\n## Installation\n\n```bash\npip install monadc\n```\n\n## Option - Handle Missing Data Safely\n\n**Example**: Extracting nested data from API responses without crashes.\n\n```python\nfrom monadc import Option, Some, Nil\n\n# Instead of this brittle code:\ndef get_user_avatar(api_response):\n    if (api_response and \"data\" in api_response and \n        api_response[\"data\"] and \"user\" in api_response[\"data\"] and\n        api_response[\"data\"][\"user\"] and \"profile\" in api_response[\"data\"][\"user\"]):\n        profile = api_response[\"data\"][\"user\"][\"profile\"]\n        return profile.get(\"avatar_url\", \"/default.png\")\n    return \"/default.png\"\n\n# Write this:\ndef get_user_avatar(api_response):\n    return (Option(api_response.get(\"data\"))\n            .flat_map(lambda data: Option(data.get(\"user\")))\n            .flat_map(lambda user: Option(user.get(\"profile\")))\n            .flat_map(lambda profile: Option(profile.get(\"avatar_url\")))\n            .unwrap_or(\"/default.png\"))\n\n# Pattern matching for different cases\ndef handle_user_data(api_response):\n    user_profile = (Option(api_response.get(\"data\"))\n                   .flat_map(lambda data: Option(data.get(\"user\")))\n                   .flat_map(lambda user: Option(user.get(\"profile\"))))\n    \n    match user_profile:\n        case Some(profile) if profile.get(\"verified\"):\n            return f\"\u2713 Verified user: {profile['name']}\"\n        case Some(profile):\n            return f\"User: {profile.get('name', 'Anonymous')}\"\n        case Nil():\n            return \"Please log in\"\n```\n\n## Try - Exception-Safe Operations\n\n**Example**: File I/O and parsing operations that can fail in multiple ways.\n\n*Note: You can also use `Result/Ok/Err` for Rust-style syntax with identical functionality.*\n\n```python\nfrom monadc import Try, Success, Failure, try_\nimport json\n\n@try_\ndef load_user_config(username: str):\n    with open(f\"users/{username}/config.json\") as f:\n        return json.load(f)\n\n@try_\ndef validate_theme(config: dict):\n    theme = config[\"ui\"][\"theme\"]\n    if theme not in [\"light\", \"dark\", \"auto\"]:\n        raise ValueError(f\"Invalid theme: {theme}\")\n    return theme\n\n# Chain operations that can each fail\ndef get_user_theme(username: str):\n    return (load_user_config(username)\n            .and_then(validate_theme)\n            .unwrap_or(\"light\"))\n\n# Pattern matching handles different failure types\ndef load_config_with_feedback(username: str):\n    result = load_user_config(username).and_then(validate_theme)\n    \n    match result:\n        case Success(theme):\n            return f\"Loaded theme: {theme}\"\n        case Failure(FileNotFoundError()):\n            return \"No config found, using defaults\"\n        case Failure(json.JSONDecodeError()):\n            return \"Config file corrupted, using defaults\" \n        case Failure(KeyError()):\n            return \"Config missing theme setting\"\n        case Failure(ValueError() as e):\n            return f\"Invalid config: {e}\"\n```\n\n## Either - Validation with Error Messages\n\n**Example**: Form validation that collects specific error messages.\n\n```python\nfrom monadc import Either, Left, Right\n\ndef validate_email(email: str) -> Either[str, str]:\n    if not email:\n        return Left(\"Email is required\")\n    if \"@\" not in email or \".\" not in email:\n        return Left(\"Please enter a valid email address\")\n    return Right(email.lower())\n\ndef validate_age(age_str: str) -> Either[str, int]:\n    try:\n        age = int(age_str)\n        if age < 13:\n            return Left(\"Must be at least 13 years old\")\n        if age > 120:\n            return Left(\"Please enter a valid age\")\n        return Right(age)\n    except ValueError:\n        return Left(\"Age must be a number\")\n\n# Pattern matching for comprehensive error handling\ndef create_user_account(form_data):\n    email_result = validate_email(form_data.get(\"email\", \"\"))\n    age_result = validate_age(form_data.get(\"age\", \"\"))\n    \n    match (email_result, age_result):\n        case (Right(email), Right(age)):\n            return create_account(email, age)\n        case (Left(email_error), Right(_)):\n            return {\"error\": f\"Email: {email_error}\"}\n        case (Right(_), Left(age_error)):\n            return {\"error\": f\"Age: {age_error}\"}\n        case (Left(email_error), Left(age_error)):\n            return {\"error\": f\"Email: {email_error}; Age: {age_error}\"}\n```\n\n\n## Key Benefits\n\n**Four functional primitives for safer code:**\n- `Option/Some/Nil` - Handle missing data without None checks\n- `Result/Ok/Err` and `Try/Success/Failure` - Exception handling with explicit error types  \n- `Either/Left/Right` - Type-safe unions for validation and error messaging\n\n**Enhanced Python integration:**\n- Function decorators (`@try_`, `@option`, `@result`) for automatic wrapping\n- First-class support for `match/case` pattern matching (Python 3.10+)\n- Full MyPy compatibility with generic type annotations\n\n## Contributing\n\nSee [CLAUDE.md](CLAUDE.md) for development setup.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Comprehensive functional programming monads for Python - Option, Either, Try, Result types with full MyPy support",
    "version": "0.1.0",
    "project_urls": {
        "Homepage": "https://github.com/carlyou/monadc",
        "Issues": "https://github.com/carlyou/monadc/issues",
        "Repository": "https://github.com/carlyou/monadc.git"
    },
    "split_keywords": [
        "either",
        " functional",
        " monads",
        " option",
        " programming",
        " result",
        " rust",
        " scala",
        " try"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8f8f1523328c4bdfdce9123889c9eecd0780779cb31c1ba0ecf37d5d3d8b24f6",
                "md5": "559b8c213de54a5955cdb96ab85e78c7",
                "sha256": "d3e21997702d13997c97c77bb272ae985a0ce9132d52a2a93ca171f118887fca"
            },
            "downloads": -1,
            "filename": "monadc-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "559b8c213de54a5955cdb96ab85e78c7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 26391,
            "upload_time": "2025-08-20T06:01:33",
            "upload_time_iso_8601": "2025-08-20T06:01:33.063983Z",
            "url": "https://files.pythonhosted.org/packages/8f/8f/1523328c4bdfdce9123889c9eecd0780779cb31c1ba0ecf37d5d3d8b24f6/monadc-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "185b98bfe4688c9b197a6b4d9bf1a5a933f015ad3edffd1c50ba98431c7130f5",
                "md5": "492cf1477a4317128529690dbf8e4ee7",
                "sha256": "f2774ccac2dcd6c1f92719a54cd062ffc2df0dc6e4749f68a69800b3ace4d2f0"
            },
            "downloads": -1,
            "filename": "monadc-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "492cf1477a4317128529690dbf8e4ee7",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 207117,
            "upload_time": "2025-08-20T06:01:34",
            "upload_time_iso_8601": "2025-08-20T06:01:34.670708Z",
            "url": "https://files.pythonhosted.org/packages/18/5b/98bfe4688c9b197a6b4d9bf1a5a933f015ad3edffd1c50ba98431c7130f5/monadc-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-20 06:01:34",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "carlyou",
    "github_project": "monadc",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "monadc"
}
        
Elapsed time: 1.05284s