Open Markets Initiative
All posts

15 June 2026Compiler

Traits, not types

A field is not an int. It is a width, an endianness, a signedness, and sometimes an implied decimal place. Modeling those independently is why the compiler can target five languages without special cases.

Open Markets Initiative · 5 min read

A field is not an int.

That sounds like pedantry until you try to generate a parser for thirty venues. A four-byte price on one feed is big-endian with two implied decimal places. On another it is little-endian with four. On a third it is four ASCII digits. All three would be typed int32 by a naive model, and all three need different code.

Decompose the field

Rather than a closed set of types, every field in the binary model carries a set of traits, each answering one question independently:

  • Size — how many bytes on the wire
  • Endian — byte order, where it applies
  • Signedness — whether the high bit is magnitude or sign
  • DecimalPlaces — the implied divisor for fixed-point values
  • Timestamp — the epoch and unit, when the field is temporal
  • Memory — how the value should be held once decoded

A field's generated code depends entirely on which traits it has. Nothing else about it matters. The generator does not branch on the venue, the protocol, or the message; it branches on traits.

Why this beats an enum of types

The type-enum approach fails in a specific and predictable way: every new venue introduces a combination nobody anticipated, and the enum grows a member. Int32BigEndianTwoDecimals becomes Int32BigEndianFourDecimals becomes Uint64LittleEndianNanosSinceMidnight, and the switch statement in every generator grows with it.

Traits are combinatorial instead of enumerated. Six independent traits describe a space that no enum could cover by hand, and adding a venue that uses an unusual combination requires no generator changes at all — the combination is already expressible.

The load-bearing constraint

Traits only work if they stay orthogonal. The moment a trait's meaning depends on another trait's value, the model has smuggled a type back in and the combinatorial property is gone.

This is the rule that gets tested most often, usually by a venue doing something genuinely strange. The answer is almost always a new trait rather than a special case in an existing one — a new question that can be asked of every field, including the ones that answer "not applicable."

Runtime rules

Traits describe fields that are fixed at compile time. Variable-length structures — repeating groups, optional blocks, payloads sized by an earlier field — are modeled separately as rules carrying dependencies: a count, a size, or a payload reference pointing at another element in the tree.

Those dependencies survive compilation and become the actual control flow in the generated parser. A repeating group is a loop because the model says it has a Count dependency, not because someone wrote a loop.

OMI is open source,
built with the community.

Everything OMI produces is free, MIT-licensed, and community-driven. Browse the full catalog, contribute a protocol, or just start building.