groovy-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jochen Theodorou <blackd...@gmx.org>
Subject Re: Interpolated strings and embedded closures
Date Sun, 19 Jul 2015 13:28:22 GMT
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/


Mime
View raw message