My view is that in general people shouldn't use "raw" object variants in important code. They should either stick to some guidelines for using them (this is not a rare thing in code) or wrap around those guidelines with macros.
As an example of a guideline: each branch must have a single field, and there should be no fields outside of the variant fields. type ABCKind = enum a, b, c AB = object x, y: int C = object z: string ABC = object case kind: ABCKind of a, b: ab: AB of c: c: C Foo = object name: string abc: ABC # AB and C have automatically defined == # we can easily define == for ABC, we could even do this for generic object variants of this form if the type system allowed it: proc `==`(u, v: ABC): bool = if u.kind != v.kind: return false result = case u.kind of a, b: u.ab == v.ab of c: u.c == v.c # == for Foo is now defined as well Run It's more verbose but is it really any harder to work with than ADTs or OOP? How do you even define == for ADTs in user code?