I'm moving the discussion on this out of the Mantis PR #178 for the benefit of all/documentation, and also because it's easier to type and format here.

@LibraSun wrote in that PR:

I'm 99% certain you'll explain this one away as "expected" but I naively believed it would yield different results:

In an expression defined by:

a=[1,2,3],

each i in a:

shift(a)

// result (array) [1,2] (unexpected

// expected [1,2,3]

This has me thinking that 'each' is leaving items on the table, as I tried unsuccessfully pointing out above.

The issue here is that your expression shift(a) is modifying the iteration subject array in place. Basically, here are the iteration steps that are run, starting with a=[1,2,3]

First iteration: a=[1,2,3]; the local i is assigned the first array element 1, but it is not used in the expression. The expression shift(a) results in 1 and causes a to be reduced to [2,3] (the first element is shifted off and becomes the result). Since this is the last value in the expression, 1 is added to the each result array.
Second iteration: a is now [2,3] because the previous iteration modified it. So i is assigned 3, because the iteration is on the second element/iteration through the array, but i is not used in the expression. The shift(a) causes a to be reduced to [3] and its return value is 2, so the value 2 is added to the iteration result array.
Third iteration: a is now [3], and we are on the third iteration, but the iteration index of 3 (third iteration) is now off the end of the array (only 1 element long), so iteration stops.

The result of this each is therefore [1,2] because those are two values that were shifted out, and a is left at [3] because there was no third run of shift() to remove it.

As I said in the PR, modifying an array you are iterating through can be dangerous and confusing in many languages, because iterators keep state during iteration, and some operations you can do inside the iteration can and will invalidate that state in some way. In Lua on Vera, this often leads to deadlocks and crashes so bad that the box reboots, not just a Luup reload. It's a Bad Idea, and programmers who know are very wary of doing this type of thing in any language unless they are certain of the side-effects/lack of side-effects.

Your additional example:

a=[1,2,3], // The array 'a' is [1,2,3]

a=unshift(a,0), // The array 'a' is [0,1,2,3]

a=push(a,4), // The array 'a' is [0,1,2,3,4]

each i in a: // Iterating over the 5 elements of 'a'

shift(a) // Take the first element of 'a' and append it to result array [ ]

// result [0,1,2] (unexpected)

// and array 'a' is now [3,4]! (also unexpected)

Also correct result. All of the gyrations before the each are not relevant to behavior here. At the start of the each, the array is [0,1,2,3,4] (5 elements). Iterations:

a=[0,1,2,3,4], i=0, shift(a) returns 0 and modifies a to [1,2,3,4]; the each result array is now [0]
a=[1,2,3,4], i=2, shift(a) returns 1 and modifies a to [2,3,4]; the each result array is now [0,1]
a=[2,3,4], i=4, shift(a) returns 2 and modifies a to [3,4]; the each result array is now [0,1,2]
Iteration stops because the iteration index/step is 4, but there remain only two elements in a=[3,4] (we're off the end of the array).

So the each result is [0,1,2] and a is left with [3,4] and this is correct operation.

Key points:

each keeps state during the iteration (as most iterators do) about the subject of the iteration; if the subject changes in a way that affects the state, unexpected results may occur;
The functions push(), pop(), shift() and unshift() modify the array in place. If you say b=shift(a) you get a value in b and a modified (shorter) a.