Documentation/Reference

Recursive type metadata

The maintained compiler exposes a finite graph of declared types, without constructing application values. Use an OPTIONAL witness containing NONE, including for records with required fields and resource-owning types.

TYPE Node
    Name AS STRING
    Children AS LIST OF Node=[]
END TYPE
DIM witness AS OPTIONAL OF Node=NONE
graph=JsonTypeSchema(witness)
root=graph.Types[graph.Root]
PRINT root.Name
FOR EACH field IN root.Fields
    PRINT field.Name; ":"; graph.Types[field.DeclaredType].Name
NEXT
Query Mapping
TypeSchema(witness) Physical BASIL fields/default presence, without serialization overrides
SerializationTypeSchema(witness) Shared SERIALIZE rules and type/field converters
JsonTypeSchema(witness) Effective declaration-default JSON rules, including shared fallbacks
FormatMetadata(graph,node[,field]) AS OPTIONAL OF M Independent typed FORMAT options or NONE; invalid node fails with JsonError

A TypeGraph has readonly public Root and Types fields. Types is a list of TypeDescriptor. References index that graph; -1 means no reference, including a unit choice alternative. Repeated/recursive references share nodes. IDs have no meaning in another graph. Opaque Identity strings distinguish nominal types and instantiated arguments; compare them as whole strings, without parsing them or using them as a persisted versioned schema identifier. Name is a display label, not a lookup key; generic records retain their base name and expose TypeArguments.

TypeDescriptor fields Meaning
Identity, Name, Kind Nominal/structural identity, display name, TypeKind
Fields Declaration-ordered ordinary fields, including flattened base fields
ElementType List/array/set element or optional payload reference
KeyType, ValueType Dictionary key/value references
Members Enum Name and signed 64-bit Value
Alternatives Choice Name and ValueType (-1 for a unit alternative)
TypeArguments Bound ordinary-record generic argument references, including phantom arguments
IsOpaque, IsResource Representation privacy and checked ownership classification
WireSupported, UnsupportedReason, WireRepresentation Standard wire-shape facts (before root converters) and explicit unsupported/dynamic reasons
EncodeType, DecodeType, HasEncoder, HasDecoder Declared converter representation references and presence, independently by direction
BitWidth, IsUnsigned Integer/DOUBLE numeric representation; width 0 when not a numeric scalar

TypeKind cases are Unsupported, BooleanValue, IntegerValue, DoubleValue, ByteValue, UInt32Value, UInt64Value, StringValue, RecordValue, ListValue, ArrayValue, DictionaryValue, SetValue, OptionalValue, EnumValue, ChoiceValue, DynamicJsonValue, ResourceValue, FunctionValue and PointerValue. Native resources/callables/pointers, dynamic JsonValue, and unclassified categories explicitly lack a static wire shape. Resource records/containers remain nonserializable. A NONE witness never acquires, copies, transfers or destroys an application resource.

TypeFieldInfo contains Name (physical), WireName, DeclaredType, EncodeType, DecodeType, IsOptional, HasDefault, ReadOnly, WriteOnly, Excluded, OmitNone, HasEncoder and HasDecoder. Optionality and ownership reflect bound generic arguments. HasDefault means an explicit TYPE field default; an optional without one can still decode missing input as NONE. The access flags refer to wire directions, not BASIL mutability.

To interpret a wire view, select the direction’s root converter when present, then visit its representation reference with that representation’s root converter bypassed once, matching the codec. Field HasEncoder/HasDecoder distinguishes an explicit field override, including one returning the same declared type: visit that direction’s field representation with the same bypass. Without a field override, visit DeclaredType normally. Children of a bypassed representation use ordinary selection again. A traversal’s visited key therefore includes direction and bypass state, not only node ID; same-type normalization converters terminate.

WireSupported/WireRepresentation describe standard handling before root converters. Physical Fields remain facts even when a converter changes the wire shape. WireSupported=TRUE is not a transitive validation of the graph. After selecting/bypassing root rules, traverse relevant fields/elements/keys/values/cases and reject unsupported nodes, or interpret explicit user FORMAT schema options. Opaque nodes expose no fields/field annotations, even PUBLIC fields; their standard WireSupported remains FALSE. A selected declared converter can expose a known representation without exposing opaque storage. An encoder alone does not establish a decoder. A JsonValue representation stays explicitly dynamic, rather than being inferred from a sample converter result.

BASIL set equality does not guarantee unique JSON array items: exclusions or converters can map different members to identical JSON. SetValue retains the language kind; its wire representation is array without a uniqueness promise.

Standard wire representation labels are boolean/integer/number/string, object, array, nullable, object-encoded-keys, single-case-object and array-or-bounds-object. Choices use exactly one case-name property (a unit case has null). Nontext dictionary keys use the existing JSON key encoding, with the key type’s selected mapping. Zero-based vectors use arrays; other dense arrays use {"$bounds":[lower,upper,...],"$values":[...]}. Bounds and vector shape are value facts, so the type describes both accepted forms. Converter presence delegates to EncodeType/DecodeType as described above. These are codec protocols, not provider-specific schemas.

FORMAT queries use physical BASIL field names case-insensitively; empty/omitted field selects the type header. Different nominal option types stay distinct; missing annotations return NONE. Returned values and copied descriptor lists are independent. Graphs provide no mutable field access, unchecked value construction, raw addresses or runtime converter registry. Per-instance JsonCodec profiles are not arguments to these declaration-default queries; custom runtime mappings need an explicit schema supplied by their library.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.