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
>