paulk-asert commented on code in PR #2489:
URL: https://github.com/apache/groovy/pull/2489#discussion_r3142661609


##########
src/test/groovy/gls/statements/MultipleAssignmentDeclarationTest.groovy:
##########
@@ -226,4 +226,1326 @@ final class MultipleAssignmentDeclarationTest {
             assert baz == 'baz'
         '''
     }
+
+    // GEP-20 tail-rest tests
+
+    @Test
+    void testTailRestFromList() {
+        assertScript '''
+            def (h, *t) = [1, 2, 3, 4]
+            assert h == 1
+            assert t == [2, 3, 4]
+        '''
+    }
+
+    @Test
+    void testTailRestWithMultipleHeads() {
+        assertScript '''
+            def (a, b, *rest) = [10, 20, 30, 40, 50]
+            assert a == 10
+            assert b == 20
+            assert rest == [30, 40, 50]
+        '''
+    }
+
+    @Test
+    void testTailRestWithTypedHead() {
+        assertScript '''
+            def (int h, *t) = [1, 2, 3]
+            assert h == 1
+            assert h instanceof Integer
+            assert t == [2, 3]
+        '''
+    }
+
+    @Test
+    void testTailRestFromEmptyList() {
+        assertScript '''
+            def (h, *t) = []
+            assert h == null
+            assert t == []
+        '''
+    }
+
+    @Test
+    void testTailRestFromString() {
+        assertScript '''
+            def (c, *cs) = "hello"
+            assert c == 'h'
+            assert cs == "ello"
+        '''
+    }
+
+    @Test
+    void testTailRestFromIterator() {
+        assertScript '''
+            def (h, t) = [1, 2, 3].iterator()   // baseline: same iterator 
semantics as existing
+            assert h == 1
+            assert t == 2
+
+            // tail-rest binds the advanced iterator (Path B)
+            def it = [10, 20, 30].iterator()
+            def (head, *tail) = it
+            assert head == 10
+            assert tail instanceof Iterator
+            assert tail.next() == 20
+            assert tail.next() == 30
+            assert !tail.hasNext()
+        '''
+    }
+
+    @Test
+    void testTailRestFromSet() {
+        assertScript '''
+            def s = new LinkedHashSet<>([7, 8, 9])
+            def (sh, *st) = s
+            assert sh == 7
+            def remaining = []
+            while (st.hasNext()) remaining << st.next()
+            assert remaining == [8, 9]
+        '''
+    }
+
+    @Test
+    void testTailRestFromStream() {
+        assertScript '''import java.util.stream.Stream
+            def (first, *rest) = Stream.of('a', 'b', 'c')
+            assert first == 'a'
+            assert rest instanceof Iterator

Review Comment:
   In the GEP, we show these lowerings:                                         
                                                                                
                                                                                
                                                               
                                                                                
                                                                                
                                                                                
                                                                 
   ```
   def (h, *t) = list
   ```
   
   becomes:
   
   ```
   // Path A — RHS supports getAt(IntRange):
   def __rhs = rhs
   def h = __rhs.getAt(0)
   def t = __rhs.getAt(1..-1)
   
    // Path B — RHS iterable but not range-indexable:
    def __it = rhs.iterator()
    def h = __it.hasNext() ? __it.next() : null
    def t = __it
   ```
   For the Stream case, the simplest implementation would just detect 
`instanceof Stream` and produce a lowering something like:
   
   ```
   // Path C — RHS is a Stream:
   def __rhs = rhs
   def __it  = __rhs.iterator()
   def h     = __it.hasNext() ? __it.next() : null
   def t     = StreamSupport.stream(
                   Spliterators.spliteratorUnknownSize(__it, 0),
                   false
               ).onClose { __rhs.close() }
   ```
   And this has some simplifications:
   * The wrapped tail is sequential and reports no spliterator characteristics 
(no SIZED, ORDERED, IMMUTABLE, etc.). Capturing the original characteristics 
requires reading __rhs.spliterator() instead of __rhs.iterator() — but they're 
mutually exclusive terminal-class ops, so head extraction would have to switch 
to Spliterator.tryAdvance, which is implementable but adds machinery that 
doesn't change semantics for the typical destructuring use case. 
   * Primitive streams (IntStream, LongStream, DoubleStream) fall back to the 
iterator() path unless you call .boxed().
   
   I do think the Stream use case probably still justifies this but it 
increases the complexity of the mental model and the education costs in 
describing the limitations. The lowering above is in some sense an 
implementation detail. I will look at changing to above and we can over time 
handle more cases if demand warrants it.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to