# Do Notation

> Chain Either, Option or Try with yield instead of nested flat_map calls.

Source: https://docs.pyrula.io/primitives/do-notation/

`@do` binds values from one family inside a generator. Each `yield` unwraps a success, and the first `Err`, `Nothing` or `Failure` ends the block and becomes its result.

```python
from pyrula import Either, do, Ok, Err

def parse_int(raw: str) -> Either[int, str]:
    try:
        return Ok(int(raw))
    except ValueError:
        return Err(f"not an int: {raw!r}")

@do
def total(a: str, b: str):
    x = yield parse_int(a)
    y = yield parse_int(b)
    return x + y

total("2", "3")     # Ok(5)
total("nope", "3")  # Err("not an int: 'nope'")
```

[`Option`](https://docs.pyrula.io/primitives/option.md) and [`Try`](https://docs.pyrula.io/primitives/try.md) work the same way:

```python
from pyrula import Option, Nothing

users = {"ada": {"email": "ada@example.com"}}

@do
def lookup(uid: str):
    user = yield Option.of(users.get(uid))
    email = yield Option.of(user.get("email"))
    return email

assert lookup("ada").get_or_else("") == "ada@example.com"
assert lookup("missing") is Nothing
```

## Limits

- A block yields one family. Mixing families raises `TypeError`.
- `Validated` is not supported; use `zip`, `sequence` or `traverse` to accumulate errors.
- `@do` does not capture exceptions raised in the body, including in a `Try` block. Wrap them with `Try.apply` first.

## Async

`@do_async` drives an async generator the same way. Python forbids `return value` in an async generator, so the result is the last yielded value:

```python
import asyncio

from pyrula import Either, Ok, Err, do_async

async def fetch_name(uid: str) -> Either[str, str]:
    return Ok("Ada") if uid == "ada" else Err("unknown user")

@do_async
async def greeting(uid: str):
    name = yield await fetch_name(uid)
    yield Ok(f"Hello, {name}")

assert asyncio.run(greeting("ada")) == Ok("Hello, Ada")
assert asyncio.run(greeting("missing")) == Err("unknown user")
```

In a `Try` block, `@do_async` captures `Exception` from the body or a yielded awaitable as `Failure`.
