There is no explicit assignment of NewTail and NewHead to the output list.
There are no assignments at all, Prolog uses unification. This is important, because unification plays the role of both analyzing existing data structures and building new ones. This is different from programming languages that have assignments.
It is also important to know that putting terms inside a clause head is an implicit unification. So this:
add1([Head|Tail],[NewHead|NewTail]) :-
...
is equivalent to this:
add1(List1, List2):-
List1 = [Head | Tail],
List2 = [NewHead | NewTail],
...
(But the first form can sometimes be executed more efficiently.)
Now, for the two roles of unification, consider this predicate:
list_head_tail([Head | Tail], Head, Tail).
which can also be written as:
list_head_tail(List, Head, Tail) :-
List = [Head | Tail].
Due to the properties of unification, this can work in different ways. We can use it to "construct" a list:
?- list_head_tail(List, a, [b, c]).
List = [a, b, c].
but also to "deconstruct" an existing list:
?- List = [1, 2, 3], list_head_tail(List, Head, Tail).
List = [1, 2, 3],
Head = 1,
Tail = [2, 3].
Note that this binds Head and Tail to values even though "there is no explicit assignment" of either of these variables in the body of list_head_tail/3 predicate. Unification can bind variables to values in this way.
So to answer your question, when you call add1([1, 2, 3, 4], NewList) the following things happen to NewList:
- it is unified with
[NewHead | NewTail]
NewHead is unified with a new value; if you use is for arithmetic, NewHead is unified with 2, so the value of NewList is now [2 | NewTail]
- in the recursive call to
add1, NewTail is unified with a new term [NewHead2 | NewTail2] (I'm using renamed (freshly named) variables here as Prolog itself in effect does)
NewHead2 is unified with 3, so your outermost (or topmost) NewList is now [2 | [3 | NewTail2]] = [2, 3 | NewTail2]
- some more recursive calls to
add1 happen, progressively setting up NewHead3 and NewTail3, NewHead4 and NewTail4, etc. -- I'll skip them here
- in the final recursive call, the last
NewTail_n variable is unified with [] and the chain of add1 calls is thus finished
- at this point
NewList = [2 | [3 | [4 | [5 | []]]]], constructed "from the outside in" (i.e. top-down); this term is equivalent to the more readable syntax [2, 3, 4, 5]