[Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

classic Classic list List threaded Threaded
47 messages Options
12345
Reply | Threaded
Open this post in threaded view
|

[Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

Daniel.Sun
Hi all,

      In most of cases, we just use GString as an enhanced String, but
currently GString is not implemented eager so it has some weird features,
which will confuse users. Let's look at some examples[1] from Jochen:

* the referenced variables are bound eager
* toString on GString evaluates the GString, this includes toString for the
values
```
def x = 1
def gstring = "$x"
assert gstring == "1"
x = 2
assert gstring == "1"
```
```
class X {
  int i = 0
  String toString(){Integer.toString(i++)}
}
def x = new X()
def gstring = "$x"
assert gstring == "0"
assert gstring == "1" // the content of gstring changed...
```

      If we implement GString eager and treated as normal String(not expose
GString API), the content of GString will not change once it is evaluated.
We can get the following benefits:
1)  Users will not be confused because of some weird feature;
2)  Better compatibility between dynamic and static compilation, SEE
GROOVY-6668[1];
3)  The performance will be improved to some extent because GString will not
be evaluated again and again even if its content is not changed;

     The proposal is a breaking change, so I propose it is targeted to
groovy 3.0.0 if accepted.

P.S. some issues like GROOVY-6668 are raised by users again and again, that
is say, many users are confused by current implementation, i.e. GString can
not be treated as normal String in STC. In the static compilation mode:
```
@groovy.transform.CompileStatic
class Test {
    static void main(String[] args) {
        def m = ['a':1, 'b':2]
        def k = 'a'
        assert 1 == m[k]
        assert 1 == m["$k" as String]
        assert 1 == m["$k"] // fails, gets null, which is not equal to 1
    }
}

```


Cheers,
Daniel.Sun

[1] https://github.com/apache/groovy/pull/708





-----
Daniel Sun
Apache Groovy committer
Blog: http://blog.sunlan.me 
Twitter: @daniel_sun

--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
Daniel Sun
Apache Groovy committer

Blog: http://blog.sunlan.me
Twitter: @daniel_sun
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

paulk_asert
I think a GEP would be the right thing for a change like this. I'd be willing to help try to put it together.
I'd hope we could come up with an approach which allows eager or lazy.
I don't want to get hung up on syntax but perhaps "${x}" could be eager and "${ -> x }"
could be the current semantics or some alternative to this.

But anyway, a GEP could outline the exact proposed change across dynamic
and static Groovy with pros and cons etc.

Cheers, Paul.


On Sat, Sep 8, 2018 at 11:07 AM Daniel.Sun <[hidden email]> wrote:
Hi all,

      In most of cases, we just use GString as an enhanced String, but
currently GString is not implemented eager so it has some weird features,
which will confuse users. Let's look at some examples[1] from Jochen:

* the referenced variables are bound eager
* toString on GString evaluates the GString, this includes toString for the
values
```
def x = 1
def gstring = "$x"
assert gstring == "1"
x = 2
assert gstring == "1"
```
```
class X {
  int i = 0
  String toString(){Integer.toString(i++)}
}
def x = new X()
def gstring = "$x"
assert gstring == "0"
assert gstring == "1" // the content of gstring changed...
```

      If we implement GString eager and treated as normal String(not expose
GString API), the content of GString will not change once it is evaluated.
We can get the following benefits:
1)  Users will not be confused because of some weird feature;
2)  Better compatibility between dynamic and static compilation, SEE
GROOVY-6668[1];
3)  The performance will be improved to some extent because GString will not
be evaluated again and again even if its content is not changed;

     The proposal is a breaking change, so I propose it is targeted to
groovy 3.0.0 if accepted.

P.S. some issues like GROOVY-6668 are raised by users again and again, that
is say, many users are confused by current implementation, i.e. GString can
not be treated as normal String in STC. In the static compilation mode:
```
@groovy.transform.CompileStatic
class Test {
    static void main(String[] args) {
        def m = ['a':1, 'b':2]
        def k = 'a'
        assert 1 == m[k]
        assert 1 == m["$k" as String]
        assert 1 == m["$k"] // fails, gets null, which is not equal to 1
    }
}

```


Cheers,
Daniel.Sun

[1] https://github.com/apache/groovy/pull/708





-----
Daniel Sun
Apache Groovy committer
Blog: http://blog.sunlan.me
Twitter: @daniel_sun

--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

Daniel.Sun
Hi Paul,

    That would be great to get your help :-)

    As we decided to make groovy 3 not completely compatible with previous
versions in the past discussions, I think it's good to think about how to
refine the implementation of `GString` too.

Cheers,
Daniel.Sun



-----
Daniel Sun
Apache Groovy committer
Blog: http://blog.sunlan.me 
Twitter: @daniel_sun

--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
Daniel Sun
Apache Groovy committer

Blog: http://blog.sunlan.me
Twitter: @daniel_sun
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

Paolo Di Tommaso
I would like to raise a flag here.  

Stated the Groovy documentation a Gstring can be resolved both in a eager and lazy manner: 

def number = 1 
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"

assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 1" 

number = 2 
assert eagerGString ==  "value == 1"
assert lazyGString == "value == 2"



The example you are reporting should *already* be reasolved eagerly and I can agree that it can be confusing. 

However the ability to resolve a GString lazily it's a very important Groovy feature for DSL builders. Wipe it out would have a serious impact (read destroy) on existing frameworks that rely on that feature. 


Cheers,
Paolo
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

paulk_asert

I agree that the two cases you provide shouldn't change. I believe what Daniel is describing
might best be considered as an optimisation for the eager case. Rather than having a GString
object with an array of "constant" Strings and an array of "constant" Values, when this case is
determined, it could instead be replaced by a String. You would lose the ability to modify
the GString after creation, e.g. in your example, eagerGString.values[0] = 3.

On Sat, Sep 8, 2018 at 9:26 PM Paolo Di Tommaso <[hidden email]> wrote:
I would like to raise a flag here.  

Stated the Groovy documentation a Gstring can be resolved both in a eager and lazy manner: 

def number = 1 
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"

assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 1" 

number = 2 
assert eagerGString ==  "value == 1"
assert lazyGString == "value == 2"



The example you are reporting should *already* be reasolved eagerly and I can agree that it can be confusing. 

However the ability to resolve a GString lazily it's a very important Groovy feature for DSL builders. Wipe it out would have a serious impact (read destroy) on existing frameworks that rely on that feature. 


Cheers,
Paolo
MG
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

MG
In reply to this post by Daniel.Sun
Hi Daniel,

another flag from me: If I understand you correctly, you want to change
existing GString literal semantics to make it work as if toString() had
implicitely on it; i.e. there would be no more GString literal, but
instead String interpolation support for regular String literals in
double quotes ?

This would be a framework breaking change for me, since I am using the
GString feature to be able to access a GString's embedded objects
extensively in my SQL building framework.

Simple example:

String category = "HEAD"

final Table pe = Table.reference(PERSON)
final Table org = Table.reference(ORG_TREE)

GString sql = """
     select $pe.ID, $pe.FIRST_NAME, $pe.LAST_NAME, $org.DEPARTMENT
     from $pe, $org
     where
         $pe.ORG_ID = $org.ID and $org.CATEGORY = ${BindValue.val(category)}
"""

sql = tableToViewSubstitution(sql) // Replace some table instances with
their View equivalent

sql = renumberReferenceNames(sql) // Iterates over sql.objects,
modifying embedded Table/Column/View/... objects from my framework

SqlExecutor.create(...).rows(sql) // Convert passed sql GString into
Groovy Sql compatible GString

How is the proposed change expected to work with existing Groovy Sql
support for implicitly interpreting embedded objects as bind values
(since this is too restrictive/inflexible for heavy use I have changed
this from implicit to explicit support (BindValue class) in my framework).

Cheers,
mg


On 08.09.2018 03:07, Daniel.Sun wrote:

> Hi all,
>
>        In most of cases, we just use GString as an enhanced String, but
> currently GString is not implemented eager so it has some weird features,
> which will confuse users. Let's look at some examples[1] from Jochen:
>
> * the referenced variables are bound eager
> * toString on GString evaluates the GString, this includes toString for the
> values
> ```
> def x = 1
> def gstring = "$x"
> assert gstring == "1"
> x = 2
> assert gstring == "1"
> ```
> ```
> class X {
>    int i = 0
>    String toString(){Integer.toString(i++)}
> }
> def x = new X()
> def gstring = "$x"
> assert gstring == "0"
> assert gstring == "1" // the content of gstring changed...
> ```
>
>        If we implement GString eager and treated as normal String(not expose
> GString API), the content of GString will not change once it is evaluated.
> We can get the following benefits:
> 1)  Users will not be confused because of some weird feature;
> 2)  Better compatibility between dynamic and static compilation, SEE
> GROOVY-6668[1];
> 3)  The performance will be improved to some extent because GString will not
> be evaluated again and again even if its content is not changed;
>
>       The proposal is a breaking change, so I propose it is targeted to
> groovy 3.0.0 if accepted.
>
> P.S. some issues like GROOVY-6668 are raised by users again and again, that
> is say, many users are confused by current implementation, i.e. GString can
> not be treated as normal String in STC. In the static compilation mode:
> ```
> @groovy.transform.CompileStatic
> class Test {
>      static void main(String[] args) {
>          def m = ['a':1, 'b':2]
>          def k = 'a'
>          assert 1 == m[k]
>          assert 1 == m["$k" as String]
>          assert 1 == m["$k"] // fails, gets null, which is not equal to 1
>      }
> }
>
> ```
>
>
> Cheers,
> Daniel.Sun
>
> [1] https://github.com/apache/groovy/pull/708
>
>
>
>
>
> -----
> Daniel Sun
> Apache Groovy committer
> Blog: http://blog.sunlan.me
> Twitter: @daniel_sun
>
> --
> Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
>

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

Daniel.Sun
Hi MG,

      No worries. This is just a proposal for the eager case of GString. We
will discuss further. According to the sample you provided, we should still
expose GString API.

      P.S.  Since groovy 3 will contain some breaking changes( mostly for
edge cases), it's an opportunity to refine existing implementation of
GString.

Cheers,
Daniel.Sun



-----
Daniel Sun
Apache Groovy committer
Blog: http://blog.sunlan.me 
Twitter: @daniel_sun

--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
Daniel Sun
Apache Groovy committer

Blog: http://blog.sunlan.me
Twitter: @daniel_sun
MG
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

MG
Hi Daniel,

I am (as you probably noticed) with you on the "Groovy 3.0 is the time to reevaluate some things in Groovy". 

Right now I am just not sure if any change in GString semantics would not break my framework, since I not only need to be able to access the GString embedded objects, but also output the current GString state convertd toString() for debug purposes...

Maybe what you propose could be gotten with an extended GString literal syntax, eg an "S"-prefix:

String s = S"x=$x" // equivalent to "x=$x".toString()

or simply a

String getS() { toString() }

property in GString class:

String s = "x=$x".s

?

Cheers,
mg


-------- Ursprüngliche Nachricht --------
Von: "Daniel.Sun" <[hidden email]>
Datum: 09.09.18 00:29 (GMT+01:00)
Betreff: Re: [Proposal] GString is implemented eager and treated as normal   String since groovy 3.0.0

Hi MG,

      No worries. This is just a proposal for the eager case of GString. We
will discuss further. According to the sample you provided, we should still
expose GString API.

      P.S.  Since groovy 3 will contain some breaking changes( mostly for
edge cases), it's an opportunity to refine existing implementation of
GString.

Cheers,
Daniel.Sun



-----
Daniel Sun
Apache Groovy committer
Blog: http://blog.sunlan.me
Twitter: @daniel_sun

--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
MG
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

MG
In reply to this post by Daniel.Sun
Hi Daniel,

I am (as you probably noticed) with you on the "Groovy 3.0 is the time to reevaluate some things in Groovy". 

Right now I am just not sure if any change in GString semantics would not break my framework, since I not only need to be able to access the GString embedded objects, but also output the current GString state convertd toString() for debug purposes...

Maybe what you propose could be gotten with an extended GString literal syntax, eg an "S"-prefix:

String s = S"x=$x" // equivalent to "x=$x".toString()

or simply a

String getS() { toString() }

property in GString class:

String s = "x=$x".s

?

Cheers,
mg


-------- Ursprüngliche Nachricht --------
Von: "Daniel.Sun" <[hidden email]>
Datum: 09.09.18 00:29 (GMT+01:00)
Betreff: Re: [Proposal] GString is implemented eager and treated as normal   String since groovy 3.0.0

Hi MG,

      No worries. This is just a proposal for the eager case of GString. We
will discuss further. According to the sample you provided, we should still
expose GString API.

      P.S.  Since groovy 3 will contain some breaking changes( mostly for
edge cases), it's an opportunity to refine existing implementation of
GString.

Cheers,
Daniel.Sun



-----
Daniel Sun
Apache Groovy committer
Blog: http://blog.sunlan.me
Twitter: @daniel_sun

--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] GString is implemented eager and treated as normal String since groovy 3.0.0

Daniel.Sun
Hi MG,

      The original target of the proposal is to make GString not confuse
users and make GString behave same both in dynamic and static compilation
mode. so it will only introduces breaking changes just in some edge cases.

Cheers,
Daniel.Sun



-----
Daniel Sun
Apache Groovy committer
Blog: http://blog.sunlan.me 
Twitter: @daniel_sun

--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html
Daniel Sun
Apache Groovy committer

Blog: http://blog.sunlan.me
Twitter: @daniel_sun
12345