LEVIATHAN v962456e · 962456eee1

leviathan-lang.com / docs / learn

Interfaces and multiple inheritance

Interfaces that require fields, multiple inheritance, and resolving collisions with distinct.

Interfaces

An interface declares required members, including fields, and allocates nothing. The implementing class is the single allocating site.

An interface with a required field

interface Named {
    string label;         // a required field
    string describe();    // a required method
}

class Tag : Named {
    public string label;
    public string describe() => "#${label}";
}

Tag t = Tag("lang");
console.writeln(t.describe());
Named n = t;
console.writeln(n.label);
#lang
lang

Multiple inheritance

Leviathan allows multiple inheritance, and solves the classic diamond problem by being explicit. Two members collide only when their name and type both match. When they do, the compiler reports the collision and you choose: distinct keeps a separate slot per source, reached by qualification, and restating the member in the derived class keeps one.

distinct keeps two slots apart

class Counter { public distinct int value = 0; }
class Badge   { public distinct int value = 99; }

class Widget : Counter, Badge {
    new Widget() {
        this.Counter::value = 5;    // two distinct 'value' slots:
        this.Badge::value   = 7;    // no ambiguity, no silent merge
    }
    // A bare read of 'value' here is a compile error: which one did you mean?
    int total() => this.Counter::value + this.Badge::value;
}

Widget w = Widget();
console.writeln(w.Counter::value);
console.writeln(w.Badge::value);
console.writeln(w.total());
5
7
12

A distinct slot belongs to the class that declares it, not to the path that reaches it, so a subclass can take it over. A subclass that redeclares an inherited distinct member (int value = 10; in a class derived from Counter) overrides that slot in place: there is still one value slot, it now holds the subclass's initializer, and a class with a single base reads it with a plain value. Methods behave the same way, and a class that inherits a distinct member from two bases reaches each with this.Base::name. Mark a redeclaration override when you want the compiler to check that it really replaces something: it is an error if nothing is inherited under that name, and a member that almost matches an inherited one (a one-letter typo) draws a warning.

That is the design stance in miniature: rather than guess, the compiler refuses to guess and hands you a precise way to say what you meant. The reference entries on interfaces, multiple inheritance, and distinct members cover the collision-collapse rules and covariant returns.

See also

  • Interfaces — Declare the members a class must provide, including fields, and let any class satisfy them by declaring those members.
  • Multiple inheritance — Combine several base classes, how same-named fields and methods collapse or stay separate, and how to reach one base's member by qualification.
  • distinct fields — Keep same-named fields of different bases in separate slots, reached by qualification.
  • override — Mark a member that must replace an inherited member or satisfy an interface requirement, and the warning for a member that almost does.