# Flow control: after, cond, while\_loop

## cond

When using Python to develop a TinyChain service, it’s important to remember that the output of your code is a compute graph which will be served by a TinyChain host; your Python code itself won’t be running in production. This means that you can’t use Python control flow operators like `if` or `while` the way that you’re used to. For example:

```python
@tc.get_op
def to_feet(txn, meters: tc.Number) -> tc.Number:
    # IMPORTANT! don't use Python's if statement! use tc.cond!
    return tc.cond(
        meters >= 0,
        meters * 3.28,
        tc.error.BadRequest("negative distance is not supported"))
```

## after

It’s also important to keep in mind that TinyChain by default resolves all dependencies concurrently, and does not resolve unused dependencies. Consider this function:

```python
@tc.post_op
def num_rows(txn):
    max_len = 100
    schema = tc.table.Schema(
        [tc.Column("user_id", tc.Number)],
        [tc.Column("name", tc.String, max_len), tc.Column("email", tc.String, max_len)])

    txn.table = tc.table.Table(schema)
    txn.table.insert((123,), ("Bob", "bob.roberts@example.com"))
    return txn.table.count()
```

This Op will *always* resolve to *zero*. This may seem counterintuitive at first, because you can obviously see the `table.insert` statement, but notice that the return value `table.count` does not actually depend on `table.insert`; `table.insert` is only intended to create a side-effect, so its result is unused. To handle situations like this, use the `after` flow control:

```python
@tc.post_op
def num_rows(txn):
    max_len = 100
    schema = tc.schema.Table(
        [tc.Column("user_id", tc.Number)],
        [tc.Column("name", tc.String, max_len), tc.Column("email", tc.String, max_len)])

    txn.table = tc.Table(schema)
    return tc.after(
        txn.table.insert((123,), ("Bob", "bob.roberts@example.com")),
        txn.table.count())
```

Now, since the program explicitly indicates that `table.count` depends on a side-effect of `table.insert`, TinyChain won’t execute `table.count` until after the call to `table.insert` has completed successfully.

## while\_loop

Loops are probably the most difficult part of TinyChain to get used to if you've never used a graph runtime before.

Consider this simple while loop:

```python
i = 0
while i < 10:
    i += 1
```

In a TinyChain compute graph, you have to account for the facts that a) you need the loop to run at execution time (when a user executes your graph), not encoding time (when the TinyChain Python client exports your graph configuration as JSON), and b) TinyChain `Value`s are immutable:

```python
import tinychain as tc

@tc.get_op
def loop(until: tc.Number) -> tc.Int:
    # the closure decorator captures referenced states from the outer scope,
    # in this case "until"
    @tc.closure
    @tc.post_op
    def cond(i: tc.Int):
        return i < until

    @tc.post_op
    def step(i: tc.Int) -> tc.Int:
        return tc.Map(i=i + 1)  # here we return the new state of the loop

    initial_state = tc.Map(i=0)  # here we set the initial state of the loop

    # return the loop itself
    return tc.while_loop(cond, step, initial_state)
```

## Nested conditionals

Consider this example:

{% hint style="danger" %}

```python
@tc.post_op
def maybe_delete(table: tc.table.Table, should_update: tc.Bool):
    a = tc.cond(should_update,
        table.update({"column": "value"}),
        table.delete())

    # this won't work!
    return tc.cond(table.is_empty(), tc.BadRequest("empty table"), a)
```

{% endhint %}

In this case, TinyChain is unable to resolve the dependencies of the state to `return` without executing *both* branches of `a`, which would make `a` no longer a conditional (the `table` would be deleted!). For this reason, nested conditionals are not allowed. The most foolproof way to handle a nested conditional is to use an `Op`:

{% hint style="success" %}

```python
@tc.post_op
def maybe_delete(table: tc.table.Table, should_update: tc.Bool):
    return tc.cond(should_update,
        table.update({"column": "value"}),
        table.delete())

@tc.post_op
def check_result(table: tc.table.Table, should_update: tc.Bool):
    return tc.cond(table.is_empty(),
        tc.error.BadRequest("empty table"),
        maybe_delete(table, should_update))
```

{% endhint %}

## Examples: updating a Tensor conditionally

For more detailed examples on how to use common flow controls, take a look at the [client tests](https://github.com/haydnv/tinychain/tree/main/tests/tctest/client).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tinychain.net/guides/flow-control-after-if-while.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
