We didn't expect a replacement of B.
In[1]:= A = {B};
f[x_, y_] := AppendTo[A, {x, y}];
Do[Print[f @@ B], {B, {{X, Y}}}];
A
During evaluation of In[1]:= {{X,Y},{X,Y}}
Out[4]= {{X, Y}, {X, Y}}
We didn't expect a replacement of B.
In[1]:= A = {B};
f[x_, y_] := AppendTo[A, {x, y}];
Do[Print[f @@ B], {B, {{X, Y}}}];
A
During evaluation of In[1]:= {{X,Y},{X,Y}}
Out[4]= {{X, Y}, {X, Y}}
A workaround is to use lexical (Module) scoping to localize B for Do in addition to the dynamic scoping that Do already has.
Module[{B}, Do[f @@ B, {B, {{X, Y}}}]];
In short, Do does scoping by temporarily changing the value of B. This B is however still the very same symbol both inside and outside of Do, it just has a different value inside of Do.
Module will effectively cause B to become a distinct symbol, different from the one stored in the list A. It achieves this by renaming it.
First, I recommend reading Leonid's amazing answer about scoping constructs. It explains very clearly what the difference between lexical and dynamic scoping is.
The docs say the following about Do:
Doeffectively usesBlockto localize values or variables.
That means Do uses dynamic scoping. That is, during the execution of the Do iteration, B has the value {X, Y} even if it doesn't appear explicitly in the loop. This doesn't quite explain yet why the value of B inside A is still changed after the loop though.
Compare your code:
Do[Print[f @@ B], {B, {{X, Y}}}];
(* {{X, Y}, {X, Y}} *)
A
(* {{X, Y}, {X, Y}} *)
To the following:
Do[Print[A], {B, {{X, Y}}}];
(* {{X, Y}} *)
A
(* {B} *)
The latter isn't too unexpected. When evaluating A for the Print, B has its local value so A evaluates to {{X, Y}} as expected. And afterwards B is undefined again (and A was never changed), so A reverts to {B}.
However, in your code, the B inside A is irretrievably lost. This is because AppendTo doesn't actually modify its first parameter in-place (leaving existing elements untouched), but actually returns a new list and replaces its first parameter with that. This is because lists are immutable. But to do so, AppendTo needs to evaluate A. You can think of AppendTo[A, x] as being defined like this:
A = Append[A, x]
That means, the A on the right-hand side gets evaluated. At this point, B has its value of {X, Y} and hence is part of the result of Append and is now stored in A.
I don't know if there's a viable workaround using (There is, see Szabolcs's nice answer.) Of course, Do which is safe regardless of which variables you've used before.Do is generally frowned upon though, and a better option would be to use a functional style using something like Scan which avoids the need for a named variable altogether:
Scan[AppendTo[A, #] &, {{X, Y}}]
Or better yet, also avoid the side effect:
A = Fold[Append[#, #2] &, A, {{X, Y}}]
(Of course, for this simple case, a simple A = Join[A, {{X, Y}}] would do, but I'm assuming your actual code has a somewhat more elaborate update function.)
Table[{A, B}, {B, {{X, Y}}}]gives{{{{X, Y}}, {X, Y}}}, but at least in this case, afterwards,Ais still{B}. I think fundamentally, this is due toDoandTableusingBlockfor variable localisation, which uses dynamic scoping. There's a great answer of Leonid's on what that means, but it's quite interesting that theBinsideAactually gets evaluated in the course of theAppendTo. That's certainly somewhat unexpected. – Martin Ender Apr 01 '16 at 07:58AppendTo, you can see the same behavior inA = {B}; Do[Print@A, {B, 2}];– Jason B. Apr 01 '16 at 08:02A == {B}after theDothough, whereas in the OPs case, there is noBleft inAafter the loop. – Martin Ender Apr 01 '16 at 08:03AppendToredefines its first argument, exactly as if you saidDo[A = Append[A, B], {B, {1}}];– Jason B. Apr 01 '16 at 08:06