groovy
  1. groovy
  2. GROOVY-5302

Closure access to variables can create local variable or not

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Not A Bug
    • Affects Version/s: 1.8.5
    • Fix Version/s: None
    • Component/s: None
    • Environment:
      Windows command line
    • Number of attachments :
      0

      Description

      While trying to override a variable with def in a closure, I discovered that leaving the def out creates an order dependant execution. This would likely confuse novice developers (though I understand why it happens).

      e.g. consider this code:


      def out_closed_x =

      { def x=0 println x }

      def x = 99
      println x
      out_closed_x()


      This will output
      99
      0
      99
      But, if first comment is uncommented and the second comment applies to the whole line, i.e.


      def x = 99

      def out_closed_x =

      { x=0 println x }

      // def x = 99 //
      println x
      out_closed_x()
      println x


      The output is
      99
      0
      0

      This is (presumably) because the local variable instantiated (by default) in the first examples continues to be referenced by the closure, but the global variable is overwritten in the second instance.

      If a def is used in the closure, then things are better - since the second version won't run.

      However, I can see this being confusing. I just wish I had a solution.

      BTW - Groovy is great and I would just like it to be even better

        Activity

        Hide
        Andrew Stratton added a comment -

        Apologies - first example should show:

        // def x = 99

        def out_closed_x =

        { x=0 println x }

        def x = 99 //
        println x
        out_closed_x()
        println x

        Show
        Andrew Stratton added a comment - Apologies - first example should show: // def x = 99 def out_closed_x = { x=0 println x } def x = 99 // println x out_closed_x() println x
        Hide
        blackdrag blackdrag added a comment -

        What you observe is the correct behaviour. Let me try to explain this... Like in Java a local variable is visible only after you have declared it. If you have in Java for example int i = i, then the i on the right side is not the local variable i I declare, but for example a field, or in case of some inner classes it could be also a local variable from an outer context. In Java this outer context has to be declared, for example by declaring a field. The other important point here is, that scopes are lexical. That means even if you use your inner class after a local variable of that name has been declared, it will not use that local variable, but the context the inner class was defined in instead.

        def out_closed_x = { x=0; println x } //1
        def x = 99 //2
        println x  //3
        out_closed_x()  //4
        println x  //5
        

        In Groovy, in a script, there is always such an outer context, the binding of the script. So in the first example, if the closure does x=0, it sets the variable x in the binding to 0. And the following println will also access this binding variable, thus printing 0. In line 2 you declare a local variable, but this local variable has nothing to do with the block in line 1, because it is before the declaration of the local variable. That's why line 3 and 5, both will print 99.

        def x = 99  //1
        def out_closed_x = { x=0; println x } //2
        println x  //3
        out_closed_x()  //4
        println x  //5
        

        In this second example you first declare a local variable in line 1. That means the x references in the other lines will refer to this local variable, because they are after the declaration. Thus this code will print 99,0,0

        Show
        blackdrag blackdrag added a comment - What you observe is the correct behaviour. Let me try to explain this... Like in Java a local variable is visible only after you have declared it. If you have in Java for example int i = i, then the i on the right side is not the local variable i I declare, but for example a field, or in case of some inner classes it could be also a local variable from an outer context. In Java this outer context has to be declared, for example by declaring a field. The other important point here is, that scopes are lexical. That means even if you use your inner class after a local variable of that name has been declared, it will not use that local variable, but the context the inner class was defined in instead. def out_closed_x = { x=0; println x } //1 def x = 99 //2 println x //3 out_closed_x() //4 println x //5 In Groovy, in a script, there is always such an outer context, the binding of the script. So in the first example, if the closure does x=0, it sets the variable x in the binding to 0. And the following println will also access this binding variable, thus printing 0. In line 2 you declare a local variable, but this local variable has nothing to do with the block in line 1, because it is before the declaration of the local variable. That's why line 3 and 5, both will print 99. def x = 99 //1 def out_closed_x = { x=0; println x } //2 println x //3 out_closed_x() //4 println x //5 In this second example you first declare a local variable in line 1. That means the x references in the other lines will refer to this local variable, because they are after the declaration. Thus this code will print 99,0,0
        Hide
        Andrew Stratton added a comment -

        Thanks for the (complete) reply. It confirmed what I thought was happening.

        I guess the only option is to encourage novices to use def as much as possible...

        Cheers
        Andy

        Show
        Andrew Stratton added a comment - Thanks for the (complete) reply. It confirmed what I thought was happening. I guess the only option is to encourage novices to use def as much as possible... Cheers Andy

          People

          • Assignee:
            blackdrag blackdrag
            Reporter:
            Andrew Stratton
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: