Scala-inspired Option type for Python with functional programming utilities
Project description
optionc
A Scala-inspired Option type for Python, providing Some and Nil types for safe handling of nullable values with functional programming patterns.
Installation
pip install optionc
Quick Start
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
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
# 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
# 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
# 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:
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
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:
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():
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:
@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
Nonechecks andAttributeErrorexceptions - 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
# 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:
# 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:
AsyncOptionfor asynchronous operations - Pattern matching: Python 3.10+ match statement integration
- Serialization: JSON/pickle support for Some/Nil instances
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 optionc-0.1.0.tar.gz.
File metadata
- Download URL: optionc-0.1.0.tar.gz
- Upload date:
- Size: 64.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e34df504da0d1537b5e9fc4259338be42f4dbdf2e1f46d4ec58915e7b42fc86c
|
|
| MD5 |
864e4d5ee65bec9c764c2f9475c2d581
|
|
| BLAKE2b-256 |
939c5c3c83c7d0827f5eda7ca498224ef9a203d83759d8fc08a7ab918e835cc5
|
File details
Details for the file optionc-0.1.0-py3-none-any.whl.
File metadata
- Download URL: optionc-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f3f30d433460e97789ac8717189872288fba76305438009746bd38832809cf9
|
|
| MD5 |
af289f0adaf3a11586713ebfcccc108d
|
|
| BLAKE2b-256 |
96d1126e05468d8eb08d9ad70feb61cc546bcf8fc9230a45ca54f30b3a8e8eab
|