some experiments with Concise Method Bodies

Remi Forax forax at univ-mlv.fr
Mon Oct 15 20:36:05 UTC 2018


while throw is not an expression,
currently the syntax "-> throw" is allowed in an expression switch but not allowed neither in a lambda and nor as a concise method body.

I believe we should fix these discrepancies by allowing -> throw as part of a lambda or a CMB.

Rémi 

----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Aaron Scott-Boddendijk" <talden at gmail.com>, "Stuart Marks" <stuart.marks at oracle.com>
> Cc: "amber-dev" <amber-dev at openjdk.java.net>
> Envoyé: Lundi 15 Octobre 2018 22:17:44
> Objet: Re: some experiments with Concise Method Bodies

> Not surprisingly, we explored this ground, and decided we liked things
> where they are.
> 
> Your "instead of" example makes the alternative look worse than it is.
> You could instead say:
> 
>     if (!condition)
>         throw new BadStuffException();
>     final Result result = goodPath();
> 
> which isn't nearly as fussy, and is more clear, as it puts your preconditions up
> front.
> 
> In the run-up to switch expressions, we explored allowing `throw` to be an
> expression, but that would lead to code for which the control flow is much
> harder to read:
> 
>     if (isFoo(x) || isBar(y) || throw new NotFooExpression()) { ... }
> 
> or
> 
>     if (methodWithSideEffect(x) && throw new UnrelatedException()) { /* dead */ }
> 
> or, even
> 
>     m(3, 4, throw new XYZException(...))
> 
> Now, you could say "Sure, I don't want all that, but I want it for ternary", and
> that would be a reasonable opinion, but at this point the cost-benefit of the
> incremental complexity starts to tilt the other way.  Refactoring a ternary to
> an if, as above, really isn't so bad -- and it is more clear.  So we decided to
> not create a special case for this.
> 
> 
> 
> 
> On 10/15/2018 3:32 PM, Aaron Scott-Boddendijk wrote:
>>>      public boolean add(E e) -> throw new UnsupportedOperationException();
>> This is similar to issues with flow-control.  It would be nice to be able
>> to do..
>>
>>      final Result r = condition ? goodPath() : throw new BadStuffException();
>>      ...
>>
>> instead of...
>>
>>      final Result;
>>      if (condition) {
>>          result = goodPath();
>>      } else {
>>          throw new BadStuffException();
>>      }
>>      ...
>>
>> Either branch of the ternary should be allowed to be a throw and, if both
>> are throws the ternary is a Void expression (allowing for a concise 'which
>> exception are we throwing').
>>
>> --
>> Aaron Scott-Boddendijk
>>
>>
>> On Tue, Oct 16, 2018 at 7:40 AM Stuart Marks <stuart.marks at oracle.com>
>> wrote:
>>
>>> When I first saw the CMB proposal, I immediately thought "delegation" and
>>> so I
>>> thought it would be useful to see how CMB could be applied in this area.
>>> The
>>> example I chose was the implementation class for
>>> Collections.unmodifiableCollection(), which returns an instance of a
>>> wrapper
>>> class [1] that delegates most (but not all) method calls to the backing
>>> collection.
>>>
>>> I cloned the amber repo and updated to the concise-method-declarations
>>> branch.
>>> Building the JDK from this branch worked smoothly. Compiling "Hello,
>>> world" worked:
>>>
>>>       public class Concise {
>>>           public static void main(String[] args)
>>>               -> System.out.println("Hello, concise method bodies!");
>>>       }
>>>
>>> I took Collections$UnmodifiableCollection and extracted it into a
>>> standalone
>>> class UnmodColl1.java [2] and stripped out some extraneous stuff. I then
>>> copied
>>> it to UnmodColl2.java [3] and converted it to use CMB. Along the way I
>>> made some
>>> shortening transformations that didn't directly relate to CMB, so I
>>> retrofitted
>>> them back to UnmodColl1.java. The results are more-or-less diff-able.
>>>
>>> Observations:
>>>
>>> * I was able to use CMB for almost everything. These wrapper classes
>>> consistent
>>> almost entirely of one-liners, to which CMB can be directly applied.
>>>
>>> * The CMB version is a bit prettier. Lack of 'return' and braces reduces
>>> visual
>>> clutter, and in a few cases it enabled things to be moved onto a single
>>> line,
>>> reducing vertical space.
>>>
>>> * Note that these wrapper classes already bend the usual style rules for
>>> braces
>>> and statements of a method body, e.g., even in the original we have the
>>> method
>>>
>>>       public int size() {return c.size();}
>>>
>>> written on a single line. If the style were followed strictly, this would
>>> be
>>> written on three lines. There's a reason for this; writing all these
>>> one-liners
>>> in standard style would waste an egregious amount of vertical space. CMB
>>> syntax
>>> provides a much bigger win over standard style than over the non-standard,
>>> compact style used in these Collections wrapper classes.
>>>
>>> On the other hand, while the non-standard, compact style does save
>>> vertical
>>> space, it's kind of annoying to deal with, because it's non-standard. In
>>> my
>>> opinion the tradeoff is in favor of the compact style. But CMB mostly
>>> relieves
>>> us of having to make this tradeoff at all.
>>>
>>> * None of these methods have javadoc, since this is a private
>>> implementation class.
>>>
>>> * Many of the delegating methods can be written using the method reference
>>> form:
>>>
>>>       public int size() = c::size;
>>>
>>> This would produce a marginal improvement in the syntax, mostly by
>>> removing the
>>> need for a set of parens and the need to pass parameters explicitly. The
>>> issue
>>> of time-of-evaluation of the receiver mostly doesn't arise here, since the
>>> delegate is a final field initialized by the constructor.
>>>
>>> * I tried to use CMB for the constructor, but I got an error message
>>> saying it
>>> was disallowed. No great loss, and makes some sense, I guess.
>>>
>>> * Many of the methods throw exceptions. I had a bit of a wrestling match
>>> with
>>> this. My first attempt was
>>>
>>>       UnsupportedOperationException uoe() -> new
>>> UnsupportedOperationException;
>>>       public boolean add(E e) -> throw uoe();
>>>
>>> But this doesn't work, because 'throw' isn't an expression, and the
>>> concise body
>>> is required to be an expression of the right type even though we know the
>>> method
>>> can never return normally. Of course, one can do this:
>>>
>>>       public boolean add(E e) { throw uoe(); }
>>>
>>> but I wanted to use CMB. My next attempt was this:
>>>
>>>       boolean throwUOE() { throw new UnsupportedOperationException(); }
>>>       public boolean add(E e) -> throwUOE();
>>>
>>> Now this works, but it's a hack. Most of the throwing methods happen to
>>> return
>>> boolean, so I was able to declare the helper method to return boolean as
>>> well.
>>> It can also be used for void-returning methods. But if I had needed to
>>> delegate
>>> several throwing methods that had different return types, I wouldn't be
>>> able to
>>> do this.
>>>
>>> I tried to use the method reference form to deal with the throwing
>>> methods, but
>>> that didn't work, since those methods all take different parameters.
>>>
>>> It would be nice if I could just do
>>>
>>>       public boolean add(E e) -> throw new UnsupportedOperationException();
>>>
>>> or
>>>
>>>       public boolean add(E e) -> throw uoe();
>>>
>>> where uoe() returns an exception instance.
>>>
>>> **
>>>
>>> Summary: it was fun playing with this feature. It seems to clean things up
>>> a
>>> little bit, and for classes that are all one-liner methods the little
>>> savings
>>> add up to a lot. The savings are incrementally greater compared to a
>>> hypothetical wrapper class that uses the standard coding style. CMB
>>> doesn't seem
>>> to be a must-have feature, at least not yet, but it does seem to have some
>>> potential.
>>>
>>> s'marks
>>>
>>>
>>> [1]
>>>
>>> http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/util/Collections.java#l1021
>>>
>>> [2] http://cr.openjdk.java.net/~smarks/amber/UnmodColl1.java
>>>
>>> [3] http://cr.openjdk.java.net/~smarks/amber/UnmodColl2.java
>>>


More information about the amber-dev mailing list