New Issue: Special method names --> interfaces

19074, "vasslitvinov", "Special method names --> interfaces", "2022-01-22T07:11:29Z"

The issue proposes to shield special methods from unintended invocations or redefinitions in user code by placing them into interfaces. This proposal is a way to resolve #19038, at least for non-initializer methods. It is due to @dlongnecke-cray, as interpreted by @vasslitvinov.

The running example is the writeThis method. To simplify presentation we abbreviate it to write and drop the channel argument.

The core of the proposal is to have a predefined interface:

interface StdWriteable(Self) {
  proc Self.write();
}

The implementations of the method write from this interface are special, whereas all other functions named write are not.

The default implementation of this special method is provided implicitly for each type that does not implement it explicitly.

Using a special method

A generic function can invoke this special method through an interface constraint as usual, ex.:

proc writeln(arg: StdWriteable) {
  arg.write();     // invokes the special method 'write' on arg
}

How about invoking it on a user type? For example, I want to write:

record R { }

var r: R;
r.write();  // I want to call the special 'write'

Case (CALL-EXP): user code defines R.write explicitly and the definition is outside an implements.
Then, it can be invoked normally, like a non-special method:

record R { }

// CALL-EXP-1: this 'implements' is explicitly written in user code.
// ==> The user declaration of R.write() defines the special method.
// CALL-EXP-2: user code does not include this 'implements' explicitly.
// ==> The user declaration of R.write() defines a non-special method
// that happens to have the same name.
R implements StdWriteable;

proc R.write() { }       

var r: R;
r.write();  // invokes R.write() defined above, whether it is special or not

Case (CALL-IFC-EXP): user code defines R.write explicitly inside R implements StdWriteable.
Case (CALL-IFC-IMP): user code does not define the special R.write explicitly.
This means the program contains the following declaration, either explicitly in the code or implicitly added by the compiler:

R implements StdWriteable {
  proc R.write() { ... either user code or the default implementation ... }
}

How to invoke such R.write is independent of its special-ness. Here are proposals of how to do it.

(CALL-CAST) - apply a "reinterprete" cast, which does not produce a new value. Note that it does not work as smoothly for multi-argument interfaces.

var r: R;
(r: StdWriteable).write(); // invokes the implementation of StdWriteable.write()
                           // determined by the visible 'R implements StdWriteable'

(CALL-OPEN) - makes the implementations of the interface function(s) from the visible R implements StdWriteable directly visible. This idea is due to John Skaller.

var r: R;
with StdWriteable(R) {
  r.write();               // invokes the implementation of StdWriteable.write()
                           // determined by the visible 'R implements StdWriteable'
}

We can tune the syntax. Or we can use a use:

var r: R;
use StdWriteable(R);
r.write();                 // invokes the implementation of StdWriteable.write()
                           // determined by the visible 'R implements StdWriteable'

Defining a special method

The rule is that the default implementation of the interface StdWriteable is defined implicitly for any type that does not provide it explicitly.

Here are the various possibilities. Assume that these declarations are always present:

record R { }
var r: R;
// case EXP/EXP-OUTSIDE:
R implements StdWriteable;
proc R.write() { } // defines the special method, which can be invoked normally
r.write();         // ok, invokes the user-defined special method
// case EXP/EXP-INSIDE:
R implements StdWriteable {
  proc R.write() { } // defines the special method
}
r.write();           // error, unless there is another visible declaration
                     // of 'proc R.write()'
// case EXP/IMP:
R implements StdWriteable;
// ==> The compiler adds an implicit body of this 'implements', as if the user wrote:
R implements StdWriteable {
  proc R.write() { } // the default implementation of the special method
}
// case IMP/IMP:
// User code does not contain 'R implements StdWriteable' or 'R.write'.
// ==> The compiler adds an implicit 'implements' with the default implementation, as if the user wrote:
R implements StdWriteable {
  proc R.write() { } // the default implementation of the special method
}
// case IMP/EXP:
proc R.write() { } // defines a NON-special method; it can be invoked normally
r.write();         // ok, invokes the non-special method
// ==> The compiler adds an implicit 'implements' with the default implementation, as in the case IMP/IMP:
R implements StdWriteable {
  proc R.write() { } // the default implementation of the special method
}