Skip to content

Reflection

Chloë Strainge edited this page Oct 14, 2026 · 1 revision

Types and properties

Every value has a .type. A type value has a .name, which names the type it represents. Asking a type value for .type gives Type.

let $numbers = [10, 20]
echo $numbers.type.name $numbers.value_type.name
echo Integer.name Integer.type.name
Array Integer
Integer Type

Exact type identity is useful for guards: a distinct type Height = Integer has type Height, rather than Integer. Broad constraints such as Number and any remain useful in annotations without changing identity tests.

Inspect an object

.properties returns [String: any]. Struct fields appear as values. Other values expose a value entry; collection values retain their collection shape. All values expose type, enums expose index, and available methods appear as bound function references. Inspecting a method does not execute it.

let $word = "moss"
let $members = $word.properties
let $length = $members["count"]
echo $members["value"] ($length)
moss 4

Getter-style properties are represented without executing user getters during inspection. Actual fields win name collisions. Receiver-free conversion factories belong to the represented type's interface rather than an instance receiver. next_item is part of the available interface too.

Inspect a type's interface

ExecResult.properties describes the represented type, not a made-up default instance. Field entries describe declared types; function entries describe their prototypes, including factories. Type.properties describes Type itself.

The prelude's .print_properties formats this information for exploration:

Integer.print_properties
ExecResult.print_properties
[Integer].print_properties

Property hashes have no guaranteed display order. Use their named entries when writing logic rather than depending on the pretty printer's ordering.


Related: Methods · Type guards · Named types

Clone this wiki locally