Getting started
Add the package as a dependency — it has none of its own:
dependency "sparkles:reflection" path="../.."Classify a type
import sparkles.reflection.kind : TypeKind, typeKindOf;
static assert(typeKindOf!int == TypeKind.signedInteger);
static assert(typeKindOf!string == TypeKind.text);
static assert(typeKindOf!(int[]) == TypeKind.sequence);typeKindOf is the one total ladder every consumer shares: enums before integrals, strings before arrays, SumType before aggregates. The scalar kinds (isScalarKind) are exactly the leaves a value writer or query sink can consume directly.
Discover members
import sparkles.reflection.member : fieldCount, fieldIdentifier,
propertyGetters, valueLikeGetter;
static assert(fieldCount!MyStruct == 2);
static assert(fieldIdentifier!(MyStruct, 0) == "alpha");The field primitives are indexed into T.tupleof — the same spine a runtime walk reads — so a field's compile-time symbol, type, and identifier always agree with the value a consumer hands out. propertyGetters!T lists the public zero-argument @property getters (one per member name, setters and private overloads excluded), and valueLikeGetter!T states the value-like wrapper rule: a fieldless type whose single public @property returns a scalar is that value.
Build your own walk
The kernel deliberately ships no visitor: each consumer's traversal has its own shape (the property tree prunes by disclosure at runtime; a query schema enumerates every SumType alternative at compile time; a writer never recurses at all). A direct static if ladder over typeKindOf, recursing through fieldCount/fieldType and propertyGetters, stays close to the consumer's policy and instantiates nothing it does not use — sparkles.dql.schema and sparkles.dql.resolve are the reference pair, two mirrored ladders over one conventions module.