Skip to Content

Prop

prop turns members that read like fields but can carry logic on write into first-class citizens. An auto prop declares in a single line; accessor bodies appear only when needed.

Auto Props and Expression Bodies

This page uses a thermometer as the running example. The full declaration looks like this; the sections below take it apart piece by piece:

class Thermometer { public prop Celsius: float = 0.0; public prop Label: string => $"{Celsius:F1}°C"; public prop Kelvin: float => Celsius + 273.15; public prop Fahrenheit: float { get => Celsius * 1.8 + 32.0; set { Celsius = (value - 32.0) / 1.8; } } }
  • prop Celsius: float = 0.0; is an auto prop: the compiler sets up the backing storage slot, and the initializer supplies the initial value.
  • An expression body => expr is a read-only computed prop, evaluated fresh on every read. Kelvin is exactly such a read-only derived surface.

Explicit Accessors

When reading and writing need different logic, use get / set accessors:

public prop Fahrenheit: float { get => Celsius * 1.8 + 32.0; set { Celsius = (value - 32.0) / 1.8; } }

get / set can each choose their form: ; (auto), => expression, or { block }. Inside a set body, value refers to the value being written.

set(value) and the Backing Slot field

The setter’s value parameter can be renamed; once renamed, only that name exists in the body and value is no longer injected. The field keyword inside an accessor body points straight at the backing storage slot, which suits “wrap on read, store verbatim on write”:

class Notebook { public prop Note: string { get => "reading: " + field; set(n) { field = n; } } = "empty"; } val t = new Notebook(); t.Note = "high"; print(t.Note);
reading: high

= "empty" is an initializer written after the accessor block; the prop reads back as "reading: " plus the stored value.

computed: Dedicated to Derived Reads

A computed prop is the keyword form of a read-only computed prop, implicitly pure, dedicated to derived values; it is a reactive computed prop:

class Reservoir { public prop Level: int = 5; public computed prop Doubled: int => Level * 2; public prop Status: string { get => $"level {Level}"; } } val r = new Reservoir(); print(r.Level, r.Doubled, r.Status); r.Level = 9; print(r.Doubled, r.Status);
5 10 level 5 18 level 9

Doubled is computed from Level on every read, and changes to Level show up immediately; see the Events and Reactivity part.

Virtual Dispatch: virtual prop and override prop

Props take part in virtual dispatch just like methods: mark the base class with virtual prop and provide a new implementation in the derived class with override prop; reads through a base-class reference or in protocol position get the runtime type’s version:

class Package { public virtual prop Label: string => "regular parcel"; } class Fresh : Package { public override prop Label: string => "cold-chain parcel"; } val shelf = new list<Package>(); shelf.Add(new Package()); shelf.Add(new Fresh()); for p in shelf { print(p.Label); }
regular parcel cold-chain parcel

Putting It All Together

val t = new Thermometer(); print(t.Celsius, t.Fahrenheit, t.Label); t.Fahrenheit = 212.0; print(t.Celsius, t.Kelvin);
0 32 0.0°C 100 373.15

Fahrenheit’s set converts the Fahrenheit value back to Celsius and stores it in Celsius, and the get computes from Celsius on the fly; the two props share the same source of truth. Kelvin is a read-only derived surface.

Notes

  • Avoid expensive work in accessor bodies: callers should be able to assume that reading or writing a prop is cheap.
Last updated on October 11, 2026