Alternatives to try/finally code structure

Next Topic
 
classic Classic list List threaded Threaded
13 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Alternatives to try/finally code structure

Milles, Eric (TR Technology)

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
Reply | Threaded
Open this post in threaded view
|

Re: Alternatives to try/finally code structure

Leo Gertsenshteyn
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) <[hidden email]> 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
Reply | Threaded
Open this post in threaded view
|

RE: Alternatives to try/finally code structure

Milles, Eric (TR Technology)

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 <[hidden email]>
Sent: Saturday, November 21, 2020 5:34 PM
To: [hidden email]
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) <[hidden email]> 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

MG
Reply | Threaded
Open this post in threaded view
|

Re: Alternatives to try/finally code structure

MG
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 [hidden email]
Sent: Saturday, November 21, 2020 5:34 PM
To: [hidden email]
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) <[hidden email]> 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


Reply | Threaded
Open this post in threaded view
|

Re: Alternatives to try/finally code structure

Christopher Smith
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 <[hidden email]> 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 <[hidden email]>
> Sent: Saturday, November 21, 2020 5:34 PM
> To: [hidden email]
> 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) <[hidden email]> 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
Reply | Threaded
Open this post in threaded view
|

Re: Alternatives to try/finally code structure

Jochen Theodorou
In reply to this post by Milles, Eric (TR Technology)
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
Reply | Threaded
Open this post in threaded view
|

RE: Alternatives to try/finally code structure

Milles, Eric (TR Technology)
In reply to this post by Christopher Smith
MG
Reply | Threaded
Open this post in threaded view
|

Re: Alternatives to try/finally code structure

MG
In reply to this post by Jochen Theodorou
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.
Reply | Threaded
Open this post in threaded view
|

RE: Alternatives to try/finally code structure

Milles, Eric (TR Technology)
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 ___ = { ->
   ...
}

Reply | Threaded
Open this post in threaded view
|

RE: Alternatives to try/finally code structure

Milles, Eric (TR Technology)
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.
12