Skip to content

Instantly share code, notes, and snippets.

@purplefox
Created March 27, 2012 09:45
Show Gist options
  • Save purplefox/2214446 to your computer and use it in GitHub Desktop.
Save purplefox/2214446 to your computer and use it in GitHub Desktop.
In JavaScript I can do this: (setTimer is just a vertx function which calls the specified handler function at some time (100 ms) in the future.)
var id = vertx.setTimer(100, function() {
stdout.println("id is " + id);
});
This works because the function is a closure, and id is declared (but not initialised) before setTimer is executed.
But if I try to do the same in Groovy it complains that id is not recognised
def id = Vertx.instance.setTimer(100, {
println "is is " + id
})
[junit] groovy.lang.MissingPropertyException: No such property: id for class: core.timer.testClient
I can get it to work in Groovy by declaring id on a separate line, but this is pretty ugly:
def id
id = Vertx.instance.setTimer(100, {
println "is is " + id
})
Just wondering... is this by design? I would expect any local variables declared in the scope before a closure to be accessible in the closure. I understand that id won't be *initialised* before setTimer returns, but does that matter?
@blackdrag
Copy link

purplefox, there are different ways of lazy evaluation. Let us assume we do it in a text replacement way. Then if I have x={y}, and execute x right away, I get an error because y is undefined, or it gives a random value. So I instead give x to another method, that has a local variable y and then execute x there... then x will return the value of that local variable in that method. Or you make it bound to the partially limited scope of for example the method. Then x will return a value, as soon as there is an y declared/used somewhere in this method. Doing that even more strict means to use lexical scopes. In those x can access only what has been defined/used before. And that is what Groovy does.

Why did we choose that? Because we wanted code blocks, that behave, if used the same way as in Java, exactly like in Java. And Java has lexical scopes, thus we have lexical scopes for local variables. We could laso have changed the evaluation and do LHS before RHS. Then the variable would be visible, the big difference is then what "Object id=id" means in Java and in Groovy. In Java this is valid code if there is a field id. With evaluating LHS before RHS this code cannot be valid anymore, since the value for id is id, which is not defined. Imagine now "def id = {id}" what do you expect here? "undefined"?

But you are right, these (not having undefined values; do LHS, before RHS; do lexical binding for local variables) are all design decision and could be done different. It is not that Groovy decided to disallow this case, it is simply that there are currently 3 design decisions speaking against this usage. We made the decisions as they are partially because of Java. Groovy is a language of its own, true. But I can give the same argument for not following the Ruby or JavaScript lead here. The reason why we stick tight to Java is mainly, that we want a tight integration with and an easy migration of Java code and Groovy. There are also limitations of the JVM, but they don't really play a rule for this case here, as long as we don't do eval in the first style I described.

@blackdrag
Copy link

One more on the binding... the binding is to be seen as a scope global to the script, that can be set and changed from outside. A local variable on the other side is not visible outside the block it has been defined in.

@purplefox
Copy link
Author

Good answer :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment