# Types and casting

```python
import os
import tinychain as tc

# use the demo host at demo.tinychain.net, unless overridden by the environment variable `TC_HOST`
HOST = tc.host.Host(os.getenv("TC_HOST", "http://demo.tinychain.net"))
# this endpoint will attempt to resolve whatever state you send it, without committing any changes
ENDPOINT = "/transact/hypothetical"

# this assumes that `x` is of type `tc.tensor.Tensor`
def average(x):
    return x.sum() / x.size

if __name__ == "__main__":
    cxt = tc.Context()  # initialize a new Op context
    cxt.x = tc.tensor.Dense.ones([3])  # initialize a new Dense tensor
    cxt.result = average(cxt.x)  # call our custom function

    actual = HOST.post(ENDPOINT, cxt)  # execute the `Op` defined by `cxt`
    assert actual == 1  # verify the result
```

The `average` function in this example works well enough, but in a public library it might not handle every situation it should. For example, if a user calls `average(tc.URI("$x"))`, the `sum` method and `size` property won't be available because a `URI` doesn't have a `sum` method or `size` property. To handle cases like this, we can use TinyChain's built-in reflection annotations:

```python
# ...
 
# the `post_op` annotation tells TinyChain to reflect over this function
# and assume that it defines a POST Op 
@tc.post_op
# the annotation on `x` tells TinyChain to expect a `Tensor`
def average(x: tc.tensor.Tensor) -> tc.Number:
    # the return annotation tells the calling code to expect a `Number`
    return x.sum() / x.size

if __name__ == "__main__":
    cxt = tc.Context()  # initialize a new Op context
    # `average` is now a TinyChain `Op`, not a native Python function,
    # so it needs to be made addressable by the calling context
    cxt.average = average
    cxt.x = tc.tensor.Dense.ones([3])  # initialize a new Dense tensor
    cxt.result = cxt.average(x=tc.URI("$x"))  # call our custom `Op`
    actual = HOST.post(ENDPOINT, cxt)  # execute the `Op` defined by `cxt`
    assert actual == 1  # verify the result

```

Now, even though the type of `x` in the calling code is a `URI`, the `average` Op still works as the caller expects because the type annotations tell the code in `def average` what to expect.

{% hint style="warning" %}
**Important!** In the TinyChain Python client, types define *what to expect.* They don't necessarily instantiate or cast types, like they do in native Python code.
{% endhint %}

To illustrate this, let's change the return type of `average` to a `String`:

{% hint style="danger" %}

```python
# ...

@tc.post_op
def average(x: tc.tensor.Tensor) -> tc.String:  # <-- return type `String`
    return x.sum() / x.size

if __name__ == "__main__":
    # ...
    actual = HOST.post(ENDPOINT, cxt)
    assert actual == "1"  # this assertion fails!
```

{% endhint %}

Now, the return annotation `tc.String` tells the calling code to expect a `String` but the `Op` still actually returns a `Number`, so the assert fails. To fix this, we'll have to explicitly cast the return value:

```python
# ...

@tc.post_op
def average(x: tc.tensor.Tensor) -> tc.String:
    avg = x.sum() / x.size
    return avg.cast(tc.String)  # explicitly cast `avg` to a `String`

if __name__ == "__main__":
    # ...
    actual = HOST.post(ENDPOINT, cxt)
    assert actual == "1"  # assert that the result is in fact a string

```
