`this` in concise method bodies

John Rose john.r.rose at oracle.com
Sat Oct 13 04:47:59 UTC 2018


On Oct 12, 2018, at 10:15 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
> 
> Summary:
> 
>  - Both the capture-this and drop-this cases have important motivating use cases
>  - Arbitrarily dropping one or the other would compromise the feature
>  - There are some possibly reasonable ways of doing overload resolution and adaptation here, at some complexity.
> 

+100

One reason this proposal is so very powerful is that it allows the
original 'this' passed to the CMB-defined method to serve either,
or both, or neither of two independent roles:

Use-1. Find a data-dependent object (a field 'f' of 'this', or 'this' itself)
to delegate the operation; this delegate will execute the target method
as the next 'this'.  The method reference is of the form 'this::target'
or 'field::target' (where 'field' is treated as 'this.field').

Use-2. Pass 'this' as a passive (non-receiver) argument to the target
method, which may choose to use the original 'this' value in some way.
The method reference is of the form 'sf::target' or 'T::target', where 'sf'
may be a static field or perhaps another constant.

(Non-use-1. If 'this' does not locate a data-dependent object, such an
object may still be obtained from another source 'x', such as a static
variable in the class or another parameter of the method.  The
method reference is of the form 'x::target'.  The method reference
may also refer to a static method, as 'T::sm', in which case there
is no receiver needed, and no data dependency at all.)

(Non-use-2. If 'this' is not passed as a passive argument, then
only the explicit arguments of the original method are passed.)

Use-1 vs. non-use-1 is determined by the expression before
the '::' in the method reference.  Does this expression make
use (explicit or implicit) of 'this', or does it only use statically
available names and parameter names?

Use-2 vs. non-use-2 is determined by the arity of the matching
method:  Does it accept the use-2 passive argument value 'this',
in which case this value is injected as a new passive argument,
or is it dropped?

There are thus four shapes of target method invocations, with
respect to their use or non-use of 'this':

Static call (neither 1 or 2):  The target method uses only the
explicit parameters.  It may be as simple as a constant-returning
method, or a method that derives a value from one of the arguments.

Delegate or bridge call (1, not 2):  The target method is called on a
"friend" of the the original object.  The original object may call 
a different method on itself; this is a bridging pattern.

Concept invocation (2, not 1):  The class of 'this' (but not 'this'
itself) declares a handler method to execute on behalf of
the original 'this', which is passed as an argument.

Prototype invocation (both 1 and 2):  The original receiver
object passes the request to a friend object, *and* passes
along its own identity.  It is as if each object has the option
of carrying around its own customized Concept, rather than
all objects of a given class using a common Concept.

(The term "Concept" comes from C++. I'm not fond of it,
but I don't have a better term than "function".  In Lisp or
Haskell everything is a "Concept".  What a concept.)

Examples:

int computeLength(String s) = String::length;  // Static for some LengthComputer
int getAsInt() = MY_RAND::nextInt;  // Static for some IntSupplier

int size() = inner::size;  // Delegate for some wrapping List
T get(int x) = inner::get;  // Delegate for same
long longHash() = this::hashCode;  // Bridge for some LongHashable
long longHash() = ThisClass::hashCode;  // Same effect via different path
long longHash() = ::hashCode;  // Same effect via different path

void reverse() = Collections::reverse;  // Concept for some List
int compareTo(List that) = MY_LEX_COMPER::compare();  // Concept for some List
String toString() = MY_TO_STRINGER::stringOf;  // Concept for some Object

void mouseClicked(MouseEvent e) = myEventParent::mouseClickedFor;  // Prototype

(Similar comments might be made about patterns which delegate
to explicit method parameters, which in some sense are "just as
deserving of attention" as the implicit 'this' parameter.  Delegating
to an explicit parameter amounts to an immediate callback.
However, the CMB proposal doesn't need to support such things
via method references, and the question of dropping a non-'this'
parameter would seem to be a vexed one.)

The Prototype pattern may seem far-fetched, but there are times
when it's useful.  It has been used to to join and generalize both
regular class-based inheritance and delegation; in this use the
delegate is called the "parent" in Self and "protoype" in JavaScript.

But I don't need to plump for Prototypes in order to observe that
the two "axes" of method reuse, Delegation and Concepts, are
both really, really useful by themselves.  If I had to choose one
this-using pattern, it would be Concepts, but I think it would be
hard to drop Delegation given the natural way it also fits into the
CMB proposal.  Since CMBs give us all four patterns under one
powerful rubric, I say let's take all four and say thank you.

— John


More information about the amber-spec-experts mailing list