RFC-033: ^^ Reflection Operator
References:
Summary
This RFC proposes introducing the ^^ operator as the reflection entry point for obtaining metadata of types and values. ^^T returns the static metadata object of type T, and ^^obj returns the dynamic type metadata of value obj. The metadata object is a regular record type containing information such as name, parameters, and fields, and can be used both at compile-time and runtime.
Motivation
Why is this feature needed?
- Serialization/Deserialization: Needs to access type's field information to automatically generate serialization code
- Compile-time metaprogramming: Needs to access type structure at compile-time to generate code or verify constraints
- Runtime debugging/tools: Needs to print type information at runtime to assist debugging
- Runtime type checking: Needs to determine type relationships at runtime, such as "what type is obj?"
Current Problems
Currently, YaoXiang has no reflection mechanism, and it is impossible to access type metadata at compile-time or runtime. If we directly use .name, .fields to access type metadata, it will conflict with user-defined fields:
Person: Type = { name: String, age: Int }
# If Person.name refers to the type metadata name, or the field name?
# This would lead to parsing difficulties and semantic confusionA syntax that does not intrude into the regular field namespace is needed to access type metadata.
Proposal
Core Design
Introduce the ^^ operator as the reflection entry point to clearly distinguish regular code from metadata queries.
Two usages:
- Static reflection (applies to types):
^^Treturns the static metadata object of typeT - Dynamic reflection (applies to values):
^^objreturns the dynamic type metadata of valueobj
Metadata structure:
TypeMeta: Type = {
name: String,
params: Array(ParamMeta),
fields: Array(FieldMeta),
return_type: Type,
refinement: Option(Expr) # Some(Expr) at compile-time, None at runtime
}
ParamMeta: Type = {
name: String,
type: Type
}
FieldMeta: Type = {
name: String,
type: Type
}Universe levels: If T: Type_n, then ^^T: Type_{n+1}, conforming to standard type-theoretic universe lifting rules.
Precedence: ^^ is a unary prefix operator with the highest precedence. ^^T.name is equivalent to (^^T).name.
Examples
Basic Usage
Point: Type = { x: Float, y: Float }
# Static reflection
meta = ^^Point
print(meta.name) # "Point"
print(meta.fields.len) # 2
print(meta.fields[0].name) # "x"
print(fields[0].type) # Float
# Dynamic reflection (requires runtime reflection to be enabled)
obj = Point(1.0, 2.0)
meta = ^^obj
print(meta.name) # "Point"Generic Types
List: (T: Type) -> Type = { data: Array(T), length: Int }
# Reflect on the generic type itself
meta = ^^List
print(meta.name) # "List"
print(meta.params) # [{ name: "T", type: Type }]
# Reflect on a specific instantiation
meta = ^^List(Int)
print(meta.name) # "List(Int)"
print(meta.params) # []Functions
add: (a: Int, b: Int) -> Int = a + b
meta = ^^add
print(meta.name) # "add"
print(meta.params) # [{ name: "a", type: Int }, { name: "b", type: Int }]
print(meta.return_type) # IntRefinement Types
Positive: (x: Int) -> Type = { x > 0 }
# At compile-time: refinement is Some(Expr)
meta = ^^Positive
print(meta.name) # "Positive"
print(meta.refinement) # Some(AST(x > 0))
# At runtime: refinement is None (erased)Usage in Compile-time Predicates
# Check if a type has fields
HasFields: (T: Type) -> Type = { ^^T.fields.len > 0 }
# Check field types
HasFloatField: (T: Type) -> Type = {
exists field in ^^T.fields: field.type == Float
}
# Usage
obj: HasFields(Point) = Point(1.0, 2.0) # ✅ Validation passed
# obj: HasFields(Int) = 42 # ❌ Validation failedSerialization Example
# Compile-time pure function: generate JSON string
to_json: (T: Type) -> ((obj: T) -> String) = {
meta = ^^T
parts: Array(String) = []
for field in meta.fields {
# Generate field access code at compile-time
parts.push("\"${field.name}\": ${obj.${field.name}}")
}
return "{" + parts.join(", ") + "}"
}
# Usage
point_to_json = to_json(Point)
print(point_to_json(Point(1.0, 2.0))) # '{"x": 1.0, "y": 2.0}'Syntax Changes
| Before | After |
|---|---|
| No reflection mechanism | ^^T to get type metadata |
| No reflection mechanism | ^^obj to get value's dynamic type metadata |
Detailed Design
Type System Impact
- New types:
TypeMeta,ParamMeta,FieldMeta - Universe levels: The type returned by
^^Tis one level higher thanT - Generic interaction: Both
^^Listand^^List(Int)are supported - Function interaction:
^^addreturns function metadata (including parameters and return type) - Refinement type interaction:
^^Positivereturns refinement type metadata (including refinement expression)
Runtime Behavior
Compile-time reflection:
^^Tis fully evaluated at compile-time, and the result is inlined as a constant- Refinement expressions are available at compile-time
Runtime reflection:
- Disabled by default, zero overhead
- Enabled via the
--enable-runtime-reflectioncompilation option - When enabled,
^^objreturns dynamic type metadata - Refinement expressions are erased to
Noneat runtime
On-demand generation + treeshake:
- Metadata is generated only for types that actually use
^^ - Types that are not referenced do not generate metadata (treeshake)
Compiler Changes
- Lexer: Recognize
^^as a single token - Parser: Add
^^prefix expression rule - Type system: Add
TypeMeta,ParamMeta,FieldMetatype definitions - Type checker: Generate a metadata instance for each type
- Compile-time evaluator: Support compile-time evaluation of
^^T - Runtime (optional): Generate RTTI for reflected types
Backward Compatibility
- ✅ No impact on existing syntax:
^^is a new operator and does not conflict with existing syntax - ✅ No impact on existing types: All types automatically support
^^ - ✅ No impact on existing functions: Functions can use
^^but are not required to - ✅ No impact on compile-time predicates:
^^Tbehaves the same as regular content in predicates - ✅ No impact on runtime: Runtime reflection is disabled by default, zero overhead
Trade-offs
Advantages
- Unified treatment: Functions, generics, and refinement types are handled uniformly
- Zero overhead: Compile-time reflection is completely erased, runtime reflection is optional
- Integration with existing systems: Seamless integration with compile-time predicates (RFC-027)
- Concise:
^^is a pure symbol and does not conflict with user-defined identifiers - On-demand generation: treeshake optimization, zero overhead for unused types
Disadvantages
- Learning curve: Need to understand the semantics of
^^and the metadata structure - Runtime overhead: Enabling runtime reflection increases memory overhead (one pointer per instance)
- Implementation complexity: Multiple compiler components need to be modified
Alternatives
| Alternative | Why not chosen |
|---|---|
reflect(T) function | Would introduce an extra identifier into the scope, which could be shadowed by users |
type_info(T) function | Same as above |
Single ^ operator | May conflict with bitwise operations, and C++26 chose ^^ precisely because of such conflicts |
@@, ##, etc. symbols | No precedent, less intuitive to explain than ^^ |
Implementation Phases
| Phase | Content | Dependencies |
|---|---|---|
| Phase 1 | Compile-time ^^ operator parsing | None |
| Phase 2 | TypeMeta data structure definition | Phase 1 |
| Phase 3 | Compile-time metadata generation | Phase 2 |
| Phase 4 | Runtime reflection support (optional) | Phase 3 |
| Phase 5 | Compile-time predicate integration | Phase 3 |
Dependency Graph
Phase 1 (Parsing)
↓
Phase 2 (Data Structures)
↓
Phase 3 (Compile-time Metadata)
↓
├────────────┐
↓ ↓
Phase 4 Phase 5
(Runtime Reflection) (Compile-time Predicates)Risks
- Parsing conflicts:
^^may conflict with existing syntax (analysis shows no conflict) - Performance impact: Compile-time metadata generation may increase compilation time (can be optimized via treeshake)
- Runtime overhead: Enabling runtime reflection increases memory overhead (mitigated by on-demand generation)
Open Questions
- [x]
^^scope: Applies only to types and values, not to expressions - [x] Chained access: Supported, metadata objects returned by
^^Tcan have their properties accessed normally - [x] Pattern matching: Supported,
TypeMetais a regular record type and can be pattern-matched normally - [x] Comparison: Supported, metadata objects of the same type are equal
- [x] Memory overhead: On-demand generation + treeshake optimization
Appendix
Appendix A: Design Decision Record
| Decision | Decision | Date | Recorder |
|---|---|---|---|
^^ scope | Applies only to types and values, not expressions | 2026-06-16 | Chenxu |
| Chained access | Supported | 2026-06-16 | Chenxu |
| Pattern matching | Supported | 2026-06-16 | Chenxu |
| Comparison | Supported, same-type metadata is equal | 2026-06-16 | Chenxu |
| Memory overhead | On-demand generation + treeshake | 2026-06-16 | Chenxu |
| Generic interaction | Both ^^List and ^^List(Int) are supported | 2026-06-16 | Chenxu |
| Refinement expression storage | Available at compile-time, erased to None at runtime | 2026-06-16 | Chenxu |
Appendix B: Glossary
| Term | Definition |
|---|---|
| Reflection | The ability to access type metadata at runtime or compile-time |
| Metadata | Information describing a type's structure (name, fields, parameters, etc.) |
| RTTI | Run-Time Type Information |
| treeshake | Compiler optimization that removes unused code |
| Refinement type | A type with constraint conditions, e.g., Positive: (x: Int) -> Type = { x > 0 } |
References
- RFC-010: Unified Type Syntax
- RFC-011: Generic Type System Design
- RFC-027: Compile-time Predicates and Unified Static Verification
- RFC-011a: Interface Implementation and Dynamic Dispatch
- C++26 Reflection Proposal
Lifecycle and Destination
┌─────────────┐
│ Draft │ ← Current status
└──────┬──────┘
│
▼
┌─────────────┐
│ Reviewing │ ← Open community discussion and feedback
└──────┬──────┘
│
├──────────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Accepted │ │ Rejected │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ accepted/ │ │ rfc/ │
│ (Formal Design) │ │ (Retained) │
└─────────────┘ └─────────────┘