Skip to content
Kavachकवच

Classes and Inheritance

Also known as: class classes object oriented oop inheritance extends subclass superclass base class abstract class constructor static method composition polymorphism

● Intermediate6 minBefore this: Interfaces, Interface ValuesSpec 2.8

Kavach has no classes and no inheritance. Records, methods, interfaces and composition do the same jobs, with fewer surprises.

From classes to Kavach

In Java, C#, Python…In Kavach
A class with fieldsa type (a record)
Methodsmethods inside the type, with self
A constructorUser(name: …), with defaults for optional fields
A static methoda method without self: Rect.square(2.0)
An abstract class or interfacean interface
Implementing ittype Circle: Shape, or extend Rect: Shape
A list of different subclassesa list of an interface type: [Shape]
Inheriting fieldsa field holding the other value (composition)
A fixed set of subclassesa union, type Shape = Circle | Square, checked by match
Adding methods to a class you didn't writeextend

All of it together

interface Shape {
    fn area(self) -> Float
    fn name(self) -> String
}

type Circle: Shape {
    radius: Float

    fn area(self) -> Float { 3.14 * self.radius * self.radius }
    fn name(self) -> String { "circle" }
}

type Square: Shape {
    side: Float

    fn area(self) -> Float { self.side * self.side }
    fn name(self) -> String { "square" }
}

type Label {
    text: String
    shape: Shape

    fn describe(self) -> String {
        "{self.text}: a {self.shape.name()} of area {self.shape.area()}"
    }
}

fn main() {
    const shapes: [Shape] = [Circle(radius: 1.0), Square(side: 2.0)]
    for s in shapes {
        print("{s.name()}: {s.area()}")
    }
    print(Label(text: "logo", shape: Square(side: 3.0)).describe())
}

The program ran and printed:

kavach run classes.kav

circle: 3.14square: 4.0logo: a square of area 9.0
classes.kav: Kavach code, then what running it prints
  • Shape is the interface: what every shape can do.
  • Circle and Square keep that promise, each its own way.
  • A [Shape] holds both kinds, and each call runs the right method.
  • Label has a shape instead of being one: composition.

Extending a record is refused

Writing a subclass out of habit gets an error that names both ways to do it in Kavach:

type Animal {
    name: String
}

type Dog: Animal {
    breed: String
}

The compiler refused the program:

kavach run inherit.kav

error: `Dog` can't extend `Animal`: `Animal` is a type, not an interface, and Kavach has no inheritance (decision 0029). Give `Dog` a field `animal: Animal`, or make `Animal` an interface
inherit.kav: Kavach code, then what running it prints
Go deeper the why, for readers who want it

Inheritance ties a class to everything above it: change a base class, and every subclass may break, even ones you have never seen (the "fragile base class" problem). Deep hierarchies also hide where a method really comes from.

Kavach keeps the useful half. Interfaces give polymorphism (one call, many types), and composition gives reuse (a value that holds another). Every method a type has is written in its own declaration or in an extend, so it is always easy to find.