For more advanced use cases, see the documentation.
For the Python API documentation, see https://tinychain.readthedocs.io/en/latest/
Hello, World!
TinyChain has one of the simplest imaginable "Hello, World!" programs— just open this link in your browser: http://demo.tinychain.net/state/scalar/value/string?key="Hello, World!"Op
:
GET
, PUT
, POST
, and DELETE
.
These correspond to
HTTP methods
.
Every TinyChain Op
is accessible over HTTP using the corresponding request method.
String
in its classpath:
/state/scalar/string
. So, String
is a subclass of
Value
, which is a subclass of Scalar
, which is a subclass of
State
, which is the parent of every TinyChain class. Any TinyChain Value
can be constructed using by GET
ting its class.
Run a TinyChain host
The TinyChain host at demo.tinychain.net is provided for convenience, but it may be slow due to the volume of requests it receives, and it may not always have the latest updates and bugfixes. For the best experience, you should run your own host. This is easy to do with Docker:
# the "docker build" command will output an image ID like "c0c251e99ef1"
docker build https://github.com/haydnv/tinychain.git
# paste the image ID into this command
docker run -it -p 8702:8702/tcp IMAGE_ID ./tinychain --address=0.0.0.0
Check that it's working by running your own "Hello, World!" program:
http://127.0.0.1:8702/state/scalar/value/string?key="Hello, World!"
Construct a TinyChain compute graph
TinyChain's graph computing model was inspired by TensorFlow v1. The easiest way to try it out is to first install the Python client:
# on very new systems, the command may be "pip" instead of "pip3"
pip3 install tinychain
Let's now try writing our own function:
# new file: test.py
import tinychain as tc
# this is the local TinyChain host you just started with "docker run..."
HOST = tc.host.Host("http://127.0.0.1:8702")
# this endpoint will attempt to run whatever program you send it
# without committing any write operations
ENDPOINT = "/transact/hypothetical"
# define a GET Op
# the type annotations are important!
# without them, TinyChain doesn't know what type to expect the arguments to be
@tc.get_op
def hello(name: tc.String) -> tc.String:
return tc.String("Hello, {{name}}").render(name=name)
if __name__ == "__main__":
cxt = tc.Context() # construct a new execution context
cxt.hello = hello # include our function definition in the context
cxt.result = cxt.hello("Name") # call the function
print(HOST.post(ENDPOINT, cxt)) # evaluate the context on the host
Check that it works as expected:
$ python3 test.py
Hello, Name
This requires some scaffolding in order to set up but it's a very convenient starting point
for the more advanced use cases covered in the documentation.
If you're curious about how this works under the hood, you can inspect the JSON that gets sent
to the host:
# ...
tc.print_json(cxt)
You should see something like:
[
[
"hello",
{
"/state/scalar/op/get": [
"name",
[
[
"String_7f8cf85223d0",
"Hello, {{name}}"
],
[
"_return",
{
"$String_7f8cf85223d0/render": {
"name": {
"$name": []
}
}
}
]
]
]
}
],
[
"result",
{
"$hello": [
"Name"
]
}
]
]
In general you shouldn't have to worry about what the generated JSON looks like, but it can be
convenient to give explicit names to the internal states of an Op
. You can do this by
using the cxt
or txn
keyword—just like Python treats the "self"
keyword specially in a method declaration, assigning it a reference to the method's instance,
TinyChain treats the cxt
or txn
keyword specially, assigning it a
reference to the Op
's execution context. For example:
# ...
@tc.get_op
def hello(cxt, name):
cxt.template = tc.String("Hello, {{name}}")
return cxt.template.render(name=name)
# ...
If you again inspect the JSON representation of your compute graph, you'll see that the anonymous
"String..." name has been replaced by "template."