Table of Contents

Properties and events

Raven uses a property-first model. val and var define the public mutability contract, while storage is an implementation detail.

Property kinds

  • val: publicly read-only after initialization.
  • var: publicly mutable after initialization.
  • Accessors may be omitted when the declaration contract fully defines the intended surface.

val may declare set/init accessors. A set accessor on val must be less accessible than the getter; otherwise Raven reports RAV0910. A var without any writable shape reports RAV0911. init remains compatible with public object-initializer assignment.

Storage properties

Storage properties are declarations without computed implementation:

val Name: string
var Count: int = 0

For storage properties, Raven can infer the property type from an initializer when the annotation is omitted:

class Foo {
    val x = 2 // inferred as int
}

Without an initializer, a type annotation is required (RAV0918).

The compiler synthesizes backing storage. You can still provide accessors to refine behavior:

val Status: OrderStatus { private set; }
var Score: int {
    get => field
    set => field = max(0, value)
}

Accessor defaults:

Contract Getter Setter
val public none
var public public

When a storage property declares an accessor list that omits get, Raven still provides the getter required by the val or var contract. For example, val Status: OrderStatus { private set; } has a public getter and a private setter. For var, writable access is part of the public contract: explicit set and init accessors must match the property's accessibility. Explicit accessor lists are required when the property surface differs from the default val/var contract.

Computed properties

Computed properties provide implementation directly and do not use synthesized storage unless explicitly needed:

val FullName: string => first + " " + last

The init accessor

init permits assignment only during initialization and preserves val semantics:

val Name: string { init; }

Initialization includes inline initializers, constructors (init(...)), initializer blocks, and object initializers.

field and indexers

Inside storage-property accessors, field references the synthesized backing field. Using field outside an accessor reports RAV0912; using it in a property without backing storage reports RAV0913.

Indexers are a property form using self[...] and follow the same val/var, get/set/init, and accessibility rules:

var self[index: int]: string {
    get => items[index]
    set { items[index] = value }
}

Events

Events let a type notify any registered handlers when something happens, without knowing which objects are listening.

Events expose a delegate-like member that supports handler subscription via += and -=. An event declaration specifies the event keyword, a name, and the event handler type:

class Button {
    event Clicked: System.Action;
}

Custom event accessors

Custom events supply add and remove accessors, which receive the implicit value parameter of the handler type:

class Button {
    event Clicked: System.Action {
        add { /* register value */ }
        remove { /* unregister value */ }
    }
}

Auto-implemented events

When an event declaration ends with ;, the compiler synthesizes a hidden backing field and trivial add/remove accessors. Auto-events are the only events that can be invoked directly, and invocation is only permitted inside the declaring type:

class Button {
    event Clicked: System.Action? // Events can be null

    func Raise() -> unit {
        Clicked?();
    }
}