SHAPE

SHIFT

Back to homepage

Traits, Protocols, Interfaces

Different languages have different names for a similar idea:

Rust trait
Swift protocol
TypeScript interface
Java interface
Go interface
Haskell typeclass
C++ concept
Scala typeclass or trait

They all describe a contract, but they are not the same contract.

That difference matters for semantic merge and cross-language translation.

If the system sees only method names, it will overclaim. A method set is not enough to prove a trait, protocol, interface, typeclass, or concept survived a merge.

Contract MatrixSame Shape Is Not The Same Contract
SurfaceClaimRequired proofCurrent evidenceRoute
RequiredRequired membersThe target exposes the needed operationsRequirement set evidenceMethod names, parameter shape, return shapeGate member set
MissingAssociated typesType members are representedAssociated type or equivalent bindingItem, Error, Output, Self relationshipsRequest target proof
RequiredGeneric boundsConstraints still applyBound and where-clause evidenceT: Send, comparable, iterable, constraint clausesReview bounds
MissingDispatchCall resolution keeps meaningStatic/dynamic dispatch evidenceTrait object, virtual call, structural call siteProve dispatch
MissingCoherenceImplementations are still legalImplementation authority evidenceOrphan rule, blanket impl, extension methodFail closed
A protocol-like construct is a bundle of obligations. Preserving one obligation does not prove the rest.

Names Are Not Contracts

Imagine a source trait:

trait Cache {
  type Item;
  fn get(&self, key: &str) -> Option<Self::Item>;
}

It can be lowered into something that looks like a TypeScript interface:

interface Cache<Item> {
  get(key: string): Item | undefined;
}

The shape is close.

But the proof is not finished.

What happened to Self::Item? Is undefined the same absence convention as Option? Are implementations structural or nominal? Can any object satisfy the interface, or must an implementation be declared? Does dispatch happen statically, dynamically, or structurally?

Those are not philosophical questions. They decide whether a merge can be admitted.

A Contract Has More Than Members
1trait Cache {2  type Item;3  fn get(&self, key: &str) -> Option<Self::Item>;4}
Source contractThe trait includes a required method and an associated type tied to Self.
The target code can be useful while still carrying explicit non-claims about full protocol equivalence.

Obligations Travel Separately

A protocol-like contract is not one fact.

It is a set of obligations:

identity of the contract
required members
associated types or type members
generic parameters
where clauses and bounds
default methods
extension methods
implementation declarations
dynamic or static dispatch
coherence and orphan rules
object-safety or existential behavior

Some obligations translate cleanly.

Some need an adapter.

Some cannot be represented in the target language without runtime checks or conventions.

That is why a universal merge system should not store “interface preserved: true.”

It should store the obligations.

Dispatch Is Meaning

Dispatch is one of the easiest places to overclaim.

static dispatch
dynamic dispatch
structural dispatch
virtual dispatch
extension method lookup
blanket implementation
conditional implementation

Two outputs can have the same method call text and different dispatch meaning.

A target might call a structurally compatible object where the source required an explicit implementation. Or a source trait object may allow dynamic dispatch where the target generic erases it.

That is not automatically bad.

It is a lowering decision that needs to be named.

Contract Route TableProtocol Evidence Routes
RouteSignalRequiresProduces
Admit member shapeRequired methods matchMember names, parameters, return shapeScoped static claim
Review type membersAssociated type became generic parameterMapping and use-site proofTarget contract decision
Probe dispatchDynamic behavior may differRuntime or call-resolution evidenceBounded dispatch claim
Block coherenceImplementation authority is missingOrphan/coherence or extension proofNon-admission
Protocol evidence routes each obligation separately instead of forcing one yes/no contract claim.

Structural Is Not Weaker By Default

TypeScript interfaces are structural. Rust traits are explicit. Go interfaces are structural but method-set based. Swift protocols have conformance rules. Haskell typeclasses have instance resolution.

No one model is universally “more semantic.”

They express different commitments.

Semantic merge should respect the source commitment and the target commitment. If a target weakens a nominal requirement into structural compatibility, the system should say so. If a target strengthens a structural shape into an explicit implementation, the system should say so.

The evidence is useful precisely because it refuses to flatten these differences into one field called interface.

The Mental Model

Traits, protocols, interfaces, typeclasses, and concepts are contract surfaces.

They are made of obligations.

Semantic merge should preserve or route each obligation:

member set
type members
bounds
dispatch
implementation authority
coherence
runtime behavior

If those obligations are represented, the candidate can move forward.

If they are missing, the system should not guess from matching names.