Contracts and ownership

Suppose we add a new image operation to an application. The native implementation is easy enough to call: include its header, pass an image and get a result. A script implementation needs a little adaptation, and the GPU version has its own ideas about where the pixels should live. Before long, the caller knows which implementation it has and carries a separate path for each one.

There are perfectly good reasons for those implementations to differ. The trouble starts when every consumer has to understand the differences as well. Adding a fourth provider then means revisiting all the places that learned about the first three.

TTX starts by asking what the consumer actually needs from that relationship. For convolution, it needs an agreed operation, a way to call it, and a way to use the resulting image. The provider should be able to answer those questions while keeping its algorithm and private storage to itself.

Agreeing on an interface

A name alone won't get us very far. Two libraries can both export convolve and still disagree on the arguments, boundary conditions or ownership of the result. TTX uses a UUID to identify the intended contract, so the consumer can ask for that particular meaning without relying on a coincidental spelling.

That still leaves the physical call. A function that receives an image pointer isn't interchangeable with one that receives an opaque receiver followed by an image pointer. Even if their API tables occupy the same number of bytes, calling through the wrong description can fail before the operation gets a chance to do anything useful.

The participants therefore also agree on the concrete representation of the API. It describes the fields and callable signatures the consumer expects to receive. Binding checks that agreement before supplying the record in the consumer's storage. A provider can recognize the UUID and still reject the requested API when the representations differ.

Sometimes we only need the first answer. A policy may want to know whether an object accepts a contract without using any of its operations. That's what supports asks. Keeping it separate from bind means we don't need to construct an interface merely to ask a question about its meaning.

Keeping the implementation available

Once we have the API record, its receiver still belongs to somebody. A script runtime might keep that state alive, a native module might own it, or the receiver might forward the work to another system. Copying a pointer into our own storage doesn't transfer any of those obligations.

The supplying contract must tell us how long the interface is usable and what keeps its state and code available. Within that lifetime, the consumer calls the agreed operations and lets the provider interpret its own receiver. There is no need for the consumer to recover a private C++ class to make the call work.

This gives the three TTX layers fairly concrete jobs. Data describes the physical form. Semantic identifies the contract and negotiates its interface. Concept exposes the policies and higher-order questions that make those interfaces useful. Each layer can do its work without trying to contain the entire problem.

Trying another provider

In the Godot lab, an image operation can come from GDScript, native C++ or CUDA. Acquiring the callable must keep the provider's state usable without copying its pixels just to obtain the interface. The configuration chooses which provider to load; the consumer uses the agreement that provider supplies.

If a replacement needs us to cast its private receiver or bypass its policy, we have found an assumption that the original boundary failed to express. That's a more useful result than teaching the consumer one more backend-specific trick. It tells us which agreement needs attention.

The Query contract is the small callable surface behind this negotiation. The next chapter follows what happens when the answer to one of those questions isn't simply yes.