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 lowering would be 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.



-- 
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