`this` in concise method bodies

Remi Forax forax at univ-mlv.fr
Sat Oct 13 13:48:00 UTC 2018

----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "John Rose" <john.r.rose at oracle.com>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Samedi 13 Octobre 2018 15:22:12
> Objet: Re: `this` in concise method bodies

> John makes some good arguments for the value of the :: form of CMBs;
> that they raise the level of reuse from imperative to declarative using
> a variety of wiring patterns.  I'd like to make a case for an
> under-appreciated aspect of the -> form as well.
> It's natural to look at a feature like this and imagine how we might use
> it to re-express existing code.  In this case, some people are looking
> at it and saying "but my existing code is OK, and this doesn't make it
> that much better."  A fair reaction, but let's remember this is only
> half the story.  The other half is, how might it _change_ the ways we
> code (for better or worse.)
> I'll make an analogy to local variable type inference.  It's obvious how
> existing code can take advantage of LVTI -- just s/explicit type/var/.
> Fine.  But, what's less obvious is how lowering the barrier to declaring
> a local variable moves the equilibrium of how people factor expressions,
> towards simpler expressions.  LVTI reduces the "penalty" for unrolling
> complex nested/chained expressions into a sequence of simpler
> expressions, where each subexpression has a descriptive name:
>     var a = ...
>     var b = f(a)
>     var c = g(a,b)
> (OK, a/b/c are not descriptive names, but you get the point.) We've all
> felt the temptation to inline a subexpression into an already complex
> expression, even though it would be more readable to give it a name,
> because the overhead of declaring it felt like "too much".  LVTI lowers
> that activation energy, giving us more choices, and nudging us away from
> the cram-it-all-in style.
> (To be fair, people are aware of some of the ways in which a new feature
> might change how people code -- but generally they see the bad ways much
> more readily than the good.  People were very quick to jump on how bad
> programmers might misuse LVTI; they were much slower to realize how it
> would nudge most programmers towards writing clearer code.  (Nod
> silently if you recognize this dynamic.))
> The -> form of CMBs have a similar characteristic to LVTI; they lower
> the overhead of factoring a subexpression into a method.  As a result,
> we should expect people to code with larger numbers of simple methods,
> each with a descriptive name.  Isn't this something we should be nudging
> people towards?  A method that evaluates exactly one expression is a
> generally a pretty good method; with this form, we'll surely get more of
> those:
>     int a() -> ...
>     int b() -> f(a())
>     int c() -> g(a(), b())
> Or, to put it the other way, the status quo (to which we're all used)
> discourages this normalized form (which functional programmers will
> recognize instantly), by blurring the distinction between cleanly
> factored methods like these, and messier methods that do more than one
> thing.
> While small, I think this is a nudge worth considering.

Given that you are comparing concise methods to var statements, i wonder if it's not more an argument for local concise methods as proposed bu Lukas Eder.


> On 10/13/2018 12:47 AM, John Rose wrote:
>> 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