|
View:
New views
13 Messages
—
Rating Filter:
Alert me
|
|
|
Closure scope and referencesHi,
As a relative newcomer to Groovy, perhaps this is a well-known issue. But I'm confused about the following. When I run this code (which never calls genClosure(): Closure genClosure (var1) { I get this output. caller.scriptVar: 2; scriptVar1: 2; scriptVar2: 11; caller.scriptVar: 4; scriptVar1: 4; scriptVar2: 11; caller.scriptVar: 6; scriptVar1: 6; scriptVar2: 11; caller.scriptVar: 8; scriptVar1: 8; scriptVar2: 11; this.scriptVar: 8; scriptVar: 10; But when closure = genClosure(scriptVar) is allowed to execute, this is the output. caller.scriptVar: 1; scriptVar1: 11; scriptVar2: 11; Can someone explain to me the references? What are: this.scriptVar and scriptVar Why do I get different results when generating the closure in these two different ways? In particular, what are the references of var1 and var2 in genClosure()? Thanks. -- Russ Abbott _____________________________________________ Professor, Computer Science California State University, Los Angeles o Check out my blog at http://russabbott.blogspot.com/ |
|
|
Re: Closure scope and referencesRuss Abbott wrote:
> Hi, > > As a relative newcomer to Groovy, perhaps this is a well-known issue. > But I'm confused about the following. When I run this code (which never > calls genClosure(): > > Closure genClosure (var1) { > Closure closure = {caller, var2 -> > > assert caller.scriptVar.is(scriptVar) > > assert !(caller.scriptVar.is(var1)) > > assert !(caller.scriptVar.is(var2)) > > caller.scriptVar++ > var1++ > var2++ > " caller.scriptVar: ${caller.scriptVar};" + > " scriptVar1: $var1; scriptVar2: $var2;" > > } // endClosure > return closure // from genClosure > > } // end genClosure > > Closure closure = {caller, var2 -> > > assert caller.scriptVar.is(scriptVar) > > assert !(caller.scriptVar.is(var2)) > > caller.scriptVar++ > scriptVar++ > var2++ > " caller.scriptVar: ${caller.scriptVar};" + > " scriptVar1: $scriptVar; scriptVar2: $var2;" > > } // endClosure > > > this.scriptVar = 0 > > def scriptVar = 10 > > // closure = genClosure(scriptVar) > > println closure(this, scriptVar) > > println closure(this, scriptVar) > println closure(this, scriptVar) > > println closure(this, scriptVar) > println "\n this.scriptVar: ${this.scriptVar}; scriptVar: $scriptVar; " > > I get this output. > > caller.scriptVar: 2; scriptVar1: 2; scriptVar2: 11; > caller.scriptVar: 4; scriptVar1: 4; scriptVar2: 11; > caller.scriptVar: 6; scriptVar1: 6; scriptVar2: 11; > caller.scriptVar: 8; scriptVar1: 8; scriptVar2: 11; > > > this.scriptVar: 8; scriptVar: 10; > > But when > > closure = genClosure(scriptVar) > > is allowed to execute, this is the output. > > caller.scriptVar: 1; scriptVar1: 11; scriptVar2: 11; > > caller.scriptVar: 2; scriptVar1: 12; scriptVar2: 11; > > caller.scriptVar: 3; scriptVar1: 13; scriptVar2: 11; > > caller.scriptVar: 4; scriptVar1: 14; scriptVar2: 11; > > > this.scriptVar: 4; scriptVar: 10; > > > Can someone explain to me the references? > What are: this.scriptVar and scriptVar 'this.scriptVar' when used inside a script is the same as 'binding.scriptVar'. It normally wouldn't be considered good practice to use this.scriptVar and also define a scriptVar using 'def' (which gives a local variable) in the same script. If you are trying to associate this with a Java mental model of the world, think of your script as being inside a Groovy-created public static void main method. Variables defined using 'def' will be local variables while there is also a class wide 'map' called binding which this will point you to for non-local property access. > Why do I get different results when generating the closure in these two > different ways? In particular, what are the references of var1 and var2 > in genClosure()? Being a closure like thing, Groovy's Closure's bind unbound variables to the enclosing scope, so var1 will be bound to the method parameter var1 at the time you called genClosure (even though that call is now complete). Perhaps this helps: int x = 4 int y = 30 c = { a -> x++; println "${++a} ${++a}" } c y // => 31 32 c y // => 31 32 println x // => 6 println y // => 30 > -- Russ Abbott > _____________________________________________ > Professor, Computer Science > California State University, Los Angeles > o Check out my blog at http://russabbott.blogspot.com/ --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: Closure scope and referencesOK. I think I see what you're saying about parameters. But I'm still confused. Let's take this version of your example.
class MyInt { def value def incr() { ++value } String toString() {return value} } def x = new MyInt(value: 30) def y = 40 void incr1(a) { println "${a.incr()}" } void incr2(a) { println "${++a}" } incr1 x // => 31 incr2 y // => 41 println "$x $y" // => 31 40 This example does not use Closures at all. When y is passed to incr2, it appears to be passed by value. But since everything is an object, why isn't a pointer to y's value passed and incremented as in the case of x? -- Russ Abbott _____________________________________________ Professor, Computer Science California State University, Los Angeles o Check out my blog at http://russabbott.blogspot.com/ On Fri, Jul 4, 2008 at 3:33 PM, Paul King <paulk@...> wrote:
|
|
|
Re: Closure scope and referencesHere's another version that uses next() as the name of ++ as post-increment. Of the four cases, I understand only the last one. (Or did I make a mistake somewhere?)
class MyInt { def value def next() { value++; } String toString() {return value} } def w = new MyInt(value: 20) def x = new MyInt(value: 30) def y = 40 def z = 50 void incr(a) { println(a++) } println(w++) // => 21. Why not 20? Why isn't w incremented. (See last line.) incr x // => 31. Why not 30? But x is incremented. (See last line.) incr y // => 40. Why isn't y incremented? (See last line.) println(z++) // => 50. This is the only one that makes sense to me. println "$w $x $y $z" // => 20 31 40 51 -- Russ Abbott _____________________________________________ Professor, Computer Science California State University, Los Angeles o Check out my blog at http://russabbott.blogspot.com/ On Fri, Jul 4, 2008 at 7:06 PM, Russ Abbott <russ.abbott@...> wrote: OK. I think I see what you're saying about parameters. But I'm still confused. Let's take this version of your example. |
|
|
Re: Closure scope and referencesIf I may simplify your example a little further, consider this: class MyInt { def value def incr() { ++value } String toString() {return value} } def x = new MyInt(value: 30) def y = 40 void incr1(a) { println "${a.incr()}" } void incr2(a) { println "${a.next()}" } incr1 x // => 31 incr2 y // => 41 println "$x $y" // => 31 40 a.incr() is an operation which changes the original (and happens to also return the changed value). a.next() returns a new object (recall that Integer's are designed to be immutable as far as the Java world is concerned) Now, changing to ++a, that is just a shorthand for a = a.next() So, the 'pointer' a which originally points to y in your example will point to a new Integer after ++a and of course Y is unchanged. Some languages like Ruby make it easier to determine which methods update the original vs return a new thing, e.g. sort vs sort! but at the moment Groovy doesn't follow that because Java has never had it. Perhaps we should consider that style a little more as it might make some things clearer - though if you are using ++ you are striving for brevity over clarity, so perhaps there is not much we can do there. Paul. Russ Abbott wrote: > OK. I think I see what you're saying about parameters. But I'm still > confused. Let's take this version of your example. > > class MyInt { > def value > def incr() { ++value } > String toString() {return value} > } > > def x = new MyInt(value: 30) > def y = 40 > > void incr1(a) { println "${a.incr()}" } > void incr2(a) { println "${++a}" } > > incr1 x // => 31 > incr2 y // => 41 > > println "$x $y" // => 31 40 > > This example does not use Closures at all. When y is passed to incr2, it > appears to be passed by value. But since everything is an object, why > isn't a pointer to y's value passed and incremented as in the case of x? > > > -- Russ Abbott > _____________________________________________ > Professor, Computer Science > California State University, Los Angeles > o Check out my blog at http://russabbott.blogspot.com/ > > On Fri, Jul 4, 2008 at 3:33 PM, Paul King <paulk@... > <mailto:paulk@...>> wrote: > > Russ Abbott wrote: > > Hi, > > As a relative newcomer to Groovy, perhaps this is a well-known > issue. But I'm confused about the following. When I run this > code (which never calls genClosure(): > > Closure genClosure (var1) { > Closure closure = {caller, var2 -> > > assert caller.scriptVar.is > <http://caller.scriptVar.is>(scriptVar) > > assert !(caller.scriptVar.is <http://caller.scriptVar.is>(var1)) > > assert !(caller.scriptVar.is <http://caller.scriptVar.is>(var2)) > > caller.scriptVar++ > var1++ > var2++ > " caller.scriptVar: ${caller.scriptVar};" + " scriptVar1: > $var1; scriptVar2: $var2;" > > } // endClosure > return closure // from genClosure > > } // end genClosure Closure closure = {caller, var2 -> > > assert caller.scriptVar.is > <http://caller.scriptVar.is>(scriptVar) > > assert !(caller.scriptVar.is <http://caller.scriptVar.is>(var2)) > > caller.scriptVar++ > scriptVar++ > var2++ > " caller.scriptVar: ${caller.scriptVar};" + " scriptVar1: > $scriptVar; scriptVar2: $var2;" > > } // endClosure > this.scriptVar = 0 > > def scriptVar = 10 > // closure = genClosure(scriptVar) > println closure(this, scriptVar) > > println closure(this, scriptVar) > println closure(this, scriptVar) > > println closure(this, scriptVar) > println "\n this.scriptVar: ${this.scriptVar}; scriptVar: > $scriptVar; " > > I get this output. > > caller.scriptVar: 2; scriptVar1: 2; scriptVar2: 11; > caller.scriptVar: 4; scriptVar1: 4; scriptVar2: 11; > caller.scriptVar: 6; scriptVar1: 6; scriptVar2: 11; > caller.scriptVar: 8; scriptVar1: 8; scriptVar2: 11; > > this.scriptVar: 8; scriptVar: 10; > > But when > closure = genClosure(scriptVar) > > is allowed to execute, this is the output. > > caller.scriptVar: 1; scriptVar1: 11; scriptVar2: 11; > > caller.scriptVar: 2; scriptVar1: 12; scriptVar2: 11; > > caller.scriptVar: 3; scriptVar1: 13; scriptVar2: 11; > > caller.scriptVar: 4; scriptVar1: 14; scriptVar2: 11; > > this.scriptVar: 4; scriptVar: 10; > > > Can someone explain to me the references? What are: > this.scriptVar and scriptVar > > > 'this.scriptVar' when used inside a script is the same as > 'binding.scriptVar'. > It normally wouldn't be considered good practice to use this.scriptVar > and also define a scriptVar using 'def' (which gives a local variable) > in the same script. > > If you are trying to associate this with a Java mental model of the > world, think of your script as being inside a Groovy-created public > static void main method. Variables defined using 'def' will be local > variables while there is also a class wide 'map' called binding which > this will point you to for non-local property access. > > > Why do I get different results when generating the closure in > these two different ways? In particular, what are the references > of var1 and var2 in genClosure()? > > > Being a closure like thing, Groovy's Closure's bind unbound variables > to the enclosing scope, so var1 will be bound to the method parameter > var1 at the time you called genClosure (even though that call is now > complete). > > Perhaps this helps: > > int x = 4 > int y = 30 > c = { a -> x++; println "${++a} ${++a}" } > c y // => 31 32 > c y // => 31 32 > println x // => 6 > println y // => 30 > > > > -- Russ Abbott > _____________________________________________ > Professor, Computer Science > California State University, Los Angeles > o Check out my blog at http://russabbott.blogspot.com/ > > > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: Closure scope and referencesRuss Abbott wrote:
> OK. I think I see what you're saying about parameters. But I'm still > confused. Let's take this version of your example. > > class MyInt { > def value > def incr() { ++value } > String toString() {return value} > } > > def x = new MyInt(value: 30) > def y = 40 > > void incr1(a) { println "${a.incr()}" } > void incr2(a) { println "${++a}" } > > incr1 x // => 31 > incr2 y // => 41 > > println "$x $y" // => 31 40 > > This example does not use Closures at all. When y is passed to incr2, it > appears to be passed by value. But since everything is an object, why > isn't a pointer to y's value passed and incremented as in the case of x? Because incr2 is effectively doing this: void incr2(a) { a = a + 1; println a } If you want ++ to work on MyInt, you need to define next(): class MyInt { def value def incr() { ++value } Number next() { ++value } String toString() {return value} } def x = new MyInt(value: 30) def y = 40 def z = new MyInt(value: 40) void incr1(a) { println "${a.incr()}" } void incr2(a) { println "${++a}" } incr1 x // => 31 incr2 y // => 41 incr2 z // => 41 println "$x $y $z" // => 31 40 41 Jim --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: Closure scope and referencesThanks for all your help. You're right that I was assuming that ++ changes the actual object rather than re-assigning the variable. That explains why it matters whether the ++ is done at the top level or in a method when the object is an Integer.
This seems to make it clear. def a = 10 assert a++ == 10 // a++ is equivalent to: b = a; a = a.next; return b assert a == 11 assert 10++ == 10 // Generates and discards 11. assert 10.next() == 11 class MyInt { def value boolean equals(other) {other instanceof MyInt && other.value == value} // next() generates and returns a successor object. It doesn't change this one. def next() {return new MyInt(value: value + 1)} } def x = new MyInt(value: 30) assert x++ == new MyInt(value: 30) // x++ is equivalent to: b = x; x = x.next; return b assert x == new MyInt(value: 31) assert new MyInt(value:50)++ == new MyInt(value:50) // Generates and discards new MyInt(value:51) assert new MyInt(value:50).next() == new MyInt(value:51) The basic lesson is that ++ is is intended to apply to variables and not to objects. When applied to an object a new successor object is created and essentially discarded. This makes ++ different from other operators. Is that a fair way to look at it? -- Russ |
|
|
Re: Closure scope and referencesRuss Abbott wrote:
> Thanks for all your help. You're right that I was assuming that ++ > changes the actual object rather than re-assigning the variable. That > explains why it matters whether the ++ is done at the top level or in a > method when the object is an Integer. > > This seems to make it clear. > > def a = 10 > assert a++ == 10 // a++ is equivalent to: b = a; a = a.next; return b > assert a == 11 > > assert 10++ == 10 // Generates and discards 11. > assert 10.next() == 11 > > class MyInt { > def value > > boolean equals(other) {other instanceof MyInt && other.value == value} > > // next() generates and returns a successor object. It doesn't change > this one. > def next() {return new MyInt(value: value + 1)} > } > > def x = new MyInt(value: 30) > > assert x++ == new MyInt(value: 30) // x++ is equivalent to: b = x; x = > x.next; return b > assert x == new MyInt(value: 31) > > assert new MyInt(value:50)++ == new MyInt(value:50) // Generates and > discards new MyInt(value:51) > assert new MyInt(value:50).next() == new MyInt(value:51) > > The basic lesson is that ++ is is intended to apply to variables and not > to objects. When applied to an object a new successor object is created > and essentially discarded. This makes ++ different from other > operators. Is that a fair way to look at it? That is a fair way to look at the convention but you can also do non-conventional behavior if you think you must have it: class MyInt { def value boolean equals(other) {other.value == value} def next() {value++; return this} } def x = new MyInt(value: 30) assert x++ == new MyInt(value: 31) assert x == new MyInt(value: 31) assert new MyInt(value:50)++ == new MyInt(value:50) // Generates and discards new MyInt(value:51) assert new MyInt(value:50).next() == new MyInt(value:51) Cheers, Paul. > -- Russ --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: Closure scope and referencesRuss Abbott schrieb:
[...] > The basic lesson is that ++ is is intended to apply to variables and not > to objects. When applied to an object a new successor object is created > and essentially discarded. This makes ++ different from other > operators. Is that a fair way to look at it? each operator can define if it modifies the object or not. Think of <,>,==,!=. All of them do not modify the object, nor do they return something that is equal to the original object.. they don't have to! << usually modifies the object, but all other operators usually create something new. For example ~ will negate an object, but it will alo create a new object without modifying the old one... if defined like it is usually defined that is. bye blackdrag -- Jochen "blackdrag" Theodorou The Groovy Project Tech Lead (http://groovy.codehaus.org) http://blackdragsview.blogspot.com/ http://www.g2one.com/ --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: Closure scope and referencesThe ++ operator changes the value of the variable to which it is applied. That is, it assigns a new value to the variable. None of the others do. That's what's confusing about it. Furthermore, when applied directly to objects other operators return results that make sense. The "!" operator returns the negation of the object. The ++ operator doesn't do that as currently defined. (It shouldn't be allowed to be applied to objects at all.) In other words, ++ really is different from most operators and quite confusing in its differences.
-- Russ Abbott _____________________________________________ Professor, Computer Science California State University, Los Angeles o Check out my blog at http://russabbott.blogspot.com/ On Tue, Jul 15, 2008 at 4:51 AM, Jochen Theodorou <blackdrag@...> wrote: Russ Abbott schrieb: |
|
|
Re: Closure scope and referencesRuss Abbott schrieb:
> The ++ operator changes the value of the /variable /to which it is > applied. That is, it assigns a new value to the variable. None of the > others do. That's what's confusing about it. there is of course also -- ;) Any +=, -=, <<=, >>= would also do the assignment, only that in these cases you see that. > Furthermore, when applied > directly to objects other operators return results that make sense. The > "!" operator returns the negation of the object. The ++ operator doesn't > do that as currently defined. (It shouldn't be allowed to be applied to > objects at all.) In other words, ++ really is different from most > operators and quite confusing in its differences. ++ is mapped to the method call next(), it is also a unary operator. These two make it different in the first place. Other operators are usually binary and are mapped to other method calls. next() may or may not be a method that modifies the current object instead of creating a new one. If ++ would be a mutating operator, then the later assignment would have no affect, and ++x would be the same as x++. now.. what exactly is the problem with allowing 1++? Only because x=1++ is not the same as x=++1? I still don't get what the confusion is bye blackdrag -- Jochen "blackdrag" Theodorou The Groovy Project Tech Lead (http://groovy.codehaus.org) http://blackdragsview.blogspot.com/ http://www.g2one.com/ --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: Closure scope and referencesMy complaint about allowing 1++ is that ++ is intended to be used variables and not with objects. It simply makes no sense to apply to objects. As such it should be syntactically illegal.
-- Russ Abbott _____________________________________________ Professor, Computer Science California State University, Los Angeles o Check out my blog at http://russabbott.blogspot.com/ On Mon, Jul 21, 2008 at 10:49 AM, Jochen Theodorou <blackdrag@...> wrote: Russ Abbott schrieb: |
|
|
Re: Closure scope and referencesRuss Abbott schrieb:
> My complaint about allowing 1++ is that ++ is intended to be used > variables and not with objects. It simply makes no sense to apply to > objects. As such it should be syntactically illegal. as I said, if there is a mutating next() method on the object, then it could make sense. While we don't encourage this, we do not disallow it for now either. So if it makes sense or not is depending on the point of view... But I tend to disallow it too. bye blackdrag -- Jochen "blackdrag" Theodorou The Groovy Project Tech Lead (http://groovy.codehaus.org) http://blackdragsview.blogspot.com/ http://www.g2one.com/ --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |