Short answer: You didn't copy the list to the variable, you gave the same list another name. [The pictures below are a lie. See below the divider -------- for the full story.]
Here's what it looks like:
The list is made of pairs, each of which points to one list item (left half) and to another pair (right half). The right half of the last pair is a special empty-list object.
The variable FOO points to the list, and the SET copies the pointer to BAR:
Why not copy the entire list? One reason is efficiency; even if the list contains a million items, it just takes a tiny, fixed amount of time to copy the pointer. But also, this technique allows for partial sharing of list structure:
-------- dual-format lists
Lists made out of pairs are called linked lists. They're ideal for writing recursive functions, like this:
ITEM 1 OF, ALL BUT FIRST OF, and IN FRONT OF are all fast constant-time functions for linked lists. But ITEM n OF, for arbitrary n, is not a constant-time operation; it takes time proportional to n. So if you write PLURALS in imperative style:
then the total time required for this version of PLURALS using linked lists is proportional to the square of the size of the list. (n calls to ITEM OF, each taking average time n/2.) For this style of programming, it's more efficient to represent the list as a dynamic array, in which ITEM n OF takes constant time regardless of the value of n. But when using dynamic arrays, ALL BUT FIRST OF or IN FRONT OF require making a new copy of the entire list, so arrays take quadratic time for the functional version of PLURALS.
Our goal in designing Snap! was that beginners should be able to write programs in any style, without thinking about how lists are represented, and still get good performance. So, for each list separately, we take note of whether you last used IN FRONT OF and friends, or last used the commands ADD, REPLACE, etc., and change the internal list structure accordingly. As long as you use a list consistently (functionally or imperatively), you get good performance. If you mix and match functional and imperative styles for a single list, we keep converting back and forth, and performance is terrible.
But, getting back to the original question, even for dynamic arrays, when you assign a list value to a variable, only the pointer to the list is copied, not the list itself.
(This is actually true for any value, not just lists, but it's only in the case of lists that anyone notices, because only lists are mutable--they can be changed without making a new copy.)