Eve Partials

A partial is an incomplete class: it declares methods that other classes must implement. Abstract classes and traits are partials.

Partials, Abstract Classes and Traits

A partial method is a method signature without a body, ended by ;. It says what an object can do, and leaves the implementation to another class. Three entities use partial methods. Each has a different job, so Eve names them separately:

EntityStateMethodsCreated with "new"
Classattributes and constructorall implementedyes
Abstract classattributes and constructorsome partialno, only a subclass constructor calls it
Traitnonepartial and providedno, never has instances

Abstract Class

A class that has at least one partial method is an abstract class. It can have attributes, a constructor and implemented methods, like any class, but it can not be instantiated. A subclass calls the abstract constructor by the name of the class, as the construction of its own constructor (see Construction). Inside a class body, object methods take @self without a type.


** abstract class: it has partial methods
class Shape = {name: String} <: Object is
    constructor(name: String) => (@self) is
        let self := Object();
        let self.name := name;
    return;

    ** implemented method, inherited by all shapes
    public method getName(@self) => (@result: String) is
        let result := self.name;
    return;

    ** partial methods: the subclass must implement them
    public method area(@self) => (@result: Real);
    public method perimeter(@self) => (@result: Real);
end Shape;

class Circle = {radius: Real} <: Shape is
    constructor(name: String, radius: Real) => (@self) is
        let self := Shape(name);  ** call the abstract constructor
        let self.radius := radius;
    return;

    public method area(@self) => (@result: Real) is
        let result := pi * self.radius * self.radius;
    return;

    public method perimeter(@self) => (@result: Real) is
        let result := 2 * pi * self.radius;
    return;
end Circle;

# Usage
process main is
    new myCircle := Circle("Small Circle", 5);
    print myCircle.getName();  ** inherited from Shape
    print myCircle.area();     ** implemented by Circle
return;

A class that has a constructor must implement all the partial methods it inherits. A missing implementation is a compile error. It is never a null result at run time.

Traits

A trait is a set of behaviors that unrelated classes can share. It is declared with the keyword trait, and it closes with end. A trait is a container of methods only:

  • it has no attributes, no properties, no constructor and no destructor, so it has no state and no instances;
  • a required method is a partial method: the class that adopts the trait must implement it;
  • a provided method has a body. It can call the required methods through @self, and the class receives it for free;
  • @self has no type: it is the class that adopts the trait;
  • a trait can be generic: trait Comparable(:T) is.

** a trait: one required method and one provided method
trait Printable is
    public method toString(@self) => (@result: String);

    public method show(@self) is
        print self.toString();
    return;
end Printable;

** a class adopts traits in the inheritance list
class Circle = {radius: Real} <: (Shape, Printable) is
    constructor(name: String, radius: Real) => (@self) is
        let self := Shape(name);
        let self.radius := radius;
    return;

    ** implement the required methods of Shape and Printable
    public method area(@self) => (@result: Real) is
        let result := pi * self.radius * self.radius;
    return;

    public method perimeter(@self) => (@result: Real) is
        let result := 2 * pi * self.radius;
    return;

    public method toString(@self) => (@result: String) is
        let result := "Circle '" + self.getName() + "' with radius {self.radius}";
    return;
end Circle;

# Usage
process main is
    new myCircle := Circle("Small Circle", 5);
    myCircle.show();                   ** provided by the trait
    new p: Printable := myCircle;      ** a trait is also a type
    p.show();
    expect myCircle is Printable;
return;

Rules of the inheritance list

  • A class has at most one superclass that carries state, and any number of traits. The superclass is optional (Object by default);
  • when a class and a trait define the same method, the method of the class wins;
  • when two traits provide the same method, the class must implement the method itself, otherwise the compiler reports an error;
  • a trait can be used as the type of a variable or a parameter, and tested with is;
  • the for loop uses the library trait Iterable: a class that adopts it can be traversed.

Note: Eve has no "partial constructor". An object receives its state from the constructor of a class, and the traits add behavior only. This is why a class never inherits conflicting attributes from two parents.


Read next: Methods