Am 19.07.2015 14:47, schrieb Dmitry Semionin:
[...]
So am i getting it correct that all the embedded closures
that are declared to have a parameter take an implicit argument of the
type StringWriter by design? Because that code snippet i mentioned,
def sOneParamClosure= "1 + 2 == ${ w -> w << 3}"
assert sOneParamClosure== '1 + 2 == 3'
is self-sufficient in the documentation, and these two statements give
no reason to assume that there's some variable around named 'w' of type
StringWriter that was simply bound by the closure. Plus, it would be
illegal syntax-wise because 'w' in the first statement is the name of
the argument, which means it has a local visibility scope and refers to
an argument passed to the closure. Am i wrong here?
{ w -> }
means you declare a "closure" with a parameter of name w. w is not of
type StringWriter, but the closure will be called with a value of type
StringWriter, and that will be accessed through w inside the "closure".
Just imagine you would write this method:
def call(w) { w<<3 }
same deal. In Groovy you don't need always to give a type, which means
Object will be used as the minimally required type. In other words the
method declaration above expands to:
def call(Object w) { w<<3}
And def is an alias for Object so:
Object call(Object w) {w<<3}
Similar for {w->w<<3} becoming {Object w->w<<3}
'w' is the name of the parameter, with local visibility scope and allows
access to the argument of the call.
So either all the parameterized embedded closures do by design take an
implicit argument of type StringWriter, or my question remains: how does
one pass arguments to such closures?
Again you wildly mix arguments and parameters ;) In the example you
showed, there is no implicit parameter. {w->} is a closure with the
explicit parameter w. {->} is a closure without parameter
I mean, let's view it as the manual
tells us. Here -
http://www.groovy-lang.org/syntax.html#_string_interpolation - it says
the following:
The placeholder expressions are surrounded by |${}| or prefixed with |$|
for dotted expressions. The expression value inside the placeholder is
evaluated to its string representation when the GString is passed to a
method taking a String as argument by calling |toString()| on that
expression.
yes, but... "${x}" is a GString that won't contain a closure, while
"${w->w<<x}" is one. The syntax for simple interpolation and
interpolation using closures overlaps
If the embedded closures are a special case of string interpolation and
interpolated strings are evaluated upon conversion to regular Java
strings, then the second statement from the code block above is a place
where such conversion takes place. So if one can somehow pass an
argument to the embedded closure, it should be either here or somewhere
above. But i don't see anything that might count for it.
A GString "foo $bar" (longer form "foo ${bar}"), is a GStringImpl object
in Groovy, implementing the interface GString and basically consists of
the String "foo " and a reference to the value of bar. If you call
toString() on this GString it will execute "foo "+bar.toString() using a
StringWriter. Meaning each toString call, will cause a new evaluation.
A GString "foo ${w->w<<bar}" will be a GStringImpl like above, also
having the String "foo ", but then it will have a closure object stored,
that represents {w->w<<bar}. Instead of doing simply toString() on the
closure, the implementation give the StringWriter directly to the
closure, after writing "foo " into it. The code associated with the
closure will then use << to write the value of bar into the resulting
String.
To show the differences to String and usage of Closure:
class MyX {
String val
String toString() {val}
}
This class will change its toString, whenever val is changed
def x = new MyX(val:"1")
String str0 = "foo "+x // normal String
def str1 = "foo ${x}" // gstring with value of x
def str2 = "foo ${w->w<<x}" // gstring with reference to x
// all seem to be the same from this test
assert str0 == "foo 1"
assert str1 == "foo 1"
assert str2 == "foo 1"
// changing toString result:
x.val = "2"
// String cannot change, since it is complete already
assert str0 == "foo 1"
// GString is "reinterpreted" for each toString
assert str1 == "foo 2"
assert str2 == "foo 2"
//changing x itself
x = new MyX(val:"3")
// String cannot change, since it is complete already
assert str0 == "foo 1"
// GString here uses the old x
assert str1 == "foo 2"
// Gstring here uses the closure, which references always the new x
assert str2 == "foo 3"
You could say there are three levels of laziness in this. The String
version does eager evaluation, so it is done only once. GString evals
every time, but you can store normal objects in there or closures. The
closures on the other hand can do full lazy evaluation.
An alternative implementation for Closure would have been to let it
return a String of course. I guess that would have been more easy to
understand for you. But it is also less powerful.
bye blackdrag
--
Jochen "blackdrag" Theodorou
blog: http://blackdragsview.blogspot.com/