SHAPE

SHIFT

Back to homepage

Semantic Adapters Make Languages Interoperable

Programming languages already interoperate.

They call each other through ABIs, RPC, bindings, generated clients, shared databases, message queues, plug-in systems, package managers, and runtime embeddings.

The problem is not that languages cannot communicate.

The problem is that most communication happens through narrow technical edges while the meaning stays private.

Rust exposes bytes.
TypeScript exposes a wrapper.
Swift exposes a client.
Python exposes a script.
 
None of those edges automatically explain identity, ownership, effects, permissions, runtime assumptions, or proof.

A semantic interlingua changes the shape of the edge.

It makes the adapter itself a first-class object.

Interop LayerChecked boundary
Semantic Adapter
Contract
capability, action, data, effect, resourceThe shared meaning every language is trying to attach to.
Source side
native language factsRust may prove ownership. TypeScript may prove structural shape. CSS may prove cascade targets.
Adapter
mapping, shim, binding, transport, projectionThe adapter says how the native fact crosses the boundary.
Proof
type gate, runtime probe, span map, loss recordThe boundary is trusted only as far as the proof reaches.
Loss
explicit and queryableIf a target cannot preserve a constraint, that is recorded instead of hidden.
A semantic adapter is not just glue code. It is a checked claim that one language surface satisfies a shared contract.

Glue Hides The Contract

Normal interoperability starts with mechanics:

Can the caller invoke this function?
Can the runtime load this module?
Can this object serialize across the wire?
Can this generated client compile?

Those are real requirements, but they are not enough.

Imagine a search system.

The index is written in Rust. The product shell is TypeScript. A native client is SwiftUI. A worker script is Python. The browser surface is HTML and CSS.

The old shape is pairwise glue:

Rust -> Node binding
Node binding -> TypeScript wrapper
TypeScript wrapper -> API route
API route -> Swift client
API route -> Python job
API route -> browser UI

Every edge invents its own private contract.

The semantic shape is different:

Languages do not need to translate through each other. They can lift into a shared semantic layer and lower through checked adapters.

The contract becomes the meeting point.

The adapter is where each language proves how it participates.

The Adapter Has Obligations

If a language adapter is only a generated file, the system can ask only shallow questions:

did generation finish?
did the file compile?
did the wrapper call the right endpoint?

A semantic adapter can answer stronger questions:

which capability is implemented?
which source symbol provides it?
which target symbol exposes it?
which effects can cross the boundary?
which resource is read or mutated?
which constraints are preserved natively?
which constraints are preserved by runtime proof?
which constraints are lost?
One Contract, Several Adapters
1capability Search @id("cap_search") {2  action query3  input SearchQuery4  returns SearchResults5  reads SearchCorpus.documents6  effects cpu.read, storage.read7}8 9adapter RustSearch @id("adapter_rust_search") {10  implements cap_search.query11  source symbol:rust/SearchIndex.query12  preserves ownership, input_shape, output_shape13  proves type_gate, fixture_gate14}
Shared contractThe capability names the action, input, output, resources, and effects before any target language is chosen.
The useful boundary is not Rust-to-TypeScript or TypeScript-to-Swift. It is each language binding to the same semantic capability through an adapter with evidence.

Interoperability Is Not Equivalence

The strongest version of this idea is not pretending every language can express every other language perfectly.

It is the opposite.

Languages become interoperable when the system can say exactly where they are not equivalent.

Interop MatrixWhat The Adapter Must Explain
SurfaceClaimRequired proofCurrent evidenceRoute
ProvedIdentityBoth sides refer to the same capabilityStable ids and source spanscap_search.query maps to RustSearch.query and TS queryPreserve
ProvedData shapeInput and output contracts surviveType or schema gateSearchQuery and SearchResults compare cleanlyGate
RequiredOwnershipRust aliasing rules remain validNative compiler and adapter boundaryOwnership preserved inside Rust providerPreserve locally
RequiredEffectsOnly declared resources cross the boundaryEffect manifest or runtime traceReads SearchCorpus.documentsProbe
MissingRuntime behaviorThe projected UI still behaves correctlyPreview or component probeFocus, loading, error, layout, accessibility snapshotsRun proof
A language boundary is trustworthy when each preserved or missing fact has a named proof route.

That makes interoperability less magical and more useful.

The system can route the boundary:

Adapter RoutesInterop Is Admission
RouteSignalRequiresProduces
AcceptContract, adapter, and proof agreeStable ids, type proof, effect boundary, current sourceAdapter becomes callable
WrapTarget lacks a native featureExplicit shim and loss recordAdapter with known compensation
ProbeMeaning is runtime-dependentBrowser, preview, fixture, trace, or integration proofRuntime-bound evidence
SplitOne adapter hides several contractsSeparate capabilities or resourcesSmaller interoperable edges
BlockRequired meaning cannot be preservedRecorded non-equivalenceNo unsafe boundary
Interoperability becomes an admission problem: each boundary is accepted, wrapped, probed, split, or blocked.

Many Languages, One System

Once adapters are semantic, a system can stop treating language boundaries as hard product boundaries.

A workflow can be authored once and projected into different runtimes.

A UI component can expose a capability contract that a web shell, native shell, and automation shell can all bind to differently.

A Rust module can provide a fast implementation without forcing the rest of the product to understand Rust.

A TypeScript SDK can expose a friendly developer surface while still carrying the stronger ownership and effect claims from the provider.

A Python automation can consume the same contract without becoming a second source of truth.

The languages stay different.
The contract stays shared.
The adapters carry proof.
The losses stay visible.

That is the important shift.

Interoperability is not a universal compiler that erases language differences.

It is a semantic coordination layer that lets languages keep their strengths while participating in one larger system.

Why This Matters

Most software becomes harder to change as it grows because every boundary accumulates private assumptions.

The API route assumes something about the database. The generated client assumes something about the server. The native app assumes something about the web product. The worker assumes something about the queue. The docs assume something about all of them.

Semantic adapters make those assumptions inspectable.

They turn interoperability into a set of durable questions:

what is the shared contract?
which language owns which part?
what did this adapter preserve?
what did it compensate for?
what proof admits it?
what would break if the contract moved?

That is why the interlingua matters.

It is useful for semantic merging, but it is bigger than merge.

It gives different programming languages a common place to meet without pretending they are the same language.