2

some experiments with Concise Method Bodies

 2 years ago
source link: https://mail.openjdk.org/pipermail/amber-dev/2018-October/003570.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

some experiments with Concise Method Bodies

some experiments with Concise Method Bodies

Brian Goetz brian.goetz at oracle.com
Mon Oct 15 20:17:44 UTC 2018

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK