nupp.reflect
Semantic type reflection and reflection-driven field codecs.
Call the namespace with a concrete type inside comptime to obtain an immutable, target-independent descriptor. fieldCodec materializes the stored fields of a reflected record into a keyed runtime codec.
The root's common facts are projected onto Info, so the usual inspection does not need to walk the graph:
local record User
id: integer
name: string = "anonymous"
end
const UserSummary: string = do
local info = nupp.reflect(User)
assert(info.kind == "record")
assert(info.fields[2].hasDefault)
return info.fields[1].name .. ":" .. info.fields[1].kind
end
assert(UserSummary == "id:integer")Use root, types, and the integer edges between them when a generator needs the complete semantic type rather than the root projection. The graph remains finite even when the declaration is recursive:
local record Node
value: string
next: Node?
end
const NextKind: string = do
local info = nupp.reflect(Node)
local edge = info.fields[2].type as integer
return info.types[edge].kind
end
assert(NextKind == "union")Module contents
Types
| Type | Kind | Description |
|---|---|---|
Annotation | record | A typed annotation application, retaining source order. |
AnnotationArgument | record | A checked member supplied to a reflected typed annotation. |
Entry | record | A named or positional edge in the indexed semantic type graph. |
ExtensionKey | interface | A typed identity for data derived lazily from a reflection descriptor, schema, or binding. |
Field | record | A visible field projected directly from the reflected root type. |
FieldCodec | record | A keyed runtime codec materialized from a reflected record. |
FieldCodecBlueprint | record | An opaque field-codec recipe returned inside comptime. |
Info | record | An immutable compile-time description of a resolved semantic type. |
Node | record | A node in an Info semantic type graph. |
SoAField | record | A top-level stored struct field available as a SoA column. |
SoAInfo | record | Target-independent semantic inputs to SoA storage derivation. |
Functions
| Function | Kind | Description |
|---|---|---|
extensionKey | function | Creates an identity for a lazily derived metadata value. |
fieldCodec | comptime function | Builds a keyed field-codec recipe from a reflected record. |
runtime | function | Reads the semantic description carried by a first-class type witness. |
runtimeType | function | Reads an available nominal witness reachable through stored fields or constructor parameters. |
Types#
Annotationrecord#
record Annotation ...A typed annotation application, retaining source order.
AnnotationArgumentrecord#
record AnnotationArgument ...A checked member supplied to a reflected typed annotation.
value is present for kind = "value"; type is an index into the owning Info.types graph for kind = "type". kind = "nil" preserves an explicitly supplied nil without pretending an absent table field contains it.
(targets = {"field"})
local record serialized
name: string?
codec: any
end
local record StringCodec
end
local record User
(name = "user_id", codec = StringCodec)
id: integer
end
const Serialization: string = do
local info = nupp.reflect(User)
local arguments = info.fields[1].annotations[1].arguments
assert(arguments[1].kind == "value")
assert(arguments[2].kind == "type")
local codec = info.types[arguments[2].type as integer]
return (arguments[1].value as string) .. ":" .. (codec.name as string)
end
assert(Serialization == "user_id:StringCodec")Entryrecord#
record Entry ...A named or positional edge in the indexed semantic type graph.
Members
| Name | Kind | Description |
|---|---|---|
name | field | |
type | field | |
read | field | |
write | field | |
stored | field | |
readable | field | |
writable | field | |
hasDefault | field | |
defaultValue | field | |
mode | field | |
bound | field | |
answer | field | |
default | field | |
annotations | field |
ExtensionKeyinterface#
interface ExtensionKey<T> ...A typed identity for data derived lazily from a reflection descriptor, schema, or binding.
It has the shape of nupp.util.Key<T>, declared here because the prelude is read before any module is. _valueType is never present; it mentions T in both positions so that keys are invariant.
Type parameters
| Name | Description |
|---|---|
T |
Fieldrecord#
record Field ...A visible field projected directly from the reflected root type.
The projection preserves declaration order, access capabilities, defaults, and annotations without requiring a lookup in Info.types. stored distinguishes stored fields, including callbacks, from methods.
local record Options
format: string = "compact"
retries: integer
end
const DefaultFormat: string = do
local field = nupp.reflect(Options).fields[1]
assert(field.name == "format" and field.hasDefault)
assert(field.readable and field.writable)
return field.defaultValue as string
end
assert(DefaultFormat == "compact")Members
| Name | Kind | Description |
|---|---|---|
name | field | |
kind | field | |
typeName | field | |
type | field | |
stored | field | |
readable | field | |
writable | field | |
hasDefault | field | |
defaultValue | field | |
annotations | field |
FieldCodecrecord#
record FieldCodec<T> ...A keyed runtime codec materialized from a reflected record.
encode copies present declared fields into a plain keyed table. decode selects the same fields from a keyed table and restores the record's runtime identity. Both operations are shallow. The stable fingerprint records the field names in declaration order.
local record Position
x: number
y: number
end
const PositionCodec: nupp.reflect.FieldCodec<Position> = do
return nupp.reflect.fieldCodec(nupp.reflect(Position))
end
local encoded = PositionCodec:encode(new Position(x = 3, y = 4))
assert(encoded.x == 3 and encoded.y == 4)
assert(PositionCodec.fingerprint == "t:x,y")Type parameters
| Name | Description |
|---|---|
T |
Members
| Name | Kind | Description |
|---|---|---|
encode | method | |
decode | method | |
fingerprint | field |
encodemethod#
encode: function(self, value: T): {[string]: any}Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
value | T |
Returns
| Type | Description |
|---|---|
{[string]: any} |
decodemethod#
decode: function(self, value: {[string]: any}): (T?, string?)Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
value | {[string]: any} |
Returns
| Type | Description |
|---|---|
T? | |
string? |
fingerprintfield#
fingerprint: stringFieldCodecBlueprintrecord#
record FieldCodecBlueprintAn opaque field-codec recipe returned inside comptime.
Inforecord#
record Info ...An immutable compile-time description of a resolved semantic type.
Info can cross typed comptime helper boundaries. Its fingerprint is suitable for cache keys because it changes with the canonical type graph, defaults, and checked annotations rather than with compiler-local identities.
local record Pair
left: string
right: integer
end
local function describe(info: nupp.reflect.Info): string
return info.fingerprint .. ":" .. tostring(#info.fields)
end
const PairDescription: string = do
return describe(nupp.reflect(Pair))
end
assert(PairDescription:match(":2$") ~= nil)Members
| Name | Kind | Description |
|---|---|---|
schema | field | |
root | field | |
types | field | |
kind | field | |
name | field | |
qualifiedName | field | |
fields | field | |
annotations | field | |
soa | field | |
fingerprint | field |
Noderecord#
record Node ...A node in an Info semantic type graph.
Fields, parameters, results, union members, generic arguments, and wrappers point to other nodes by their integer index. Resolve only the edges relevant to the node's kind.
local type Handler = function(message: string, attempts: integer): boolean
const HandlerShape: string = do
local info = nupp.reflect(Handler)
local handler = info.types[info.root]
assert(handler.kind == "func")
local first = (handler.parameters as {nupp.reflect.Entry})[1]
local input = info.types[first.type as integer]
local output = info.types[(handler.returns as {integer})[1]]
return (first.name as string) .. ":" .. input.kind .. "->" .. output.kind
end
assert(HandlerShape == "message:string->boolean")Members
| Name | Kind | Description |
|---|---|---|
copyable | field | Whether a nominal value has no ownership obligation or scoped borrow. |
kind | field | |
name | field | |
nominal | field | |
annotations | field | |
fields | field | |
staticFields | field | |
metamethods | field | |
nestedTypes | field | |
associatedTypes | field | |
members | field | |
parameters | field | |
returns | field | |
typeParameters | field | |
typeBounds | field | |
packParameters | field | |
constParameters | field | |
parameterKinds | field | |
typeArguments | field | |
packArguments | field | |
constArguments | field | |
supertypes | field | |
element | field | |
body | field | |
of | field | |
origin | field | |
noReturn | field | |
noYield | field |
copyablefield#
copyable: boolean?Whether a nominal value has no ownership obligation or scoped borrow.
SoAFieldrecord#
record SoAField ...A top-level stored struct field available as a SoA column.
SoAInforecord#
record SoAInfo ...Target-independent semantic inputs to SoA storage derivation.
This says whether the declaration can be split into columns and identifies those columns. It deliberately does not contain target sizes or offsets; use nupp.mem.soa.layoutof for those.
Functions#
extensionKeyfunction#
local extensionKey: function<T>(build: function(host: any): T): ExtensionKey<T>Creates an identity for a lazily derived metadata value.
A descriptor, schema, or binding builds a key's value once, then returns the same value from its private cache. Key identities and their slot numbers are process-local and must not be persisted.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
build | function(host: any): T |
Returns
| Type | Description |
|---|---|
ExtensionKey<T> |
fieldCodeccomptime function#
local fieldCodec: function(info: Info): FieldCodecBlueprintBuilds a keyed field-codec recipe from a reflected record.
The opaque recipe must directly initialize a matching FieldCodec<Record> declaration. It may pass through typed comptime helpers on the way to that materialization boundary.
local record Message
id: integer
text: string
end
local function keyed(
info: nupp.reflect.Info
): nupp.reflect.FieldCodecBlueprint
return nupp.reflect.fieldCodec(info)
end
const MessageCodec: nupp.reflect.FieldCodec<Message> = do
return keyed(nupp.reflect(Message))
end
local encoded = MessageCodec:encode(new Message(id = 1, text = "hello"))
assert(encoded.id == 1 and encoded.text == "hello")Arguments
| Name | Type | Description |
|---|---|---|
info | Info |
Returns
| Type | Description |
|---|---|
FieldCodecBlueprint |
runtimefunction#
Reads the semantic description carried by a first-class type witness. Generic consumers use the same descriptor as concrete callers.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
typeObject | Type<T> |
Returns
| Type | Description |
|---|---|
Info |
runtimeTypefunction#
local runtimeType: function(info: Info, index: integer): Type<unknown>?Reads an available nominal witness reachable through stored fields or constructor parameters. An unresolved forward declaration returns nil.
Arguments
| Name | Type | Description |
|---|---|---|
info | Info | |
index | integer |
Returns
| Type | Description |
|---|---|
Type<unknown>? |