# Anatomy of an Op

## Type annotations

```python
import tinychain as tc

# This is a native Python function.
# It doesn't need type annotations, although you can add them if you want,
# for example if they make the code more clear,
# or if you're using a linter which requires them.
#
# Since this is a native Python function, TinyChain code can only call it
# at compile-time.
def example1(b):
    a = 2
    return b + a, b * a

# This is a TinyChain Op. It can be evaluated at run-time.
# Every TinyChain Op has its own Context, which you can access
# by using the reserved names "cxt" or "txn" as the first argument of the Op.
#
# Since this is meant to be called at run-time, TinyChain doesn't know
# at compile-time what type of arguments to expect. So an Op needs
# type annotations in order to provide the correct API when compiled to JSON.
#
# For example, if you leave out the tc.Number annotation, TinyChain will
# provide a generic tc.State when this Op is compiled, and you'll get an
# error that says that tc.State doesn't support the "+" or "*" operators.
@tc.get_op
def example2(cxt, b: tc.Number) -> tc.Tuple:
    cxt.a = 2
    return b + cxt.a, b * cxt.a
```

Notice the type annotation `Number` on the parameter `b`. The actual value of parameter `b` may not be known when `example` is encoded as part of a graph configuration, so the type annotation is necessary in order to provide the correct API methods from inside the Python function definition. For example, if someone calls `example(URI("http://example.com/numeric_constant"))`, this is perfectly valid code, but it doesn't explicitly provide the type of the `Value` at `http://example.com/numeric_constant`.

Likewise, the return type annotation `Tuple` is provided for the benefit of the calling code. Without it, TinyChain would not know what type of `State` to return at encoding time (when the compute graph configuration is generated) and helper methods like `unpack` would be unavailable.

## The Op Context

The `cxt` parameter in the `example` function above is a `Context` object. It allows you to explicitly name the states which define your `Op`—in this case, there is one state called `b` passed in as an argument and one state called `a` defined in the body of the `Op`. TensorFlow users should find this familiar because it provides the same functionality as the `name_scope` function and `name=` keyword argument in TensorFlow.

{% hint style="danger" %}
Developers new to TinyChain sometimes find it confusing that the name "cxt" is used to refer to many different, independent contexts. Consider this (non-working!) example:

```python
import tinychain as tc

@tc.get_op
def my_constant() -> tc.Int:
    return 2

@tc.get_op
def example(cxt):
    return cxt.my_constant() * 5  # this is where the error happens!

if __name__ == "__main__":
    cxt = tc.Context()
    cxt.my_constant = my_constant
    cxt.example = example

    # note: here's a run-time call to the example op
    cxt.product = cxt.example()
```

This won't work! Calling `example` in this case will raise a `NotFoundException` because `example` has a *completely different and independent* `Op` context. You can verify this yourself by calling`tc.print_json(cxt)`from within the `example` function.

This should make intuitive sense. If `example` and `my_context` were hosted on different servers, would it make sense for the one to be able to access the other's internal state? What about from a security perspective?
{% endhint %}

## Automatic concurrency

Let's take another look at the `example` function:

```
@tc.get_op
def example(cxt, b: tc.Number) -> tc.Int:
    cxt.a = 2
    return cxt.a + b, cxt.a * b
```

The Python interpreter is imperative and single-threaded, so as a Python developer you're probably accustomed to the assumption that each line of your code will execute in exactly the order that it's written. TinyChain, however, is multi-threaded and features automatic concurrency (like TensorFlow). So, in the example above, `cxt.a + b` and `cxt.a * b` both execute simultaneously when a user executes your compute graph.

Automatic concurrency is crucial for a performant distributed runtime, but it comes with some trade-offs. For example, `Value`s are immutable:

{% hint style="danger" %}

```
@tc.get_op
def example(cxt, b: tc.Number) -> tc.Int:
    cxt.a = 1
    cxt.a += b  # this is where the error happens!
    return cxt.a
```

This won't work! If it did, it would be impossible for TinyChain to calculate the dependencies of each state in the `Op` context, and thus impossible to resolve independent states concurrently.
{% endhint %}

Only `Collections` like a `BTree`, `Table`, or `Tensor` are mutable. This introduces the need to handle *side-effects* with the `After` flow control, which we'll cover in the next section.


---

# 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/anatomy-of-an-op.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.
