optionc


Nameoptionc JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryScala-inspired Option type for Python with functional programming utilities
upload_time2025-08-05 06:49:41
maintainerNone
docs_urlNone
authorCarl You
requires_python>=3.10
licenseMIT
keywords functional maybe monads option programming scala
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # optionc

A Scala-inspired Option type for Python, providing `Some` and `Nil` types for safe handling of nullable values with functional programming patterns.

## Installation

```bash
pip install optionc
```

## Quick Start

```python
from optionc import Option, Some, Nil

# Create options (Scala-like)
user_email = Option("john@example.com")  # Some("john@example.com")
empty_value = Option(None)               # Nil()

# Direct construction
valid_user = Some("alice@example.com")
no_user = Nil()

# Safe transformations
result = (user_email
          .map(str.upper)
          .filter(lambda s: '@' in s)
          .map(lambda s: s.split('@')[1]))

print(result.get_or_else("unknown"))  # "EXAMPLE.COM"
```

## Core Usage

### Creating Options

```python
from optionc import Option, Some, Nil

# From values
Option("hello")     # Some("hello")
Option(42)          # Some(42)  
Option(None)        # Nil()
Option([])          # Some([]) - empty collections are valid

# Direct construction
Some("direct")      # Some("direct")
Nil()              # Nil()
```

### Transformations

```python
# Safe mapping
result = Some(5).map(lambda x: x * 2)  # Some(10)
empty = Nil().map(lambda x: x * 2)     # Nil()

# Filtering
Some(10).filter(lambda x: x > 5)       # Some(10)
Some(3).filter(lambda x: x > 5)        # Nil()

# Flat mapping for nested operations
def divide_by_two(x):
    return Some(x / 2) if x % 2 == 0 else Nil()

Some(8).flat_map(divide_by_two)        # Some(4.0)
Some(5).flat_map(divide_by_two)        # Nil()
```

### Exception Handling

```python
# Normal methods throw exceptions naturally
Some("hello").map(lambda s: s.nonexistent_method())  # AttributeError

# Safe variants return Nil on exceptions  
Some("hello").map_safe(lambda s: s.nonexistent_method())  # Nil()

# Available safe methods: map_safe, flat_map_safe, filter_safe, foreach_safe
```

### Extracting Values

```python
# Get with default
Some("hello").get_or_else("default")    # "hello"
Nil().get_or_else("default")           # "default"

# Get with lazy default
Nil().get_or_else_get(lambda: compute_default())

# Unsafe get (throws on Nil)
Some("hello").get()                    # "hello"
Nil().get()                           # ValueError
```

## Utility Functions

Common patterns for creating Options from various sources:

```python
from optionc import from_callable, from_dict_get, from_getattr

# From function calls
config = from_callable(lambda: load_config_file())  # Some(config) or Nil()

# From dictionary access
user_name = from_dict_get(data, "name", "anonymous")  # Some("anonymous") if key missing

# From object attributes  
email = from_getattr(user, "email", None)  # Some(email) or Nil()
```

### Real-World Example

```python
from optionc import Option, from_dict_get, from_getattr

def process_user_data(data):
    """Safe user data processing pipeline."""
    return (from_dict_get(data, "user")
            .flat_map(lambda user: from_dict_get(user, "profile"))
            .flat_map(lambda profile: from_dict_get(profile, "email"))
            .filter(lambda email: "@" in email)
            .map(lambda email: email.lower())
            .get_or_else("no-email@example.com"))

# Usage
user_data = {
    "user": {
        "profile": {
            "email": "ALICE@EXAMPLE.COM"
        }
    }
}

result = process_user_data(user_data)  # "alice@example.com"
result = process_user_data({})         # "no-email@example.com"
```

## Decorators

Automatically wrap function returns in Options:

### @option

Returns `Some(result)` for non-None values, `Nil()` for None. Exceptions propagate normally:

```python
from optionc import option

@option
def find_user(user_id: str) -> User:
    return database.get(user_id)  # Returns Option[User]

@option  
def compute_discount(price: float) -> float:
    if price < 0:
        raise ValueError("Invalid price")
    return price * 0.1

# Usage
user = find_user("123")  # Some(User) or Nil()
discount = compute_discount(100.0)  # Some(10.0)
# compute_discount(-1) raises ValueError
```

### @option_safe

Same as `@option` but catches exceptions and returns `Nil()`:

```python
from optionc import option_safe

@option_safe
def parse_int(s: str) -> int:
    return int(s)  # Some(42) or Nil() on ValueError

@option_safe
def safe_divide(a: int, b: int) -> float:
    return a / b  # Some(result) or Nil() on ZeroDivisionError

# Usage
result = parse_int("42")        # Some(42)
result = parse_int("invalid")   # Nil()
result = safe_divide(10, 2)     # Some(5.0)
result = safe_divide(10, 0)     # Nil()
```

### Decorator Chaining

Decorators work seamlessly with Option methods:

```python
@option_safe
def extract_domain(email: str) -> str:
    if "@" not in email:
        raise ValueError("Invalid email")
    return email.split("@")[1]

# Chain decorated functions
result = (Option("user@EXAMPLE.com")
          .map(str.lower)
          .flat_map(lambda email: extract_domain(email))
          .map(str.upper))

print(result.get())  # "EXAMPLE.COM"
```

## Why use optionc?

### Good for:
- **Null safety**: Eliminate `None` checks and `AttributeError` exceptions
- **Functional pipelines**: Chain operations without intermediate null checks  
- **API design**: Clear contracts about nullable vs non-nullable values
- **Error handling**: Graceful degradation with safe method variants

### Integration:
- **Type hints**: Full generic type support with `Option[T]`
- **underscorec**: Works seamlessly with functional programming patterns
- **Testing**: Deterministic behavior makes testing easier

## API Reference

### Option Methods

```python
# Checking state
.is_defined()           # True for Some, False for Nil
.is_empty()            # False for Some, True for Nil

# Transformations
.map(func)             # Transform value if present
.map_safe(func)        # Transform value, Nil on exception
.flat_map(func)        # Transform and flatten nested Options  
.flat_map_safe(func)   # Flat map, Nil on exception
.filter(predicate)     # Keep value if predicate matches
.filter_safe(pred)     # Filter, Nil on exception

# Extracting values
.get()                 # Get value (throws on Nil)
.get_or_else(default)  # Get value or default
.get_or_else_get(func) # Get value or call function

# Combining
.or_else(alternative)  # Return self if Some, else alternative
.or_else_get(func)     # Return self if Some, else call function

# Side effects  
.foreach(func)         # Execute function if Some
.foreach_safe(func)    # Execute function, ignore exceptions
```

## Contributing

Development setup:

```bash
# Clone and setup
git clone https://github.com/carlyou/optionc.git
cd optionc
uv sync --dev

# Run tests
uv run pytest

# Run tests with coverage
uv run pytest --cov=optionc --cov-report=html
```

## Future Improvements

- **Async support**: `AsyncOption` for asynchronous operations
- **Pattern matching**: Python 3.10+ match statement integration  
- **Serialization**: JSON/pickle support for Some/Nil instances

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "optionc",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "functional, maybe, monads, option, programming, scala",
    "author": "Carl You",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/93/9c/5c3c83c7d0827f5eda7ca498224ef9a203d83759d8fc08a7ab918e835cc5/optionc-0.1.0.tar.gz",
    "platform": null,
    "description": "# optionc\n\nA Scala-inspired Option type for Python, providing `Some` and `Nil` types for safe handling of nullable values with functional programming patterns.\n\n## Installation\n\n```bash\npip install optionc\n```\n\n## Quick Start\n\n```python\nfrom optionc import Option, Some, Nil\n\n# Create options (Scala-like)\nuser_email = Option(\"john@example.com\")  # Some(\"john@example.com\")\nempty_value = Option(None)               # Nil()\n\n# Direct construction\nvalid_user = Some(\"alice@example.com\")\nno_user = Nil()\n\n# Safe transformations\nresult = (user_email\n          .map(str.upper)\n          .filter(lambda s: '@' in s)\n          .map(lambda s: s.split('@')[1]))\n\nprint(result.get_or_else(\"unknown\"))  # \"EXAMPLE.COM\"\n```\n\n## Core Usage\n\n### Creating Options\n\n```python\nfrom optionc import Option, Some, Nil\n\n# From values\nOption(\"hello\")     # Some(\"hello\")\nOption(42)          # Some(42)  \nOption(None)        # Nil()\nOption([])          # Some([]) - empty collections are valid\n\n# Direct construction\nSome(\"direct\")      # Some(\"direct\")\nNil()              # Nil()\n```\n\n### Transformations\n\n```python\n# Safe mapping\nresult = Some(5).map(lambda x: x * 2)  # Some(10)\nempty = Nil().map(lambda x: x * 2)     # Nil()\n\n# Filtering\nSome(10).filter(lambda x: x > 5)       # Some(10)\nSome(3).filter(lambda x: x > 5)        # Nil()\n\n# Flat mapping for nested operations\ndef divide_by_two(x):\n    return Some(x / 2) if x % 2 == 0 else Nil()\n\nSome(8).flat_map(divide_by_two)        # Some(4.0)\nSome(5).flat_map(divide_by_two)        # Nil()\n```\n\n### Exception Handling\n\n```python\n# Normal methods throw exceptions naturally\nSome(\"hello\").map(lambda s: s.nonexistent_method())  # AttributeError\n\n# Safe variants return Nil on exceptions  \nSome(\"hello\").map_safe(lambda s: s.nonexistent_method())  # Nil()\n\n# Available safe methods: map_safe, flat_map_safe, filter_safe, foreach_safe\n```\n\n### Extracting Values\n\n```python\n# Get with default\nSome(\"hello\").get_or_else(\"default\")    # \"hello\"\nNil().get_or_else(\"default\")           # \"default\"\n\n# Get with lazy default\nNil().get_or_else_get(lambda: compute_default())\n\n# Unsafe get (throws on Nil)\nSome(\"hello\").get()                    # \"hello\"\nNil().get()                           # ValueError\n```\n\n## Utility Functions\n\nCommon patterns for creating Options from various sources:\n\n```python\nfrom optionc import from_callable, from_dict_get, from_getattr\n\n# From function calls\nconfig = from_callable(lambda: load_config_file())  # Some(config) or Nil()\n\n# From dictionary access\nuser_name = from_dict_get(data, \"name\", \"anonymous\")  # Some(\"anonymous\") if key missing\n\n# From object attributes  \nemail = from_getattr(user, \"email\", None)  # Some(email) or Nil()\n```\n\n### Real-World Example\n\n```python\nfrom optionc import Option, from_dict_get, from_getattr\n\ndef process_user_data(data):\n    \"\"\"Safe user data processing pipeline.\"\"\"\n    return (from_dict_get(data, \"user\")\n            .flat_map(lambda user: from_dict_get(user, \"profile\"))\n            .flat_map(lambda profile: from_dict_get(profile, \"email\"))\n            .filter(lambda email: \"@\" in email)\n            .map(lambda email: email.lower())\n            .get_or_else(\"no-email@example.com\"))\n\n# Usage\nuser_data = {\n    \"user\": {\n        \"profile\": {\n            \"email\": \"ALICE@EXAMPLE.COM\"\n        }\n    }\n}\n\nresult = process_user_data(user_data)  # \"alice@example.com\"\nresult = process_user_data({})         # \"no-email@example.com\"\n```\n\n## Decorators\n\nAutomatically wrap function returns in Options:\n\n### @option\n\nReturns `Some(result)` for non-None values, `Nil()` for None. Exceptions propagate normally:\n\n```python\nfrom optionc import option\n\n@option\ndef find_user(user_id: str) -> User:\n    return database.get(user_id)  # Returns Option[User]\n\n@option  \ndef compute_discount(price: float) -> float:\n    if price < 0:\n        raise ValueError(\"Invalid price\")\n    return price * 0.1\n\n# Usage\nuser = find_user(\"123\")  # Some(User) or Nil()\ndiscount = compute_discount(100.0)  # Some(10.0)\n# compute_discount(-1) raises ValueError\n```\n\n### @option_safe\n\nSame as `@option` but catches exceptions and returns `Nil()`:\n\n```python\nfrom optionc import option_safe\n\n@option_safe\ndef parse_int(s: str) -> int:\n    return int(s)  # Some(42) or Nil() on ValueError\n\n@option_safe\ndef safe_divide(a: int, b: int) -> float:\n    return a / b  # Some(result) or Nil() on ZeroDivisionError\n\n# Usage\nresult = parse_int(\"42\")        # Some(42)\nresult = parse_int(\"invalid\")   # Nil()\nresult = safe_divide(10, 2)     # Some(5.0)\nresult = safe_divide(10, 0)     # Nil()\n```\n\n### Decorator Chaining\n\nDecorators work seamlessly with Option methods:\n\n```python\n@option_safe\ndef extract_domain(email: str) -> str:\n    if \"@\" not in email:\n        raise ValueError(\"Invalid email\")\n    return email.split(\"@\")[1]\n\n# Chain decorated functions\nresult = (Option(\"user@EXAMPLE.com\")\n          .map(str.lower)\n          .flat_map(lambda email: extract_domain(email))\n          .map(str.upper))\n\nprint(result.get())  # \"EXAMPLE.COM\"\n```\n\n## Why use optionc?\n\n### Good for:\n- **Null safety**: Eliminate `None` checks and `AttributeError` exceptions\n- **Functional pipelines**: Chain operations without intermediate null checks  \n- **API design**: Clear contracts about nullable vs non-nullable values\n- **Error handling**: Graceful degradation with safe method variants\n\n### Integration:\n- **Type hints**: Full generic type support with `Option[T]`\n- **underscorec**: Works seamlessly with functional programming patterns\n- **Testing**: Deterministic behavior makes testing easier\n\n## API Reference\n\n### Option Methods\n\n```python\n# Checking state\n.is_defined()           # True for Some, False for Nil\n.is_empty()            # False for Some, True for Nil\n\n# Transformations\n.map(func)             # Transform value if present\n.map_safe(func)        # Transform value, Nil on exception\n.flat_map(func)        # Transform and flatten nested Options  \n.flat_map_safe(func)   # Flat map, Nil on exception\n.filter(predicate)     # Keep value if predicate matches\n.filter_safe(pred)     # Filter, Nil on exception\n\n# Extracting values\n.get()                 # Get value (throws on Nil)\n.get_or_else(default)  # Get value or default\n.get_or_else_get(func) # Get value or call function\n\n# Combining\n.or_else(alternative)  # Return self if Some, else alternative\n.or_else_get(func)     # Return self if Some, else call function\n\n# Side effects  \n.foreach(func)         # Execute function if Some\n.foreach_safe(func)    # Execute function, ignore exceptions\n```\n\n## Contributing\n\nDevelopment setup:\n\n```bash\n# Clone and setup\ngit clone https://github.com/carlyou/optionc.git\ncd optionc\nuv sync --dev\n\n# Run tests\nuv run pytest\n\n# Run tests with coverage\nuv run pytest --cov=optionc --cov-report=html\n```\n\n## Future Improvements\n\n- **Async support**: `AsyncOption` for asynchronous operations\n- **Pattern matching**: Python 3.10+ match statement integration  \n- **Serialization**: JSON/pickle support for Some/Nil instances\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Scala-inspired Option type for Python with functional programming utilities",
    "version": "0.1.0",
    "project_urls": {
        "Homepage": "https://github.com/your-username/optionc",
        "Issues": "https://github.com/your-username/optionc/issues",
        "Repository": "https://github.com/your-username/optionc.git"
    },
    "split_keywords": [
        "functional",
        " maybe",
        " monads",
        " option",
        " programming",
        " scala"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "96d1126e05468d8eb08d9ad70feb61cc546bcf8fc9230a45ca54f30b3a8e8eab",
                "md5": "af289f0adaf3a11586713ebfcccc108d",
                "sha256": "8f3f30d433460e97789ac8717189872288fba76305438009746bd38832809cf9"
            },
            "downloads": -1,
            "filename": "optionc-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "af289f0adaf3a11586713ebfcccc108d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 9546,
            "upload_time": "2025-08-05T06:49:40",
            "upload_time_iso_8601": "2025-08-05T06:49:40.426378Z",
            "url": "https://files.pythonhosted.org/packages/96/d1/126e05468d8eb08d9ad70feb61cc546bcf8fc9230a45ca54f30b3a8e8eab/optionc-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "939c5c3c83c7d0827f5eda7ca498224ef9a203d83759d8fc08a7ab918e835cc5",
                "md5": "864e4d5ee65bec9c764c2f9475c2d581",
                "sha256": "e34df504da0d1537b5e9fc4259338be42f4dbdf2e1f46d4ec58915e7b42fc86c"
            },
            "downloads": -1,
            "filename": "optionc-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "864e4d5ee65bec9c764c2f9475c2d581",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 64541,
            "upload_time": "2025-08-05T06:49:41",
            "upload_time_iso_8601": "2025-08-05T06:49:41.874257Z",
            "url": "https://files.pythonhosted.org/packages/93/9c/5c3c83c7d0827f5eda7ca498224ef9a203d83759d8fc08a7ab918e835cc5/optionc-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-05 06:49:41",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "your-username",
    "github_project": "optionc",
    "github_not_found": true,
    "lcname": "optionc"
}
        
Elapsed time: 1.97965s