You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@groovy.apache.org by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com> on 2020/11/21 19:29:15 UTC

Alternatives to try/finally code structure

Groovy Devs,

I have been pondering how to automate the writing of try/finally blocks used to unconditionally restore object state.  Does anyone know of a Groovier way to do something like this before I pursue a macro method, an AST transformation, or something more involved?  It currently requires a lot of typing to do this.

Scenario 1: stack-ify a scalar field/property -- often used when traversing a list or tree and "state" is the current element

class Foo {
  private state
  def bar() {
    def temp = state // may be any number of fields saved to temp vars
    state = newState
    try {
      baz()
    } finally {
      state = temp
    }
  }
  def baz() {
    // make use of state; does not require previous values
  }
}

Scenario 2: mutation rollback -- similar but "state" is not written to beforehand

class Foo {
  private state
  def bar() {
    def temp = state // may be any number of fields saved to temp vars
    try {
      baz()
    } finally {
      state = temp
    }
  }
  def baz() {
    // modifies state
  }
}

Note: "state" is not always convertible into a java.util.Stack.  Sometimes this is 3rd-party code that is being extended.



I was thinking of something like this so as not to overload the try keyword:

class Foo {
  def bar() {
    push (field1 = value, field2) { // AST would be re-written to try/finally like Scenario 1/2
      baz()
    }
  }
  ...
}

This e-mail is for the sole use of the intended recipient and contains information that may be privileged and/or confidential. If you are not an intended recipient, please notify the sender by return e-mail and delete this e-mail and any attachments. Certain required legal entity disclosures can be accessed on our website: https://www.thomsonreuters.com/en/resources/disclosures.html

RE: Alternatives to try/finally code structure

Posted by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com>.
Or an alternative where the method creates an AutoCloseable instance that fits into a try-with-resources:

class Foo {
  def bar() {
    try (push(field1 = value); push(field2)) {
      baz()
    }
  }
}

"push" would create something that has read/write access to "field1".  Assignment "field1 = value" happens in constructor and "field1 = <previous value>" happens in the close() method.

RE: Alternatives to try/finally code structure

Posted by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com>.
Or I could still call it "push" and it could look like this:

try (field.push(value)) {
   ...
}

------

> Now imagine that try-catch-finally wrapper method was an inlined closure, and it inlines the closure it receives, then you get a solution to your problem with very little overhead.

This may be in alignment with what you are saying.  If I had a method "auto(oldValue, newValue)" that creats an inline AutoCloseable AIC instance, I could package up capture, mutate and restore within the enclosing scope.  
try (auto(field, value)) {
   ...
}


Re: Alternatives to try/finally code structure

Posted by Christopher Smith <ch...@gmail.com>.
This is substantially lower-level than is probably warranted, but what
you're looking for could be implemented by pushing the values onto the
stack immediately at the entry point.

On Tue, Nov 24, 2020, 15:36 Milles, Eric (TR Technology) <
eric.milles@thomsonreuters.com> wrote:

> > since you seemed to say in the beginning that you did not want to create
> objects (if I read this correctly), why creating AutoCloseable instances
> here would not pose a problem ?)
>
> Best case scenario is that I get the same performance and memory footprint
> of the original code (included below).  So an AST transform is what I look
> to in order to type less code but get the same runtime outcome.  A macro
> method must replace an expression with another expression, so writing out a
> try statement is not possible AFAIK (unless it is embedded in a
> ClosureExpression or something like that).  However an AIC is a
> ConstructorCallExpression so that is possible with a macro method.  And I
> think the one extra plus going for the AIC AutoCloseable is it could be
> reused anywhere an AutoCloseable is accepted.
>
> If there is something simpler/groovier I am interested to hear about it.
> The suggestion of converting Foo into an AutoCloseable significantly
> changes the design/structure.  I'm more looking to rewrite the contents of
> the bar() method only.
>
>
> class Foo {
>   private state
>   def bar() {
>     def temp = state
>     state = newState
>     try {
>       baz()
>     } finally {
>       state = temp
>     }
>   }
>   def baz() {
>     // make use of state; does not require previous values
>   }
> }
>
>
>
>
> In any case, I did manage to prototype it out and it works.  Here it is in
> case anyone wanted to have a look.
>
>
> class Foo {
>   def bar() {
>     println field
>     try (def x = auto(field, 'new')) { // needed to provide "def x ="
> because parser validates for this and macro transform runs after that
>       baz()
>     }
>     println field
>   }
>   def baz() {
>     println field
>   }
>
>   private field = 'old'
> }
>
> new Foo().bar() // should print "old", "new", "old"
>
>
>
>
>
> import static org.codehaus.groovy.ast.ClassHelper.*
> import static org.codehaus.groovy.ast.tools.GeneralUtils.*
>
> import org.codehaus.groovy.ast.*
> import org.codehaus.groovy.ast.expr.*
> import org.codehaus.groovy.macro.runtime.Macro
> import org.codehaus.groovy.macro.runtime.MacroContext
>
> import groovy.transform.AutoFinal
>
> @AutoFinal
> class MoreMacroMethods {
>
>   @Macro
>   static Expression auto(MacroContext context, VariableExpression
> oldValue, Expression newValue = oldValue) {
>     if (isImplicitThis(context)) {
>       ClassNode type = newAutoCloseable(context)
>
>       FieldNode field = type.addField('temp', 0x12, OBJECT_TYPE, oldValue)
>
>
> type.addObjectInitializerStatements(block(assignS(varX(oldValue.name),
> newValue)))
>
>       type.addMethod('close', 0x11, VOID_TYPE, Parameter.EMPTY_ARRAY, new
> ClassNode[] {make(Exception.class)}, null).tap {
>         addAnnotation(new AnnotationNode(make(Override.class)))
>         setCode(assignS(varX(oldValue.name), fieldX(field)))
>         setSourcePosition(context.call)
>       }
>
>       return new ConstructorCallExpression(type,
> ArgumentListExpression.EMPTY_ARGUMENTS).tap {
>         usingAnonymousInnerClass = true
>       }
>     }
>   }
>
>   private static ClassNode newAutoCloseable(MacroContext context) {
>     def (ClassNode enclosingClass, MethodNode enclosingMethod) =
> findEnclosing(context)
>     def name = enclosingClass.name + '$' +
> (anonymousClassCount(enclosingClass) + 1)
>     def type = new InnerClassNode(enclosingClass, name, 1,
> AUTOCLOSEABLE_TYPE)
>     type.anonymous = true; type.enclosingMethod = enclosingMethod
>
>     context.compilationUnit.addNewPhaseOperation({
> org.codehaus.groovy.control.SourceUnit unit ->
>       if (unit.is(context.sourceUnit)) {
>         unit.AST.addClass(type)
>       }
>     } as org.codehaus.groovy.control.CompilationUnit.ISourceUnitOperation,
> 3)
>
>     return type
>   }
>
>   private static int anonymousClassCount(ClassNode node) {
>     int count = 0
>     for (Iterator<InnerClassNode> it = node.getInnerClasses();
> it.hasNext();) {
>       InnerClassNode innerClass = it.next()
>       if (innerClass.isAnonymous()) {
>         count += 1
>       }
>     }
>     return count
>   }
>
>   private static Tuple2<ClassNode, MethodNode> findEnclosing(MacroContext
> context) {
>     Tuple.tuple(context.sourceUnit.AST.classes[1],
> context.sourceUnit.AST.classes[1].methods[0]) // TODO: search
> context.sourceUnit.AST for enclosing type and method of context.call
>   }
>
>   private static boolean isImplicitThis(MacroContext context) {
>     context.call.with {
>       objectExpression instanceof VariableExpression &&
>       objectExpression.isThisExpression() &&
>       isImplicitThis()
>     }
>   }
> }
>
>

RE: Alternatives to try/finally code structure

Posted by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com>.
> since you seemed to say in the beginning that you did not want to create objects (if I read this correctly), why creating AutoCloseable instances here would not pose a problem ?)

Best case scenario is that I get the same performance and memory footprint of the original code (included below).  So an AST transform is what I look to in order to type less code but get the same runtime outcome.  A macro method must replace an expression with another expression, so writing out a try statement is not possible AFAIK (unless it is embedded in a ClosureExpression or something like that).  However an AIC is a ConstructorCallExpression so that is possible with a macro method.  And I think the one extra plus going for the AIC AutoCloseable is it could be reused anywhere an AutoCloseable is accepted.

If there is something simpler/groovier I am interested to hear about it.  The suggestion of converting Foo into an AutoCloseable significantly changes the design/structure.  I'm more looking to rewrite the contents of the bar() method only.


class Foo {
  private state
  def bar() {
    def temp = state
    state = newState
    try {
      baz()
    } finally {
      state = temp
    }
  }
  def baz() {
    // make use of state; does not require previous values
  }
}




In any case, I did manage to prototype it out and it works.  Here it is in case anyone wanted to have a look.


class Foo {
  def bar() {
    println field
    try (def x = auto(field, 'new')) { // needed to provide "def x =" because parser validates for this and macro transform runs after that
      baz()
    }
    println field
  }
  def baz() {
    println field
  }

  private field = 'old'
}

new Foo().bar() // should print "old", "new", "old"





import static org.codehaus.groovy.ast.ClassHelper.*
import static org.codehaus.groovy.ast.tools.GeneralUtils.*

import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.expr.*
import org.codehaus.groovy.macro.runtime.Macro
import org.codehaus.groovy.macro.runtime.MacroContext

import groovy.transform.AutoFinal

@AutoFinal
class MoreMacroMethods {

  @Macro
  static Expression auto(MacroContext context, VariableExpression oldValue, Expression newValue = oldValue) {
    if (isImplicitThis(context)) {
      ClassNode type = newAutoCloseable(context)

      FieldNode field = type.addField('temp', 0x12, OBJECT_TYPE, oldValue)

      type.addObjectInitializerStatements(block(assignS(varX(oldValue.name), newValue)))

      type.addMethod('close', 0x11, VOID_TYPE, Parameter.EMPTY_ARRAY, new ClassNode[] {make(Exception.class)}, null).tap {
        addAnnotation(new AnnotationNode(make(Override.class)))
        setCode(assignS(varX(oldValue.name), fieldX(field)))
        setSourcePosition(context.call)
      }

      return new ConstructorCallExpression(type, ArgumentListExpression.EMPTY_ARGUMENTS).tap {
        usingAnonymousInnerClass = true
      }
    }
  }

  private static ClassNode newAutoCloseable(MacroContext context) {
    def (ClassNode enclosingClass, MethodNode enclosingMethod) = findEnclosing(context)
    def name = enclosingClass.name + '$' + (anonymousClassCount(enclosingClass) + 1)
    def type = new InnerClassNode(enclosingClass, name, 1, AUTOCLOSEABLE_TYPE)
    type.anonymous = true; type.enclosingMethod = enclosingMethod

    context.compilationUnit.addNewPhaseOperation({ org.codehaus.groovy.control.SourceUnit unit ->
      if (unit.is(context.sourceUnit)) {
        unit.AST.addClass(type)
      }
    } as org.codehaus.groovy.control.CompilationUnit.ISourceUnitOperation, 3)

    return type
  }

  private static int anonymousClassCount(ClassNode node) {
    int count = 0
    for (Iterator<InnerClassNode> it = node.getInnerClasses(); it.hasNext();) {
      InnerClassNode innerClass = it.next()
      if (innerClass.isAnonymous()) {
        count += 1
      }
    }
    return count
  }

  private static Tuple2<ClassNode, MethodNode> findEnclosing(MacroContext context) {
    Tuple.tuple(context.sourceUnit.AST.classes[1], context.sourceUnit.AST.classes[1].methods[0]) // TODO: search context.sourceUnit.AST for enclosing type and method of context.call
  }

  private static boolean isImplicitThis(MacroContext context) {
    context.call.with {
      objectExpression instanceof VariableExpression &&
      objectExpression.isThisExpression() &&
      isImplicitThis()
    }
  }
}


Re: Alternatives to try/finally code structure

Posted by MG <mg...@arscreat.com>.
Something along that line, yes :-)
(Just wondering, since you seemed to say in the beginning that you did 
not want to create objects (if I read this correctly), why creating 
AutoCloseable instances here would not pose a problem ?)

Cheers,
mg

On 23/11/2020 00:45, Milles, Eric (TR Technology) wrote:
>> Now imagine that try-catch-finally wrapper method was an inlined closure, and it inlines the closure it receives, then you get a solution to your problem with very little overhead.
> This may be in alignment with what you are saying.  If I had a method "auto(oldValue, newValue)" that creats an inline AutoCloseable AIC instance, I could package up capture, mutate and restore within the enclosing scope.
> try (auto(field, value)) {
>     ...
> }
>
> would translate to this:
>
> try (new AutoCloseable() {
>    def tmp = field;
>    {
>      field = value
>    }
>    @Override
>    void close() {
>      field = tmp
>    }
> }) {
>     ...
> }
>
> "field" could be any VariableExpression, PropertyExpression, AttributeExpression or FieldExpression -- anything that is read/write.  "value" could be just about any Expression.
>
> This way, the try statement could be its normal self.  There could be anything in the try block, there could be catch blocks and there could be a finally block as well.  And you could reuse this anywhere an AutoCloseable is accepted, so withCloseable is an option in place of try.


RE: Alternatives to try/finally code structure

Posted by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com>.
> Now imagine that try-catch-finally wrapper method was an inlined closure, and it inlines the closure it receives, then you get a solution to your problem with very little overhead.

This may be in alignment with what you are saying.  If I had a method "auto(oldValue, newValue)" that creats an inline AutoCloseable AIC instance, I could package up capture, mutate and restore within the enclosing scope.  
try (auto(field, value)) {
   ...
}

would translate to this:

try (new AutoCloseable() {
  def tmp = field;
  {
    field = value
  }
  @Override
  void close() {
    field = tmp
  }
}) {
   ...
}

"field" could be any VariableExpression, PropertyExpression, AttributeExpression or FieldExpression -- anything that is read/write.  "value" could be just about any Expression.

This way, the try statement could be its normal self.  There could be anything in the try block, there could be catch blocks and there could be a finally block as well.  And you could reuse this anywhere an AutoCloseable is accepted, so withCloseable is an option in place of try.

Re: Alternatives to try/finally code structure

Posted by MG <mg...@arscreat.com>.
I was primarily referring to what Jochen said about a mixin being a 
possible solution: "could be a mixin... but if your requirement is to 
avoid the creation of additional objects, then this will not work, as 
the Closure will be created new every time.".

But I also looked at your code, and saw a lot of snippets that always 
only saved and restored the enclosingElement field.
For that special case one could use a helper try-catch-finally wrapper 
method that takes a closure that executes the actual setting of relevant 
references and the visit.
And if one could live with the overhead of references being saved & 
restored which will not be changed, one could use the same method for 
(maybe) all of the cases you have.
Now imagine that try-catch-finally wrapper method was an inlined 
closure, and it inlines the closure it receives, then you get a solution 
to your problem with very little overhead.


On 22/11/2020 21:28, Milles, Eric (TR Technology) wrote:
> I'm not understanding how inlining a closure expression could help reduce the burden of saving state to a temporary variable and then restoring it after execution, unless there was some additional AST processing.
<zip>

RE: Alternatives to try/finally code structure

Posted by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com>.
I'm not understanding how inlining a closure expression could help reduce the burden of saving state to a temporary variable and then restoring it after execution, unless there was some additional AST processing.

macro method take 2:

class Foo {
  def bar() {
    push { field1 = value, field2 ->
     baz()
    }
  }
}

macro method take 1 -- I think this would have better IDE support:

class Foo {
  def bar() {
    push (field1 = value, field2) {
      baz()
    }
  }
}



An alternative to the macro method would be a local transform.  A variable declaration would allow an annotation.  So maybe something like this could be a more general capture-restore:

@CaptureRestore({ -> ... })
def (one, two) = [field1 = value, field2] // maybe "_" or something would prevent the need for typing temp var names.

converts to

def one = field1
def two = field2
one = value
try {
  ...
} finally {
  field1 = one
  field2 = two
}


This idea has the code to execute come before the state capture and update, which is not the best IMO.  Especially if "..." represents a lot of code.  To flip it, the closure could be assigned to something:
@CaptureRestore({ field1 = value, field2 })
def ___ = { ->
   ...
}


Re: Alternatives to try/finally code structure

Posted by MG <mg...@arscreat.com>.
If Groovy would support closure-block constructs ("inlining closures"), 
this could be overcome without having to resort to writing one's own 
macro (which is more effort, especially if you do not already have a 
seperate macro module set up in your project). I still think inlining 
closures in cases where the closure is in fact used more like a block 
construct would be a useful Groovy feature to have*.

As I have said before I use something similar to what Eric is looking 
for in my SqlExecutor Groovy Sql wrapper
sqlExecutor.withRollback {
     // do some DB changes which will automatically be rolled back at 
end of closure / block
}
for tests (as a specialized version of 
SqlExecutor#withTransaction(Closure) )

Inlining that would allow me to e.g. use return statements inside the 
withRollback block and have them behave as expected, instead of just 
leaving the closure.
In Eric's case it would get rid of the Closure creation overhead (which 
in my case is not that much of a concern, evidently).

I think we should continue to look for powerful, generally useful 
building blocks, that allow programmers to solve a wide range of problems.
If we could add something that would allow for the saving/restoring of a 
bunch of variables/fields (maybe along the line of @NamedDelegate), then 
it feels like the whole problem could be solved in Groovy without 
needing to resort to specialized macros or AST transformations.
(I am not saying using a macro here is inherently bad, just that the 
hurdle for most Groovy devs will be considerably higher)

Cheers,
mg

*It could be abused, yes, but so can many others, and one could make it 
abundantly clear in the docu that inlining closures is not a silver 
bullet, and you should know what you are doing.

On 22/11/2020 15:21, Jochen Theodorou wrote:
> On 21.11.20 20:29, Milles, Eric (TR Technology) wrote:
> [...]
> <zip>
> could be a mixin... but if your requirement is to avoid the creation of
> additional objects, then this will not work, as the Closure will be
> created new every time. And then a macro would be indeed better.

Re: Alternatives to try/finally code structure

Posted by Jochen Theodorou <bl...@gmx.org>.
On 21.11.20 20:29, Milles, Eric (TR Technology) wrote:
[...]
> I was thinking of something like this so as not to overload the try keyword:
>
> class Foo {
>
>    def bar() {
>
>      push (field1 = value, field2) { // AST would be re-written to
> try/finally like Scenario 1/2
>
>        baz()
>
>      }
>
>    }
>
>    ...
>
> }


how about

> class Foo {
>
>   def withState(tryState, code) {
>     def finalState = state
>     try {
>       state = tryState
>       code.call()
>     } finally {
>       state = finalState
>     }
>   }
>  ....
>   def bar(){
>     withState(value) {
>       baz()
>     }
>   }
> }

could be a mixin... but if your requirement is to avoid the creation of
additional objects, then this will not work, as the Closure will be
created new every time. And then a macro would be indeed better.

bye jochen

RE: Alternatives to try/finally code structure

Posted by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com>.
Yes, snapshot and rollback.

Here is a concrete example of the pattern:  https://github.com/groovy/groovy-eclipse/blob/master/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java#L430


Re: Alternatives to try/finally code structure

Posted by Christopher Smith <ch...@gmail.com>.
Is it accurate to rephrase your goal as snapshotting some
variable-per-scenario current state, running code, and then rolling
back any changes to the snapshot?

On Sat, Nov 21, 2020 at 8:02 PM MG <mg...@arscreat.com> wrote:
>
> Hi Eric,
>
> do these references (it seems you do not need to open the can of worms that is deep copying in your problem) you need to save/restore reside in a single class, or could such a class be introduced (maybe in a generic manner) ? I am unclear how wide the different scenarios are here you want to solve in a generic manner...
>
> Cheers,
> mg
>
>
> On 22/11/2020 02:03, Milles, Eric (TR Technology) wrote:
>
> Thanks for the reply.  So if I use an AutoCloseable, I would have something like:
>
>
>
> class Foo {
>
>   private state
>
>   def bar() {
>
>     def temp = state
>
>     state = newState
>
>     try ({ -> state = temp } as AutoCloseable) { // or rewrite this line using withCloseable
>
>       baz()
>
>     }
>
>   }
>
> }
>
>
>
> In my case, Foo is something that visits the elements of a tree or list.  I don't want to create a new instance to visit each element.  bar represents a method that advances to the next element and baz (maybe from super type) processes the next element set in "state".  state is a field or property so I am expecting the assignments to live inside the bar method's scope.
>
>
>
> My goal is to eliminate repetitious typing of assigning the current value to a temporary variable, assigning the next value to the field and at the end restoring the previous value.
>
>
>
>
>
> From: Leo Gertsenshteyn <le...@gmail.com>
> Sent: Saturday, November 21, 2020 5:34 PM
> To: dev@groovy.apache.org
> Subject: Re: Alternatives to try/finally code structure
>
>
>
> Hi Eric,
>
>
>
> Am I correctly interpreting that baz() in the above examples calls into 3rd party code and that code has direct reference to the state and controls the state variables' lifecycle? If that's not the case, there may much much simpler solutions involving intermediate objects, recursion, etc., but I will assume those are not available to you.
>
>
>
> In that case, these look like scenarios for which Java's "try-with-resources" was meant to reduce verbosity. It allows a try block to take objects that implement the AutoCloseable interface and it will call their "close()" methods at the end of the block -- no "finally" needed.
>
>
>
> In Groovy, we can leverage that with ".withCloseable { ... }" closure blocks. Here's an example script:
>
>
>
> class Foo implements AutoCloseable  {
>
>     Foo {
>
>        // set up state as needed
>
>        println("Opened a Foo!")
>
>     }
>
>
>
>     @Override
>
>     void close() throws Exception {
>
>        // tear down state as needed
>
>        println("Closed a Foo!")
>
>     }
>
> }
>
> def main() {
>
>     new Foo().withCloseable {
>
>         // baz()
>
>         println("Hello!")
>
>     }
>
> }
>
>
>
> main()
>
>
>
> Cheers,
>
> Leo
>
>
>
> On Sat, Nov 21, 2020 at 11:29 AM Milles, Eric (TR Technology) <er...@thomsonreuters.com> wrote:
>
> Groovy Devs,
>
>
>
> I have been pondering how to automate the writing of try/finally blocks used to unconditionally restore object state.  Does anyone know of a Groovier way to do something like this before I pursue a macro method, an AST transformation, or something more involved?  It currently requires a lot of typing to do this.
>
>
>
> Scenario 1: stack-ify a scalar field/property -- often used when traversing a list or tree and "state" is the current element
>
>
>
> class Foo {
>
>   private state
>
>   def bar() {
>
>     def temp = state // may be any number of fields saved to temp vars
>
>     state = newState
>
>     try {
>
>       baz()
>
>     } finally {
>
>       state = temp
>
>     }
>
>   }
>
>   def baz() {
>
>     // make use of state; does not require previous values
>
>   }
>
> }
>
>
>
> Scenario 2: mutation rollback -- similar but "state" is not written to beforehand
>
>
>
> class Foo {
>
>   private state
>
>   def bar() {
>
>     def temp = state // may be any number of fields saved to temp vars
>
>     try {
>
>       baz()
>
>     } finally {
>
>       state = temp
>
>     }
>
>   }
>
>   def baz() {
>
>     // modifies state
>
>   }
>
> }
>
>
>
> Note: "state" is not always convertible into a java.util.Stack.  Sometimes this is 3rd-party code that is being extended.
>
>
>
>
>
>
>
> I was thinking of something like this so as not to overload the try keyword:
>
>
>
> class Foo {
>
>   def bar() {
>
>     push (field1 = value, field2) { // AST would be re-written to try/finally like Scenario 1/2
>
>       baz()
>
>     }
>
>   }
>
>   ...
>
> }
>
>
>
> This e-mail is for the sole use of the intended recipient and contains information that may be privileged and/or confidential. If you are not an intended recipient, please notify the sender by return e-mail and delete this e-mail and any attachments. Certain required legal entity disclosures can be accessed on our website: https://www.thomsonreuters.com/en/resources/disclosures.html
>
>


-- 
Christopher Smith

Re: Alternatives to try/finally code structure

Posted by MG <mg...@arscreat.com>.
Hi Eric,

do these references (it seems you do not need to open the can of worms 
that is deep copying in your problem) you need to save/restore reside in 
a single class, or could such a class be introduced (maybe in a generic 
manner) ? I am unclear how wide the different scenarios are here you 
want to solve in a generic manner...

Cheers,
mg


On 22/11/2020 02:03, Milles, Eric (TR Technology) wrote:
>
> Thanks for the reply.  So if I use an AutoCloseable, I would have 
> something like:
>
> class Foo {
>
>   private state
>
>   def bar() {
>
>     def temp = state
>
>     state = newState
>
>     try ({ -> state = temp } as AutoCloseable) { // or rewrite this 
> line using withCloseable
>
>       baz()
>
>     }
>
>   }
>
> }
>
> In my case, Foo is something that visits the elements of a tree or 
> list.  I don't want to create a new instance to visit each element. 
> bar represents a method that advances to the next element and baz 
> (maybe from super type) processes the next element set in "state". 
> state is a field or property so I am expecting the assignments to live 
> inside the bar method's scope.
>
> My goal is to *eliminate repetitious typing* of assigning the current 
> value to a temporary variable, assigning the next value to the field 
> and at the end restoring the previous value.
>
> *From:* Leo Gertsenshteyn <le...@gmail.com>
> *Sent:* Saturday, November 21, 2020 5:34 PM
> *To:* dev@groovy.apache.org
> *Subject:* Re: Alternatives to try/finally code structure
>
> Hi Eric,
>
> Am I correctly interpreting that baz() in the above examples 
> calls into 3rd party code and that code has direct reference to the 
> state and controls the state variables' lifecycle? If that's not the 
> case, there may much much simpler solutions involving intermediate 
> objects, recursion, etc., but I will assume those are not available to 
> you.
>
> In that case, these look like scenarios for which Java's 
> "try-with-resources" was meant to reduce verbosity. It allows a try 
> block to take objects that implement the AutoCloseable interface and 
> it will call their "close()" methods at the end of the block -- no 
> "finally" needed.
>
> In Groovy, we can leverage that with ".withCloseable { ... }" closure 
> blocks. Here's an example script:
>
>     class Foo implements AutoCloseable {
>
>     Foo {
>
>            // set up state as needed
>
>            println("Opened a Foo!")
>
>         }
>
>         @Override
>
>         void close() throws Exception {
>
>            // tear down state as needed
>
>            println("Closed a Foo!")
>
>         }
>
>     }
>
>     def main() {
>
>     new Foo().withCloseable *{*
>
>     **// baz()**
>
>             println("Hello!")
>
>     *}*
>
>     }
>
>     main()
>
> Cheers,
>
> Leo
>
> On Sat, Nov 21, 2020 at 11:29 AM Milles, Eric (TR Technology) 
> <eric.milles@thomsonreuters.com 
> <ma...@thomsonreuters.com>> wrote:
>
>     Groovy Devs,
>
>     I have been pondering how to automate the writing of try/finally
>     blocks used to unconditionally restore object state. Does anyone
>     know of a Groovier way to do something like this before I pursue a
>     macro method, an AST transformation, or something more involved? 
>     It currently requires a lot of typing to do this.
>
>     *Scenario 1: stack-ify a scalar field/property* -- often used when
>     traversing a list or tree and "state" is the current element
>
>     class Foo {
>
>     private state
>
>     def bar() {
>
>     def temp = state // may be any number of fields saved to temp vars
>
>     state = newState
>
>     try {
>
>     baz()
>
>     } finally {
>
>     state = temp
>
>     }
>
>       }
>
>     def baz() {
>
>     // make use of state; does not require previous values
>
>       }
>
>     }
>
>     *Scenario 2: mutation rollback *-- similar but "state" is not
>     written to beforehand
>
>     class Foo {
>
>     private state
>
>     def bar() {
>
>     def temp = state // may be any number of fields saved to temp vars
>
>     try {
>
>     baz()
>
>     } finally {
>
>     state = temp
>
>     }
>
>       }
>
>     def baz() {
>
>     // modifies state
>
>       }
>
>     }
>
>     *Note:* "state" is not always convertible into a java.util.Stack.
>     Sometimes this is 3rd-party code that is being extended.
>
>     I was thinking of something like this so as not to overload the
>     try keyword:
>
>     class Foo {
>
>     def bar() {
>
>     push (field1 = value, field2) { // AST would be re-written to
>     try/finally like Scenario 1/2
>
>     baz()
>
>     }
>
>       }
>
>     ...
>
>     }
>
>     This e-mail is for the sole use of the intended recipient and
>     contains information that may be privileged and/or confidential.
>     If you are not an intended recipient, please notify the sender by
>     return e-mail and delete this e-mail and any attachments. Certain
>     required legal entity disclosures can be accessed on our website:
>     https://www.thomsonreuters.com/en/resources/disclosures.html
>     <https://www.thomsonreuters.com/en/resources/disclosures.html>
>


RE: Alternatives to try/finally code structure

Posted by "Milles, Eric (TR Technology)" <er...@thomsonreuters.com>.
Thanks for the reply.  So if I use an AutoCloseable, I would have something like:

class Foo {
  private state
  def bar() {
    def temp = state
    state = newState
    try ({ -> state = temp } as AutoCloseable) { // or rewrite this line using withCloseable
      baz()
    }
  }
}

In my case, Foo is something that visits the elements of a tree or list.  I don't want to create a new instance to visit each element.  bar represents a method that advances to the next element and baz (maybe from super type) processes the next element set in "state".  state is a field or property so I am expecting the assignments to live inside the bar method's scope.

My goal is to eliminate repetitious typing of assigning the current value to a temporary variable, assigning the next value to the field and at the end restoring the previous value.


From: Leo Gertsenshteyn <le...@gmail.com>
Sent: Saturday, November 21, 2020 5:34 PM
To: dev@groovy.apache.org
Subject: Re: Alternatives to try/finally code structure

Hi Eric,

Am I correctly interpreting that baz() in the above examples calls into 3rd party code and that code has direct reference to the state and controls the state variables' lifecycle? If that's not the case, there may much much simpler solutions involving intermediate objects, recursion, etc., but I will assume those are not available to you.

In that case, these look like scenarios for which Java's "try-with-resources" was meant to reduce verbosity. It allows a try block to take objects that implement the AutoCloseable interface and it will call their "close()" methods at the end of the block -- no "finally" needed.

In Groovy, we can leverage that with ".withCloseable { ... }" closure blocks. Here's an example script:


class Foo implements AutoCloseable  {

    Foo {

       // set up state as needed

       println("Opened a Foo!")

    }



    @Override

    void close() throws Exception {

       // tear down state as needed

       println("Closed a Foo!")

    }

}

def main() {

    new Foo().withCloseable {

        // baz()

        println("Hello!")

    }

}



main()


Cheers,
Leo

On Sat, Nov 21, 2020 at 11:29 AM Milles, Eric (TR Technology) <er...@thomsonreuters.com>> wrote:
Groovy Devs,

I have been pondering how to automate the writing of try/finally blocks used to unconditionally restore object state.  Does anyone know of a Groovier way to do something like this before I pursue a macro method, an AST transformation, or something more involved?  It currently requires a lot of typing to do this.

Scenario 1: stack-ify a scalar field/property -- often used when traversing a list or tree and "state" is the current element

class Foo {
  private state
  def bar() {
    def temp = state // may be any number of fields saved to temp vars
    state = newState
    try {
      baz()
    } finally {
      state = temp
    }
  }
  def baz() {
    // make use of state; does not require previous values
  }
}

Scenario 2: mutation rollback -- similar but "state" is not written to beforehand

class Foo {
  private state
  def bar() {
    def temp = state // may be any number of fields saved to temp vars
    try {
      baz()
    } finally {
      state = temp
    }
  }
  def baz() {
    // modifies state
  }
}

Note: "state" is not always convertible into a java.util.Stack.  Sometimes this is 3rd-party code that is being extended.



I was thinking of something like this so as not to overload the try keyword:

class Foo {
  def bar() {
    push (field1 = value, field2) { // AST would be re-written to try/finally like Scenario 1/2
      baz()
    }
  }
  ...
}

This e-mail is for the sole use of the intended recipient and contains information that may be privileged and/or confidential. If you are not an intended recipient, please notify the sender by return e-mail and delete this e-mail and any attachments. Certain required legal entity disclosures can be accessed on our website: https://www.thomsonreuters.com/en/resources/disclosures.html

Re: Alternatives to try/finally code structure

Posted by Leo Gertsenshteyn <le...@gmail.com>.
Hi Eric,

Am I correctly interpreting that baz() in the above examples calls into 3rd
party code and that code has direct reference to the state and controls the
state variables' lifecycle? If that's not the case, there may much much
simpler solutions involving intermediate objects, recursion, etc., but I
will assume those are not available to you.

In that case, these look like scenarios for which Java's
"try-with-resources" was meant to reduce verbosity. It allows a try block
to take objects that implement the AutoCloseable interface and it will call
their "close()" methods at the end of the block -- no "finally" needed.

In Groovy, we can leverage that with ".withCloseable { ... }" closure
blocks. Here's an example script:

class Foo implements AutoCloseable  {    Foo {       // set up state
as needed       println("Opened a Foo!")    }
        @Override
    void close() throws Exception {       // tear down state as needed
      println("Closed a Foo!")    }
}

def main() {

    new Foo().withCloseable *{
        *// baz()        println("Hello!")
    *}
*}

main()


Cheers,
Leo

On Sat, Nov 21, 2020 at 11:29 AM Milles, Eric (TR Technology) <
eric.milles@thomsonreuters.com> wrote:

> Groovy Devs,
>
>
>
> I have been pondering how to automate the writing of try/finally blocks
> used to unconditionally restore object state.  Does anyone know of a
> Groovier way to do something like this before I pursue a macro method, an
> AST transformation, or something more involved?  It currently requires a
> lot of typing to do this.
>
>
>
> *Scenario 1: stack-ify a scalar field/property* -- often used when
> traversing a list or tree and "state" is the current element
>
>
>
> class Foo {
>
>   private state
>
>   def bar() {
>
>     def temp = state // may be any number of fields saved to temp vars
>
>     state = newState
>
>     try {
>
>       baz()
>
>     } finally {
>
>       state = temp
>
>     }
>
>   }
>
>   def baz() {
>
>     // make use of state; does not require previous values
>
>   }
>
> }
>
>
>
> *Scenario 2: mutation rollback *-- similar but "state" is not written to
> beforehand
>
>
>
> class Foo {
>
>   private state
>
>   def bar() {
>
>     def temp = state // may be any number of fields saved to temp vars
>
>     try {
>
>       baz()
>
>     } finally {
>
>       state = temp
>
>     }
>
>   }
>
>   def baz() {
>
>     // modifies state
>
>   }
>
> }
>
>
>
> *Note:* "state" is not always convertible into a java.util.Stack.
> Sometimes this is 3rd-party code that is being extended.
>
>
>
>
>
>
>
> I was thinking of something like this so as not to overload the try
> keyword:
>
>
>
> class Foo {
>
>   def bar() {
>
>     push (field1 = value, field2) { // AST would be re-written to
> try/finally like Scenario 1/2
>
>       baz()
>
>     }
>
>   }
>
>   ...
>
> }
>
>
> This e-mail is for the sole use of the intended recipient and contains
> information that may be privileged and/or confidential. If you are not an
> intended recipient, please notify the sender by return e-mail and delete
> this e-mail and any attachments. Certain required legal entity disclosures
> can be accessed on our website:
> https://www.thomsonreuters.com/en/resources/disclosures.html
>