Defining a global variable

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

Defining a global variable

Anton Shepelev
Hello, all

I tried to define a global variable in a Groovy script.
Section 1.1. of the "Semantics" documents says that one way
to define a variable is via its type, so I defined one in
the global scope of my script:

   String test

   void func()
   {
      println(test)
   }

   test = 'hello'
   func()

but it failed with the error: "No such property: test for
class: Script1". That didn't explain to me what the problem
was, but I learned that the script was looking for a
property named `test' in the global autogenerated Script
class. OK, thought I, I will give it what it asks, and
cosulted section 1.6.2. (Properties) of the "Object
orientation" document, which told me that the definition of
a property is identical to the definition of a variable I
used above, i.e. that

   String test

defienes a string property `test' or a string variable
`test', depending on context. I was now stuck and resorted
to an internet search, which brought up the following page

   Groovy Variable Scope:
   https://www.baeldung.com/groovy/variable-scope

where I read:

   Scopes in Groovy follow, above all, the rule that all
   variables are created public by default. This means that,
   unless specified, we'll be able to access any variable we
   created from any other scope in the code.

Why, then, is `test' inaccessible from the function in my
script?  And below:

   The easiest way to create a global variable in a Groovy
   script is to assign it anywhere in the script without any
   special keywords. We don't even need to define the type:

      x = 200

After I removed the "String test" line from my script it
started to work, but why?  I returned to the "Semantics"
document and read section 1.2. (Variable assignment) to see
whether the assignment operator may have the side effect of
defining a missing variable, but there is no indication that
it has.

Can you please explain to me, preferable with the
correspoding references to official documentation, why my
scirpt does not work whereas the modified one does?

Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

Jochen Theodorou
On 14.10.20 13:45, Anton Shepelev wrote:
[...]
>     String test
>
> defienes a string property `test' or a string variable
> `test', depending on context.

the context here is that of a method, so it is a local variable

> I was now stuck and resorted
> to an internet search, which brought up the following page
>
>     Groovy Variable Scope:
>     https://www.baeldung.com/groovy/variable-scope
>
> where I read:
>
>     Scopes in Groovy follow, above all, the rule that all
>     variables are created public by default. This means that,
>     unless specified, we'll be able to access any variable we
>     created from any other scope in the code.
>
> Why, then, is `test' inaccessible from the function in my
> script?

because the method you defined is not nested in another method, instead
it exists parallel to the other method that represents your script body,
and they do not share local variables. Frankly... for years we have been
defending this position, but now, with so much distance I actually
really wonder why we keep this. It comes up as a problem on a regular
base, but wouldn't be so hard to "fix" really. I mean it is not broken,
it works as designed, just not very intuitive.

>  And below:
>
>     The easiest way to create a global variable in a Groovy
>     script is to assign it anywhere in the script without any
>     special keywords. We don't even need to define the type:
>
>        x = 200
>
> After I removed the "String test" line from my script it
> started to work, but why?

without the declaration you are writing to a outer scope variable, which
in case of a script usually ends up in the binding. A variable in the
binding of a script is defined by writing it. Reading a non-existent
variable would cause an error.

> I returned to the "Semantics"
> document and read section 1.2. (Variable assignment) to see
> whether the assignment operator may have the side effect of
> defining a missing variable, but there is no indication that
> it has.

it is actually less the assignment operator itself, it is the effect of
scopes of dynamic variables. To compare.. in Java you have a static
scope for a variable. The compiler knows at any time where a variable is
declared, and where to write it to (local variable or class). In Groovy
we also have the static variable scopes, but they are "extended" in
dynamic Groovy with dynamic variables. This means the (meta) class is
"asked" for the value and existence of a variable at runtime as soon as
we leave the static scope. In Java leaving the static scope would mean
an compilation error. But in Groovy we are then using the MOP to find
meaning in what is requested. In case of a Closure this results in
checking the enclosed scope for the value of a variable. In case of a
class this results in set/getProperty and similar methods being called
as part of the MOP. A Groovy script is a class too, only that its
getProperty method checks the binding for the value and setProperty
writes it. There is no formal way to declare a variable in a binding, it
is solely defined by the program writing it. As such it has no static
type (as all dynamic variables).

I would have loved to point to our documentation for this, but I
actually am unable to find this there. I actually thought I have written
something years ago (back when it was still codehaus), but I cannot find
any trace of that either.... I guess I should really get used to write
everything I write for a project also on my blog, so that I can have at
least one original reference. So no references I am aware of, sorry

bye Jochen


Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

boekhold@gmx.com
In reply to this post by Anton Shepelev
Hi,

I've been caught out by this in the past as well. It's actually quite
simple...

A Groovy Script like:

    String test

    void func(){
         println(test)
    }

    test = 'hello'
    func()

Gets compiled to a class as:

    class MyScript extends Script {
         def run() {
             String test

             test = 'hello'
             func()
         }

         void func() {
             println(test)
         }
    }

All "lose code" gets collected into the run() method, and functions
defined in your script are copied into the class as methods. As you can
see, the declaration "String test" ends up as part of the run() method,
and therefore the "test" variable is scoped locally to that run()
method. It's not a class member. Your func() method is not able to see it.

You can work around that by annotating the "String test" declaration
with the @Field annotation:

    @Field String test

    void func() {
         println(test)
    }

    test = 'hello'
    func()

This will cause the "String test" declaration to be included in the
Script class as a class member:

    class MyScript extends Script {
         String test

         def run() {
             test = 'hello'
             func()
         }

         void func(){
             println(test)
         }
    }

This is described in
https://docs.groovy-lang.org/latest/html/documentation/core-metaprogramming.html#xform-Field.
It is also very lightly mentioned in
https://groovy-lang.org/structure.html#_variables, but that page
probably doesn't make it clear enough.

Maarten

On 14/10/2020 15:45, Anton Shepelev wrote:

> Hello, all
>
> I tried to define a global variable in a Groovy script.
> Section 1.1. of the "Semantics" documents says that one way
> to define a variable is via its type, so I defined one in
> the global scope of my script:
>
>     String test
>
>     void func()
>     {
>        println(test)
>     }
>
>     test = 'hello'
>     func()
>
> but it failed with the error: "No such property: test for
> class: Script1". That didn't explain to me what the problem
> was, but I learned that the script was looking for a
> property named `test' in the global autogenerated Script
> class. OK, thought I, I will give it what it asks, and
> cosulted section 1.6.2. (Properties) of the "Object
> orientation" document, which told me that the definition of
> a property is identical to the definition of a variable I
> used above, i.e. that
>
>     String test
>
> defienes a string property `test' or a string variable
> `test', depending on context. I was now stuck and resorted
> to an internet search, which brought up the following page
>
>     Groovy Variable Scope:
>     https://www.baeldung.com/groovy/variable-scope
>
> where I read:
>
>     Scopes in Groovy follow, above all, the rule that all
>     variables are created public by default. This means that,
>     unless specified, we'll be able to access any variable we
>     created from any other scope in the code.
>
> Why, then, is `test' inaccessible from the function in my
> script?  And below:
>
>     The easiest way to create a global variable in a Groovy
>     script is to assign it anywhere in the script without any
>     special keywords. We don't even need to define the type:
>
>        x = 200
>
> After I removed the "String test" line from my script it
> started to work, but why?  I returned to the "Semantics"
> document and read section 1.2. (Variable assignment) to see
> whether the assignment operator may have the side effect of
> defining a missing variable, but there is no indication that
> it has.
>
> Can you please explain to me, preferable with the
> correspoding references to official documentation, why my
> scirpt does not work whereas the modified one does?
>
Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

Anton Shepelev
In reply to this post by Jochen Theodorou
Jochen Theodorou to Anton Shepelev:

> > String test
> >
> > void func()
> > {
> >    println(test)
> > }
> >
> > test = 'hello'
> > func()
> >
> > Why, then, is `test' inaccessible from the function in
> > my script?
>
> because the method you defined is not nested in another
> method, instead it exists parallel to the other method
> that represents your script body, and they do not share
> local variables.

Thanks for the explanation, Jochen.

> Frankly... for years we have been defending this position,
> but now, with so much distance I actually really wonder
> why we keep this.

Perhaps it would have helped if you had documented not only
language features but also their rationale (justification),
at least is difficult, debatable cases.

> It comes up as a problem on a regular base, but wouldn't
> be so hard to "fix" really. I mean it is not broken, it
> works as designed, just not very intuitive.

Now that I know the answer, I am not sure the behavior
should be changed: the must be a way to dictinguish between
local variables of the `run' method, the class fields, and
the binding variables.

> it is actually less the assignment operator itself, it is
> the effect of scopes of dynamic variables. To compare.. in
> Java you have a static scope for a variable.

But I do not know Java and am learning Groovy as a stand-
alone language, not as a Java derivative.

> The compiler knows at any time where a variable is
> declared, and where to write it to (local variable or
> class). In Groovy we also have the static variable scopes,
> but they are "extended" in dynamic Groovy with dynamic
> variables. This means the (meta) class is "asked" for the
> value and existence of a variable at runtime as soon as we
> leave the static scope.

Somewhat similar to metatables in Lua, isn't it?  Why does
not the same thing work from class methods, even from static
class methods? --

   class Boo {
       static def boo()
       {    x = 1
       }
   }

> I would have loved to point to our documentation for this,
> but I actually am unable to find this there.

In another reply, Maarten Boekhold refers to section 3.4
(Variables) of the "Program structure" document, which has
the following to say about assignments to undeclared (or
undefined?) variables:

   if the variable is undeclared, it goes into the script
   binding. The binding is visible from the methods, and is
   especially important if you use a script to interact with
   an application and need to share data between the script
   and the application.

The explanation is OK, but I failed to look into that
document. Do you think you can provide better documentation
at a better place? If so, I should certainly appreciate it.
I also thank Maarten for reply.

Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

Jochen Theodorou
On 14.10.20 17:01, Anton Shepelev wrote:
> Jochen Theodorou to Anton Shepelev:
[...]
>> Frankly... for years we have been defending this position,
>> but now, with so much distance I actually really wonder
>> why we keep this.
>
> Perhaps it would have helped if you had documented not only
> language features but also their rationale (justification),
> at least is difficult, debatable cases.

the reason is easy: it reflects the implementation. But the
implementation can be changed

[...]
>> it is actually less the assignment operator itself, it is
>> the effect of scopes of dynamic variables. To compare.. in
>> Java you have a static scope for a variable.
>
> But I do not know Java and am learning Groovy as a stand-
> alone language, not as a Java derivative.

Sure, any other language you know ;) Python for example has a different
variable scoping system. But most static languages have something like
those static variable scopes.

>> The compiler knows at any time where a variable is
>> declared, and where to write it to (local variable or
>> class). In Groovy we also have the static variable scopes,
>> but they are "extended" in dynamic Groovy with dynamic
>> variables. This means the (meta) class is "asked" for the
>> value and existence of a variable at runtime as soon as we
>> leave the static scope.
>
> Somewhat similar to metatables in Lua, isn't it?

I never worked with Lua, but from what I have seen I would answer yes.

> Why does
> not the same thing work from class methods, even from static
> class methods? --
>
>     class Boo {
>         static def boo()
>         {    x = 1
>         }
>     }

It does work, only a class does not have the special implementation
Script has.

> class AlwaysAnswer{
>   def getProperty(String something){ 42 }
>   def ask(question){ return answer }
> }
>
> def alwaysAnswer = new AlwaysAnswer()
> assert alwaysAnswer.answerToMeaningOfLife == 42
> assert alwaysAnswer.myName == 42
> assert alwaysAnswer.ask("Are you kidding me?") == 42

The class AlwaysAnswer is a real class and exists additionally to the
class we generate for the script itself. You will see that the code
above does not throw any exception, that is because I implemented the
getProperty method, which is used by the meta class system to answer
when asked for an unknown property. For a static method/property we have
something too:
https://docs.groovy-lang.org/latest/html/documentation/core-metaprogramming.html#_static_propertymissing

If you look at
https://github.com/apache/groovy/blob/master/src/main/java/groovy/lang/Script.java#L54
you see that the class Script does implement getProperty method. Script
is normally used as base class for all Groovy scripts. My example above
for example will generate code like this:

>> class AlwaysAnswer{
>>   def propertyMissing(String something){ 42 }
>>   def ask(question){ return answer }
>> }
>>
>> class Scrip1243324 extends groovy.lang.Script{
>>   void run() {
>>      def alwaysAnswer = new AlwaysAnswer()
>>      assert alwaysAnswer.answerToMeaningOfLife == 42
>>      assert alwaysAnswer.myName == 42
>>      assert alwaysAnswer.ask("Are you kidding me?") == 42
>>   }
>> }

It is now possible to let the Groovy compiler use a different base class
for scripts, in which case the runtime behaviour changes and you could
let it disallow those global variables - or allow read before write. Or
the script defines overwrites the getProperty method and defines this
kind of behaviour itself.

[...]

> In another reply, Maarten Boekhold refers to section 3.4
> (Variables) of the "Program structure" document, which has
> the following to say about assignments to undeclared (or
> undefined?) variables:
>
>     if the variable is undeclared, it goes into the script
>     binding. The binding is visible from the methods, and is
>     especially important if you use a script to interact with
>     an application and need to share data between the script
>     and the application.

I think you should read the whole chapter 3
http://groovy-lang.org/structure.html#_scripts_versus_classes Otherwise
you miss out on some of the concepts and I think this passage alone then
makes much less sense. On the other hand Maarten really explain most of
that already in his mail

> The explanation is OK, but I failed to look into that
> document. Do you think you can provide better documentation
> at a better place? If so, I should certainly appreciate it.
> I also thank Maarten for reply.

A better place? What was wrong?

bye Jochen

Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

Anton Shepelev
Jochen Theodorou to Anton Shepelev:

> > Jochen Theodorou:
> >
> > > Frankly... for years we have been defending this
> > > position, but now, with so much distance I actually
> > > really wonder why we keep this.
> >
> > Perhaps it would have helped if you had documented not
> > only language features but also their rationale
> > (justification), at least is difficult, debatable cases.
>
> the reason is easy: it reflects the implementation. But
> the implementation can be changed

The implementation reflects the implementation? I do not
understand.

> > In another reply, Maarten Boekhold refers to section 3.4
> > (Variables) of the "Program structure" document, which
> > has the following to say about assignments to undeclared
> > (or undefined?) variables:
> >
> >    if the variable is undeclared, it goes into the
> >    script binding. The binding is visible from the
> >    methods, and is especially important if you use a
> >    script to interact with an application and need to
> >    share data between the script and the application.
>
> I think you should read the whole chapter 3
> http://groovy-lang.org/structure.html#_scripts_versus_classes
> Otherwise you miss out on some of the concepts and I think
> this passage alone then makes much less sense. On the
> other hand Maarten really explain most of that already in
> his mail

I have read that chapter, and the passage makes perfect
sense. Even normal procedural programming is emulated using
OOP techniques.  Among other things, that chapter says:

   You can also mix methods and code.

Whereas methods are also code, I believe it would be much
clearer if it said "immediate code" -- code that is executed
immediately rather than compiled into a callable method.

> > The explanation is OK, but I failed to look into that
> > document. Do you think you can provide better
> > documentation at a better place? If so, I should
> > certainly appreciate it.  I also thank Maarten for
> > reply.
>
> A better place? What was wrong?

I am not sure, but we both failed to find that paragraph
until Maarten pointed it out. But now that I think about it
again, it is the right place.

Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

Jochen Theodorou
On 15.10.20 12:16, Anton Shepelev wrote:

> Jochen Theodorou to Anton Shepelev:
>
>>> Jochen Theodorou:
>>>
>>>> Frankly... for years we have been defending this
>>>> position, but now, with so much distance I actually
>>>> really wonder why we keep this.
>>>
>>> Perhaps it would have helped if you had documented not
>>> only language features but also their rationale
>>> (justification), at least is difficult, debatable cases.
>>
>> the reason is easy: it reflects the implementation. But
>> the implementation can be changed
>
> The implementation reflects the implementation? I do not
> understand.

specification by implementation I mean

[...]
> Whereas methods are also code, I believe it would be much
> clearer if it said "immediate code" -- code that is executed
> immediately rather than compiled into a callable method.

well.. even scripts are first compiled into a class before the class is
then executed. Groovy has no interpreter

[...]
> I am not sure, but we both failed to find that paragraph
> until Maarten pointed it out. But now that I think about it
> again, it is the right place.

I was searching for something different. A more high-level description
of variable scopes and how they work instead of describing the specific
case of a script with the common base class.

bye Jochen


MG
Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

MG
On 15/10/2020 18:27, Jochen Theodorou wrote:
> well.. even scripts are first compiled into a class before the class is
> then executed. Groovy has no interpreter

Which, I think, is a lesser known fact, and quite surprising to people
who perceive Groovy just under its "script language" aspect ;-)


Reply | Threaded
Open this post in threaded view
|

RE: Defining a global variable

Merlin Beedell
I thought that implicit variables would overcome this.  Not an elegant solution - as you are simply declaring variables on the fly without an explicit data type or even a 'def'.
Having said, it is generally safer to explicitly list the parameters used in a method when used in this context (e.g. in a script without an explicit class declaration to mop up this case).
And as for 'global' - I sure wish the word 'global' was used instead of 'static'.  It just kinda makes more sense to me!
//===========
   test=''  //implicit declaration of a variable
   void func()
   {
      println(test)
   }
   test = 'hello'
   func()
//===========

Or just

//===========
   void func()
   {
      println(test)
   }
   test = 'hello'
   func()  
//===========

Merlin Beedell
-----Original Message-----
From: MG <[hidden email]>
Sent: 15 October 2020 6:21 PM
To: [hidden email]; Jochen Theodorou <[hidden email]>
Subject: Re: Defining a global variable

On 15/10/2020 18:27, Jochen Theodorou wrote:
> well.. even scripts are first compiled into a class before the class
> is then executed. Groovy has no interpreter

Which, I think, is a lesser known fact, and quite surprising to people who perceive Groovy just under its "script language" aspect ;-)


MG
Reply | Threaded
Open this post in threaded view
|

Re: Defining a global variable

MG
Hi Merlin,

I don't know how your reply pertains to what I said, but generally
speaking having implicit variables is imho generally a very poor design
choice, as anyone who has experienced them in e.g. a Basic dialect will
be able to confirm, simply because one innocuous typo can introduce a
hard to track bug into your code, by implicitly defining a new variable
instead of modifying an existing one.

Cheers,
mg


On 21/10/2020 17:46, Merlin Beedell wrote:

> I thought that implicit variables would overcome this.  Not an elegant solution - as you are simply declaring variables on the fly without an explicit data type or even a 'def'.
> Having said, it is generally safer to explicitly list the parameters used in a method when used in this context (e.g. in a script without an explicit class declaration to mop up this case).
> And as for 'global' - I sure wish the word 'global' was used instead of 'static'.  It just kinda makes more sense to me!
> //===========
>     test=''  //implicit declaration of a variable
>     void func()
>     {
>        println(test)
>     }
>     test = 'hello'
>     func()
> //===========
>
> Or just
>
> //===========
>     void func()
>     {
>        println(test)
>     }
>     test = 'hello'
>     func()
> //===========
>
> Merlin Beedell
> -----Original Message-----
> From: MG <[hidden email]>
> Sent: 15 October 2020 6:21 PM
> To: [hidden email]; Jochen Theodorou <[hidden email]>
> Subject: Re: Defining a global variable
>
> On 15/10/2020 18:27, Jochen Theodorou wrote:
>> well.. even scripts are first compiled into a class before the class
>> is then executed. Groovy has no interpreter
> Which, I think, is a lesser known fact, and quite surprising to people who perceive Groovy just under its "script language" aspect ;-)
>
>