# Option

> Present or absent, without None checks.

Source: https://docs.pyrula.io/primitives/option/

`Option[T]` is `Some(value)` or `Nothing`. `map` and `filter` skip `Nothing`, so a chain handles absence once at the end.

```python
from pyrula import Option, Some, Nothing

config = {"timeout": "30"}

timeout = (
    Option.of(config.get("timeout"))   # Some("30"), or Nothing if missing
    .map(int)                          # Some(30)
    .filter(lambda s: s > 0)           # Nothing if not positive
    .get_or_else(10)                   # 30
)

Option.of(config.get("port")).or_else(lambda: Some(8080))   # Some(8080)
Some(Some(5)).flatten()                                     # Some(5)
```

`flat_map` chains a function that returns an `Option`. `flatten` raises `TypeError` if the `Some` does not hold an `Option`.

## Get a value out

```python
Option.of(None).get_or_else(0)         # 0
Some(5).fold(0, lambda v: v * 2)       # 10
Nothing.fold(0, lambda v: v * 2)       # 0
```

`get_or_else` evaluates its argument eagerly. `get_or_else_with(factory)` calls the factory once, only on `Nothing`. `tap` observes a present value without changing it.

## Matching

`Nothing` is a singleton, so guard on identity. A bare `case Nothing` is a capture pattern.

```python
match Option.of(config.get("timeout")):
    case Some(value):
        message = f"timeout: {value}"
    case absent if absent is Nothing:
        message = "timeout missing"

assert message == "timeout: 30"
```

## Traversal and sequencing

`traverse(values, fn)` stops at the first `Nothing`; `sequence(values)` collects existing `Option` values. Both return a `list` inside `Some`.

```python
settings = {"host": "localhost", "port": "8080"}
Option.traverse(["host", "port"], lambda k: Option.of(settings.get(k)))
# Some(['localhost', '8080'])
Option.traverse(["host", "missing"], lambda k: Option.of(settings.get(k)))
# Nothing
Option.sequence([Some(1), Some(2)])  # Some([1, 2])
```

A callback that returns a non-`Option` raises `TypeError`.

## Constructors

| Call | Result |
|---|---|
| `Option.of(v)` | `Nothing` if `v is None`, else `Some(v)` |
| `Option.when(test, v)` | `Some(v)` if `test` else `Nothing` |
| `Option.from_callable(fn)` | `Some(fn())`, or `Nothing` if it returns `None` or raises an `Exception` |

## Conversion

```python
Some(5).to_either("missing")               # Ok(5)
Nothing.to_either("missing")               # Err("missing")
Nothing.to_either_with(lambda: "missing")  # Err('missing'); factory runs only on Nothing
```

`to_list()` returns an empty or single-element [`IList`](https://docs.pyrula.io/primitives/collections.md). [`@do`](https://docs.pyrula.io/primitives/do-notation.md) chains dependent lookups. The JSON form is in [JSON form](https://docs.pyrula.io/primitives/json-form.md).
