Interfaces and combinators
The naming library has the same interfaces in C#, Python and TypeScript.
C# names are shown; Python uses snake_case and TypeScript camelCase.
Interfaces
| Interface | Responsibility |
|---|---|
ISegmentClass |
A set of values one segment may take (vocab, identifier, pair, guid, interval, token, any). IsValid(value), MayOverlap(other). |
Pattern |
An invertible pattern over dot-separated segments: Render(bindings) builds a name, TryMatch(name, out bindings) parses it back, Shapes() describes it for overlap checks, Bind(name, value) fixes a parameter. |
Bindings |
An immutable map from parameter name to segment value. Typed values (World, Scenario, CurrencyPair, Interval, Venue, Param) implement IBindingSource and add their parameters. |
Entity |
Scope, path, identity, entity type and events. Derives Topic, Key, IndexKey, Subject, CeType and parses subjects. Bind specializes it. |
FragmentDocument |
One YAML fragment as read from disk or an embedded resource. |
NameRegistry |
The composition of fragments. Compose, With, Load; GetEntity, GetVenue, GetWorker; TryParseTopic, TryParseKey. Rejects any two families that could produce the same name. |
Combinators
Every pattern is built from these. In YAML fragments, the same combinators are written as lists and single-key mappings (shown in the last column).
| Combinator | Signature | Meaning | Example → rendered | YAML |
|---|---|---|---|---|
Lit |
string → Pattern |
a fixed segment | Lit("exchange") → exchange |
exchange |
Lits |
string[] → Pattern |
fixed segments | Lits("sys", "api") → sys.api |
[sys, api] |
Seg |
(class, name) → Pattern |
one typed segment bound to a parameter | Seg(Pair, "pair") with pair=BTC_USDT → BTC_USDT |
{pair: pair} |
Then / Seq |
Pattern × Pattern → Pattern |
sequence | Lit("a").Then(Seg(Vocab,"b")) with b=x → a.x |
[a, {vocab: b}] |
OneOf |
(selector, (name, Pattern)…) → Pattern |
named choice; the selector parameter picks the alternative | World with world_kind=hyp, world=X → hyp.X |
{oneOf: {selector: world_kind, alternatives: {act: [act], hyp: [hyp, {identifier: world}]}}} |
Optional |
Pattern → Pattern |
zero or one; rendered when all its parameters are bound | Lit("a").Then(Seg(Pair,"p").Optional()) with nothing bound → a |
{optional: {pair: p}} |
Bind |
Pattern × (name, value) → Pattern |
partial application: the parameter becomes a literal | Book.Bind("venue", "alpaca") → ….exchange.alpaca.orderbook.… |
bind: {…, with: {venue: alpaca}} |
ref |
– | reuse a named pattern, local or from an imported fragment | – | {ref: virtufin.worlds/world} |
Entity |
(scope, path, identity, type, events) → Entity |
packages a stem and an identity | see Fragments | entities: {…} |
Entity.Bind |
Entity × (name, value) → Entity |
a more specific entity; it refines the original | generic order book → Alpaca order book | bind: {orderbook: {entity: …, with: {venue: alpaca}}} |
Names.DeadLetter |
topic → topic |
the dlq. wrapper |
dlq.sc.LIVE.trading.order.submitted |
– |
Names.Experimental |
segments → name |
the x. wrapper |
x.demo.events |
– |
A parameter value of the form {name} (for example {worker_id}) renders
and matches as itself, so a template such as
wrk.httpgetworker.command.{worker_id} can be stored and substituted later
by WorkManager.
Laws
Property tests check these in all three languages, against the shared
vectors in lib/naming/conformance/vectors.yaml:
| Law | Statement |
|---|---|
| Round trip | Match(Render(b)) == b and Render(Match(n)) == n |
| Associativity | (a.Then(b)).Then(c) and a.Then(b.Then(c)) render and match identically |
| Unit | Empty.Then(p) == p == p.Then(Empty) |
| Disjoint choice | the alternatives of a OneOf never match the same name; construction fails otherwise |
| No ambiguity | in a composed registry, no two families match the same topic or key, unless one refines the other (then the more specific wins when parsing) |
| Case separation | vocab, guid, interval, token (lowercase) never overlap identifier, pair (uppercase) |