Skip to content

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)