Velo by Example: Data Classes

A data class is an immutable value type: compared by value and copied — never aliased — across actor and native boundaries. It is how you move structured data between threads.

State is exactly the constructor parameters; the body may add methods but no fields. Fields are immutable — reassigning one is a compile error.

data class Point(int x, int y) {
    func sum() int { x + y; };
};

Point p = new Point(3, 4);
int s = p.sum();   # 7

Equality is by value — same class, all fields equal (deeply). No operator overload needed.

Point a = new Point(1, 2);
Point b = new Point(1, 2);
bool same = a == b;   # true

A data class crosses an actor boundary as an independent copy, methods intact — the natural way to return structured results.

actor class Geometry() {
    func translate(Point p, int dx, int dy) Point {
        new Point(p.x + dx, p.y + dy);
    };
};

actor[Geometry] geo = new Geometry();
Point moved = await geo.translate(p, 10, 20);   # (13, 24)

Across the native boundary it is marshalled by value: the host registers a matching JVM type with registerData, and a Kotlin data class fits with no annotations.

// Kotlin host
data class NativePoint(val x: Int, val y: Int)

val natives = NativeRegistry()
    .registerData("Point", NativePoint::class)
    .register(Geometry::class)