You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by Dmitry Semionin <dm...@gmail.com> on 2015/07/18 23:41:10 UTC
Interpolated strings and embedded closures
Hello everybody.
I'm totally new to Groovy, and thought it would be right to start learning
it by studying the online documentation on the official website. One of the
topics i didn't quite understand was the string interpolation in
conjunction with closures.
The documentation offers the following example:
def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" assert
sOneParamClosure == '1 + 2 == 3'
with the following comment: "Here, the closure takes a single
java.io.StringWriter argument, to which you can append content with the <<
leftShift operator."
I have several questions about the code above:
1. Why is w stated to be of type java.io.StringWriter? Why can't it be of
any other type compatible with the << operator, any integer type for
example?
2. The embedded closure from the code above has an argument, but how would
one pass a parameter to such closure? Would doing it even make sense, or
the embedded closures are some special kind that either takes no params or
a single implicit param of type java.io.StringWriter?
3. If there's no way to make such closures have arguments of arbitrary
types and pass parameters to them, then what's the point in such closures
at all? The doc says that closures have a benefit of a lazy variable
evaluation, and that is important, but one doesn't need a parameterized
closure to achieve it. So what am i missing?
I'll be very grateful for any help you guys could provide!
- Dmitry
Re: Interpolated strings and embedded closures
Posted by Jochen Theodorou <bl...@gmx.org>.
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/
Re: Interpolated strings and embedded closures
Posted by Dmitry Semionin <dm...@gmail.com>.
Hi Jochen, and thanks for the reply.
On Sun, Jul 19, 2015 at 9:14 AM, Jochen Theodorou <bl...@gmx.org> wrote:
>
> parameter = variable (with type) declared in a method/closure
> argument = value (with type) used to call a method/closure
>
Yep, i totally confused the terms 'parameter' and 'argument' with each
other. Thanks for clearing it for me. But, apart from that, the question
still stays.
So it means the closure is called with a value of type StringWriter. The
> parameter can of course have any type that is a parent to StringWriter. No
> type being used means for example Object. But if you supply an incompatible
> type, you will get an exception on call. For example if you give the
> parameter w the type int, then you do have leftShift on there, but the
> method call to the closure itself is still done with a StringWriter and
> will fail.
>
Wait a minute. 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?
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? 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.
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.
So what am i getting wrong?
- Dmitry
Re: Interpolated strings and embedded closures
Posted by Jochen Theodorou <bl...@gmx.org>.
Am 18.07.2015 23:41, schrieb Dmitry Semionin:
> Hello everybody.
>
> I'm totally new to Groovy, and thought it would be right to start
> learning it by studying the online documentation on the official
> website. One of the topics i didn't quite understand was the string
> interpolation in conjunction with closures.
> The documentation offers the following example:
>
> |def sOneParamClosure= "1 + 2 == ${ w -> w << 3}"
> assert sOneParamClosure== '1 + 2 == 3'
>
> |
>
> with the following comment: "||Here, the closure takes a single
> |java.io.StringWriter| argument, to which you can append content with
> the |<<| leftShift operator."
>
> I have several questions about the code above:
>
> 1. Why is |w| stated to be of type |java.io.StringWriter|? Why can't it
> be of any other type compatible with the |<<| operator, any integer type
> for example?
parameter = variable (with type) declared in a method/closure
argument = value (with type) used to call a method/closure
So it means the closure is called with a value of type StringWriter. The
parameter can of course have any type that is a parent to StringWriter.
No type being used means for example Object. But if you supply an
incompatible type, you will get an exception on call. For example if you
give the parameter w the type int, then you do have leftShift on there,
but the method call to the closure itself is still done with a
StringWriter and will fail.
> 2. The embedded closure from the code above has an argument, but how
> would one pass a parameter to such closure? Would doing it even make
> sense, or the embedded closures are some special kind that either takes
> no params or a single implicit param of type |java.io.StringWriter|?
> 3. If there's no way to make such closures have arguments of arbitrary
> types and pass parameters to them, then what's the point in such
> closures at all? The doc says that closures have a benefit of a lazy
> variable evaluation, and that is important, but one doesn't need a
> parameterized closure to achieve it. So what am i missing?
You have argument and parameter wrong. Does my definition of parameter
and argument above explain this as well to you?
bye blackdrag
--
Jochen "blackdrag" Theodorou
blog: http://blackdragsview.blogspot.com/