----- Mail original ----- > De: "Brian Goetz" <brian.go...@oracle.com> > À: "John Rose" <john.r.r...@oracle.com> > Cc: "amber-spec-experts" <amber-spec-experts@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. Rémi > > > > On 10/13/2018 12:47 AM, John Rose wrote: >> On Oct 12, 2018, at 10:15 AM, Brian Goetz <brian.go...@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