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.