better-py

Unit Monad

Represent computations with no meaningful return value

The Unit monad represents computations that produce a value but can also represent the unit type (void), similar to Python's None or other languages' unit type. It's useful for sequencing operations where the return value is not meaningful.

Unit[A] wraps a value of type A or None, providing a way to represent void operations in a functional style.

Creating Units

Creating Empty Units

The Unit constructor creates an empty Unit value.

from better_py import Unit

unit = Unit()
repr(unit)  # "Unit()"

Use this for operations that don't produce meaningful values.

Wrapping Values

The Unit.of method creates a Unit from a value.

from better_py import Unit

unit = Unit.of(42)
repr(unit)  # "Unit(42)"

Use this when you need to wrap a value in a Unit context.

Creating Unit Type

The Unit.unit method creates an empty Unit value.

from better_py import Unit

unit = Unit.unit()
repr(unit)  # "Unit()"

This is a named way to create an empty Unit for clarity in your code.

Transforming Units

Mapping Values

The map method applies a function to the Unit value.

from better_py import Unit

Unit.of(5).map(lambda x: x * 2)     # Unit(10)
Unit().map(lambda x: x * 2)         # Unit()

When map is called on an empty Unit, it returns the Unit unchanged.

Real-World Pattern: Multi-Step Pipeline

from better_py import Unit

def validate(data: dict) -> Unit[dict]:
    if not data.get("name"):
        raise ValueError("Name required")
    return Unit.of(data)

def transform(data: dict) -> Unit[dict]:
    return Unit.of({**data, "processed": True})

def persist(data: dict) -> Unit[None]:
    db.save(data)
    return Unit.unit()

def process_data(data: dict) -> Unit[None]:
    return (Unit.of(data)
        .map(lambda d: validate(d))
        .map(lambda d: transform(d))
        .map(lambda d: persist(d))
        .map(lambda _: Unit.unit()))

process_data({"name": "Alice"})

This pattern shows Unit's strength for sequencing operations: each step transforms or processes data, and the final Unit.unit() represents the completion of the pipeline without a meaningful return value.

When to Use

Use Unit when:

  • You have operations with no meaningful return value
  • You want to sequence side effects
  • You're building pipelines with void operations
  • You want to represent void/unit type
  • You need functional style for void operations

Don't use Unit when:

  • You have meaningful return values (use plain types)
  • You need optional values (use Maybe instead)
  • You need error handling (use Result instead)
  • You need side effect management (use IO instead)

See Also

  • Maybe - For optional values
  • Result - For error handling
  • IO - For side effect management

On this page