Object-oriented programming
Vow’s OOP surface is intentionally small: data + methods, interfaces, single inheritance, and composition. There is no garbage collector, no prototypes, and no multiple inheritance.
Hands-on path: Tutorial — OOP.
Classes and structs
Section titled “Classes and structs”class and struct are the same layout. Methods take an explicit self:
class User { name: String age: int
fn greet(self) -> String { return "hello " + self.name }}
fn main(caps: Caps) -> int { var u = User { name: "Ada", age: 36 } u.age = 37 print u.greet() return u.age}- Fields are mutable on
varbindings (u.age = 37) - Methods use
fn, notfunction - Literal syntax:
User { name: "Ada", age: 36 } - Constructors: define
fn new(...) -> Type, thennew Type(...)
class Counter { n: int fn new(start: int) -> Counter { return Counter { n: start } }}
fn main(caps: Caps) -> int { var c = new Counter(0) return c.n}Composition (preferred for shared state)
Section titled “Composition (preferred for shared state)”class Timestamps { created_at: int updated_at: int}
class User { name: String times: Timestamps}Has-a, not is-a — you always see where a field came from.
Interfaces — shared behavior
Section titled “Interfaces — shared behavior”interface Shape { fn area(self) -> int
fn describe(self) -> int { return self.area() }}
struct Circle { radius: int fn area(self) -> int { return 3 * self.radius * self.radius }}
fn report(s: Shape) -> int { return s.describe()}- Interface methods may include a default body
- A parameter typed as
Shapeis static dispatch (monomorphized per call site) - See
tests/lang/interface_basic.vow
dyn — heterogeneous collections
Section titled “dyn — heterogeneous collections”When one list must hold different implementors:
// fragment-onlyfn total_area(shapes: Array<dyn Shape>) -> int { var sum = 0 for s in shapes { sum = sum + s.area() } return sum}
fn main(caps: Caps) -> int { var shapes = [ Circle { radius: 2 } as dyn Shape, Square { side: 3 } as dyn Shape ] return total_area(shapes)}dyn makes the vtable cost visible in the type. See tests/lang/dyn_shapes.vow.
Inheritance — open / extends / override
Section titled “Inheritance — open / extends / override”// fragment-onlyopen class Animal { name: String
fn speak(self) -> String { return "..." }}
class Dog extends Animal { breed: String
override fn speak(self) -> String { return "woof" }}
fn main(caps: Caps) -> int { let d = Dog { name: "rex", breed: "lab" } assert d.name == "rex" assert d.breed == "lab" assert d.speak() == "woof" return 0}| Rule | Meaning |
|---|---|
| Closed by default | Only open class may be subclassed |
| Single inheritance | One extends parent — no diamond hierarchy |
override mandatory |
Shadowing a parent method without override is a type error |
| Static dispatch | Method resolution is compile-time today |
See tests/lang/inheritance.vow.
Method chaining
Section titled “Method chaining”Return self for fluent builders:
// fragment-onlyclass Response { status: int body: String
fn status(self, code: int) -> Response { self.status = code return self }
fn json(self, payload: String) -> Response { self.body = payload return self }}
fn main(caps: Caps) -> int { let r = Response { status: 200, body: "" } let r2 = r.status(404).json("{\"error\":\"not found\"}") return r2.status}See tests/lang/method_chain.vow. Overloads (response.send / json in vow-web-server) resolve at compile time by arity and types.
What to use when
Section titled “What to use when”| Need | Prefer |
|---|---|
| Shared behavior | interface (+ dyn if heterogeneous) |
| Shared fields with specialization | open / extends / override |
| Shared fields without hierarchy | Composition (nested struct) |
| Fluent builders | Method chaining (return self) |
Deliberately out
Section titled “Deliberately out”- Tutorial: OOP
- Interfaces & dyn
- Types
- vow-web-server —
Request/Responsebuilders