Eve Methods

In Eve a method belongs to a class. An object method is declared in the body of its class and receives the object as its parameter @self (see Classes). An extension method adds a method to a class from outside its body, in a module or an aspect, without changing the class. Code that does not belong to a class is written as functions (see Functions). This chapter is about extension methods: how they are declared, where they can be used, and why they are safe.

Extension methods

An extension method is declared at the level of a module or an aspect. Its first parameter is @self with the type of the class it extends; the other parameters follow. It is called like a method of that class: p.shift(1, 2).

Syntax:


** extension method: Class gets the method name
method name(@self: Class, parameters) is
  ** statements that use self
  ...
return;

Example:

The module geometry declares the class Point and extends it with the method shift. It exports both, so a driver that imports the module can create points and shift them:


# geometry: points and how to move them
module geometry is
  export (Point, shift, distance);

  class Point = {x, y :Real} <: Object;

  ** extension method: Point gets the method shift
  method shift(@self: Point, dx, dy: Real) is
    let self.x += dx;
    let self.y += dy;
  return;

  ** a function, not a method: it belongs to no object
  function distance(a, b: Point) => (@result: Real) is
    let result := sqrt((b.x - a.x)^2 + (b.y - a.y)^2);
  return;
end geometry;

# use the extension method
driver move_points is
  from "lib" use (geometry(*));

  process main is
    new p := Point(x: 1, y: 2);       ** Point has no constructor: filled by name
    p.shift(2, 3);                     ** the extension method
    expect p.x == 3 and p.y == 5;
    print distance(p, Point(x: 0, y: 0));
  return;
end move_points;

Where an extension method can be used

  • Inside the module or the aspect that declares it, always;
  • In another script only when a module exports it, export (shift);, and the script imports that module. An aspect can't export: its extension methods stay inside it;
  • A driver can declare extension methods too, for its own use.

Static, not monkey patching

Some languages let a program add or replace the methods of a class while it runs ("monkey patching"): the class changes for everybody, and a method can be replaced without the authors of the class knowing it. Eve extension methods are different. They are static:

  • The class itself never changes. An extension method is resolved by the compiler, from the declarations and the imports of the script that calls it;
  • Only the scripts that declare or import the extension see it. Two modules can extend the same class with different methods without any conflict, until one script imports both; then the import fails with a name conflict, as for any two members with the same name;
  • An extension method can't hide or replace a method of the class: a method of the class with the same name and the same signature is a compile error. With another signature it is an overload (Version 0.4, see Overloading, D-150);
  • An extension method sees only the public members of the class, like any other code outside the class: it can't reach the private or protected state.

So an extension method is safe: it adds behavior where it is imported, and nothing else in the program changes.

Unsafe methods

A method whose name ends with ! is an unsafe method: public method record!(@self) is … return;, called s.record!();. It changes shared state: the properties of its class, which all objects of the class share, or the variables of a module, through the functions of that module. Because it changes shared state, it is unsafe for parallel processing: an aspect that calls an unsafe method, directly or through other calls, can be applied but not started in a parallel group. A method without ! works on its own object and its parameters only: it may change self, its @ parameters, print or write files.

A method, like a function or a process, can release its resources with defer (see Defer). The parameters of methods follow the rules of all subprograms: defaults, varargs, named arguments and @ parameters (see Subprograms).

A method is not a value: Eve has no references to methods and no method type, so a method is never passed as a callback, stored or returned (see Functions in a class).

Method Chaining in Eve

Method chaining is an advanced programming technique available in Eve that allows developers to call multiple methods on the same object in a single statement. This can lead to more concise and readable code, especially when performing a series of operations on an object.

Note: Method chaining is an optional feature in Eve. Developers can choose whether to implement it based on their specific needs and coding style preferences.

How Method Chaining Works

In method chaining, each method in the chain returns the object it was called on after performing its operation. In Eve such a method writes @self in its result list as well: => (@self) means "the result is the object itself", so there is nothing to assign. This allows the next method in the chain to be called on the same object.

Example of Method Chaining


class Number(:T) = {value: T} <: Object is
    constructor(initialValue: T) => (@self) is
        let self := Object();
        let self.value := initialValue;
    return;

    public method add(@self, addend: T) => (@self) is
        let self.value += addend;
    return;

    public method multiply(@self, factor: T) => (@self) is
        let self.value *= factor;
    return;

    public method print(@self) => (@self) is
        print "Number value: {self.value}";
    return;
end Number;

# Usage
process main is
    new num := Number(:i32)(10);
    num.add(5).multiply(2).print();
return;
  

Benefits of Method Chaining

  • More concise code
  • Improved readability for sequences of operations
  • Fluent interface design

Considerations

While method chaining can lead to more elegant code, it's important to use it judiciously. Very long chains can sometimes become difficult to read or debug. Developers should balance the benefits of conciseness with overall code clarity.

Best Practice: Consider breaking very long chains into multiple statements or using intermediate variables for better readability and easier debugging.

Implementing Method Chaining

To implement method chaining in Eve, developers need to:

  1. Write @self in the parameter list (the object it receives) and in the result list (the object it returns): method add(@self, x: T) => (@self) is
  2. End the method with a plain return;: the result is already the object
  3. Ensure that the method performs its operation before returning

Remember, implementing method chaining is optional in Eve. Developers can choose to use traditional method calls if they prefer, or mix chained and non-chained methods as appropriate for their code structure.


Read next: Processing