Details

    • Type: New Feature New Feature
    • Status: Open Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 1.5.1
    • Fix Version/s: None
    • Component/s: groovy-jdk
    • Labels:
      None
    • Testcase included:
      yes
    • Number of attachments :
      0

      Description

      assert 'abc'.contains('a') //1
      assert 'a' in ('abc'as List) //2
      assert 'a' in 'abc' //3 this breaks

      You ask a string if a substring is contained in it (//1). You can do that with the in-Keyword with Lists (//2), too. But you can't do this with the in-Keyword with Strings. It would be a consistent behaviour from Groovy if you could do //3.

        Activity

        Hide
        Paul King added a comment - - edited

        The first of the above examples is actually mirroring contains in Java 5 ,e.g. 'abcd'.contains('bc') so there is no magic conversion to an array or list happening for the first operand. The 3rd example would be doing such magic. While an interesting suggestion, I don't think it is pulling its weight as a new feature. Adding as List or toList() to the example is all that is required and if we were going to make it automatic, it isn't obvious to me if it should behave like the 1st or 2nd examples.

        Recommend we close this as Won't Fix.

        Show
        Paul King added a comment - - edited The first of the above examples is actually mirroring contains in Java 5 ,e.g. 'abcd'.contains('bc') so there is no magic conversion to an array or list happening for the first operand. The 3rd example would be doing such magic. While an interesting suggestion, I don't think it is pulling its weight as a new feature. Adding as List or toList() to the example is all that is required and if we were going to make it automatic, it isn't obvious to me if it should behave like the 1st or 2nd examples. Recommend we close this as Won't Fix.
        Hide
        Bernd Schiffer added a comment -

        @Paul: Yes, you're right, the first and the second example are now actually possible to write in Groovy. It's the third example that would be new.

        You suggests to use as List or toList(), but that's exactly the main point I raised this issue. With as List or toList() you have to write 9 more characters in the example above - whereas there only 12 characters at all with 'a' in 'abc'! So you nearly double the amount of characters. That's one thing. The other thing is, that I like the behaviour of Strings as Lists in some way, like 'abc'[-1] to get the last character of a String. So it would be one more consistent behaviour between Strings and Lists to support the in-Keyword for both. Last but not least: it, the behaviour from example 3, was very obvious to me while I tried to write it that way, but of course didn't work. It feels very natural in the Groovy style.

        Show
        Bernd Schiffer added a comment - @Paul: Yes, you're right, the first and the second example are now actually possible to write in Groovy. It's the third example that would be new. You suggests to use as List or toList() , but that's exactly the main point I raised this issue. With as List or toList() you have to write 9 more characters in the example above - whereas there only 12 characters at all with 'a' in 'abc' ! So you nearly double the amount of characters. That's one thing. The other thing is, that I like the behaviour of Strings as Lists in some way, like 'abc' [-1] to get the last character of a String. So it would be one more consistent behaviour between Strings and Lists to support the in-Keyword for both. Last but not least: it, the behaviour from example 3, was very obvious to me while I tried to write it that way, but of course didn't work. It feels very natural in the Groovy style.
        Hide
        Russel Winder added a comment -

        In Python strings, lists and tuples are all sequences and indexing operates on sequences. The above is leading to doing in Groovy what is done in Python. However, in Python, lists are mutable whereas strings and tuples are immutable. This means it is can be very troublesome writing supposedly polymorphic functions over sequences since there is no static typing to support overloading and you often have to distinguish lists, tuples and strings since the operation set is not the same.

        My point: Although strings and lists appears to both be sequences, the boundary has to be drawn somewhere, the question is where.

        In this case, at least in Java 5.0 and higher, the in operator could be mapped to the use of the contains method. I would therefore propose that for this example lists and strings can be treated polymorphically and so it would be good to do so. I therefore support making this feature work.

        So, I suggest that we leave this open and when Groovy commits to Java 5.0 and above rather than the current Java 1.4 and above, this feature can be added. As Java 1.4 does not have contains in String, I propose no action currently.

        Show
        Russel Winder added a comment - In Python strings, lists and tuples are all sequences and indexing operates on sequences. The above is leading to doing in Groovy what is done in Python. However, in Python, lists are mutable whereas strings and tuples are immutable. This means it is can be very troublesome writing supposedly polymorphic functions over sequences since there is no static typing to support overloading and you often have to distinguish lists, tuples and strings since the operation set is not the same. My point: Although strings and lists appears to both be sequences, the boundary has to be drawn somewhere, the question is where. In this case, at least in Java 5.0 and higher, the in operator could be mapped to the use of the contains method. I would therefore propose that for this example lists and strings can be treated polymorphically and so it would be good to do so. I therefore support making this feature work. So, I suggest that we leave this open and when Groovy commits to Java 5.0 and above rather than the current Java 1.4 and above, this feature can be added. As Java 1.4 does not have contains in String, I propose no action currently.
        Hide
        Bernd Schiffer added a comment -

        @Russel: Why is it a matter of Java 5.0? Ony because of the contains()-Method on Strings? Before Java 5.0 one would have used 'abc'.indexOf('a') != -1, and that's only a stranger syntax than 'abc'.contains('a'). So you could now overload the in operator with indexOf() and if you switch to Java 5.0 you could change the implementation to contains(). This way this issue is not depending on the switch to Java 5.0.

        Show
        Bernd Schiffer added a comment - @Russel: Why is it a matter of Java 5.0? Ony because of the contains() -Method on Strings? Before Java 5.0 one would have used 'abc'.indexOf('a') != -1 , and that's only a stranger syntax than 'abc'.contains('a') . So you could now overload the in operator with indexOf() and if you switch to Java 5.0 you could change the implementation to contains() . This way this issue is not depending on the switch to Java 5.0.
        Hide
        Paul King added a comment -

        I don't think Java 5 is the issue. We already have contains on String. The issue for me is whether the list semantics or string semantics would be what you wanted the shorthand to represent, i.e. would 'a' in 'abc' be a shorthand for 'abc'.contains('a') or 'abc'.toList().contains('a')? At the moment, x in y maps to isCase(x, y) which for strings checks for equality, so that switch(s) can match string case values. So, adding either behavior would definitely be a special case albeit potentially useful.

        Show
        Paul King added a comment - I don't think Java 5 is the issue. We already have contains on String. The issue for me is whether the list semantics or string semantics would be what you wanted the shorthand to represent, i.e. would 'a' in 'abc' be a shorthand for 'abc'.contains('a') or 'abc'.toList().contains('a') ? At the moment, x in y maps to isCase(x, y) which for strings checks for equality, so that switch(s) can match string case values. So, adding either behavior would definitely be a special case albeit potentially useful.
        Hide
        Alexandru Popescu added a comment -

        I do agree with Russel on this. I think Groovy is considering String partially list (f.e. subscript operator), so we should discuss if not full support should be added.

        Show
        Alexandru Popescu added a comment - I do agree with Russel on this. I think Groovy is considering String partially list (f.e. subscript operator), so we should discuss if not full support should be added.
        Hide
        blackdrag blackdrag added a comment -

        what is a bi concerning me is, that switch-case is implemented by calling the isCase method and I think "in" calls it too. And while "{{if ('x' in 'extreme')}" looks ok, I am concerned about

        switch ('x') {
           case 'extreme': println 'match!'; break
           default: printl 'no match'; break
        }
        
        Show
        blackdrag blackdrag added a comment - what is a bi concerning me is, that switch-case is implemented by calling the isCase method and I think "in" calls it too. And while "{{if ('x' in 'extreme')}" looks ok, I am concerned about switch ('x') { case 'extreme': println 'match!'; break default : printl 'no match'; break }
        Hide
        Paul King added a comment -

        To rephrase Jochen's comment. At the moment we have:

        • String only sometimes treated like array/collection (inconsistent)
        • in always calls through to isCase (consistent)

        After this change we would have (perhaps only part way there):

        • String always treated like array/collection (consistent)
        • in always calls through to isCase except for String where it calls through to contains (inconsistent)
        Show
        Paul King added a comment - To rephrase Jochen's comment. At the moment we have: String only sometimes treated like array/collection (inconsistent) in always calls through to isCase (consistent) After this change we would have (perhaps only part way there): String always treated like array/collection (consistent) in always calls through to isCase except for String where it calls through to contains (inconsistent)
        Hide
        Jim White added a comment - - edited

        One equivalence for in should be:

        def lhs = "foo"
        def rhs = "o"
        assert (rhs.any { it == lhs }) == (lhs in rhs)
        

        The change needed here is for in to coerce its RHS to a collection using the same logic that is done for each, collect, any, etc. That appears to be DefaultTypeTransformation.asCollection(o) (DGM.iterator(Object o) via InvokerHelper.asIterator(Object o).

        If in compiles directly to isCase then that is a problem. We'll need an in method.

        Show
        Jim White added a comment - - edited One equivalence for in should be: def lhs = "foo" def rhs = "o" assert (rhs.any { it == lhs }) == (lhs in rhs) The change needed here is for in to coerce its RHS to a collection using the same logic that is done for each , collect , any , etc. That appears to be DefaultTypeTransformation.asCollection(o) ( DGM.iterator(Object o) via InvokerHelper.asIterator(Object o) . If in compiles directly to isCase then that is a problem. We'll need an in method.

          People

          • Assignee:
            Unassigned
            Reporter:
            Bernd Schiffer
          • Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated: