amber-dev Digest, Vol 49, Issue 9

Remi Forax forax at univ-mlv.fr
Thu Mar 25 14:55:27 UTC 2021



----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Vikram Bakshi" <vab2048 at gmail.com>, "amber-dev" <amber-dev at openjdk.java.net>
> Cc: admin at xakeps.dk
> Envoyé: Jeudi 25 Mars 2021 14:54:09
> Objet: Re: amber-dev Digest, Vol 49, Issue 9

> On 3/25/2021 8:58 AM, Vikram Bakshi wrote:
>>> Any ideas how this could be made better? I though about try-catch
>> expressions,
>>> but it's still a lot of a code; I've thought about helper method like
>>> getOrThrow(supplier, exception -> {}), but it's still feels like a half
>>> solution
>> There is a feature in the D programming language (and I believe in others)
>> called 'scope guards'. I have never programmed in D before (Java is my
>> bread and butter) but I came across it and definitely thought it would be
>> useful in Java.
> 
> This is similar to (though stronger than) the `defer` mechanism in
> Golang.  It allows you to accumulate a sequence of actions to be
> potentially performed later.  The D feature is stronger because (a) it
> provides multiple accumulation sets and (b) works with arbitrary block
> boundaries (which is more refactoring-friendly) rather than function
> boundaries.
> 
> Saying `scope(failure) S` is similar to wrapping the remainder of the
> block with `try { remainder } catch (e) { S; throw e; }`.

yep

> 
> Features like this have supporters and detractors.  The supporters point
> to the lower ceremony; the detractors point to the fact that it is
> harder to know what code will be executed if there is a failure at a
> given point.  Java tends to come down on the side of making things
> clearer through block structuring.

Yep, it's boiled down to the scope being explicit like in a try-with-resources or implicit like with defer.


public class DeferScope<X extends Exception> implements AutoCloseable {
  public interface Block<X extends Exception> extends AutoCloseable {
    void close() throws X;
  }

  private Block<X> block = () -> {};

  public static  <X extends Exception> DeferScope<X> scope(Class<X> exceptionClass) {
    return new DeferScope<>();
  }

  public void close() throws X {
    block.close();
  }

  public void defer(Block<? extends X> block) {
    var oldBlock = this.block;
    this.block = () -> {
      try(oldBlock) {
        block.close();
      }
    };
  }
}

...
  import static DeferScope.scope;

  public static void main(String[] args) {
    try (var scope = scope(RuntimeException.class)) {

      scope.defer(() -> System.out.println("defer 1"));
      scope.defer(() -> System.out.println("defer 2"));

    }
  }


and you can also use a ThreadLocal (really, a dynamic variable like in LISP) so the method defer() becomes static but you loose the typing of the lambda taken as parameter of defer().  .

Rémi


More information about the amber-dev mailing list