Anatomy of an Op
Understand when and how to use Contexts and type annotations
Type annotations
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.
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:
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 callingtc.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?
Automatic concurrency
Let's take another look at the example
function:
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:
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.
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.
Last updated