From the description of flatten/2 :
...because one would generally append elements one-by-one to an difference list, or prepend elements one-by-one to a proper (or even an open list) (an open list is a list terminating in a hole instead of ).
append/3 does nothing surprising (or optimized) either!!
Clicking on the "source code" button on the top right (see above) reveals that it does exactly what you would do when coding append by hand: recurse down the backbone of
List1 until == has been reached, copying it element by element until the "hole" that is at the end position of the copied backbone copy can be unified with
List2. The end!
The name of append/3 is not so well chosen, it's a bit imperative.
A better name would be concatenation/3.
% Freshvar on argpos 3 ?- append([1,2],[a,b],X). X = [1, 2, a, b]. % Freshvar on argpos 2 ?- append([1,2],X,[1,2,a,b]). X = [a, b]. % Freshvar on argpos 1; append/3 is unsure whether there might be a 2nd solution ?- append(X,[a,b],[1,2,a,b]). X = [1, 2] ; false. % Freshvar on argpos 1 and 2; multiple solutions! ?- append(X,Y,[1,2,a,b]). X = , Y = [1, 2, a, b] ; X = , Y = [2, a, b] ; X = [1, 2], Y = [a, b] ; X = [1, 2, a], Y = [b] ; X = [1, 2, a, b], Y =  ; false.