This work like a recusive function.
There is two step (1&2) to define a recursive function
my_append(A,B,C) take two list A & B and build a list that is the appending of elements of A and B (call this result C).
Here the third parameter act as an result of the function (that not true but a good first approximation).
1) The base case.
my_append([], L, L).
In the case where the first Liste is empty, trivaly the result is everything that can be put in second parameter.
2) The recursive case.
my_append([H|T], L, [H|NewTail]):-
my_append(T, L, NewTail).
In the case where the first list isn't empty the first list have the form [Head|Tail], and we want that head to be the head of our result (the third arg).
The second line give details on how build the end/tail of the result.
The end/tail of the result is a appending of the shorten first arg and the second arg: This is the recursion; the problem is express using it own definition but on a list witch is one element shorter.
So day after day the first arg is shorter and shorter until the base case match.
Note that if you call my_append() with parameters that aren't bound variable(know variables if you prefers) then the result can be undeterministic : aka each call will return a different solution until there is no more solution.
I call the method like this :
my_append([1,2],[3,4],L)
and it match
my_append([1,2],[3,4],[1,2,3,4])
it's logicals implication are given by :
my_append([1,2],[3,4],L) => match 2)
so the reduction is :
my_append([1,2],[3,4],[1,I])
where my_append([2],[3,4],I) must be reduce.
my_append([2],[3,4],I) => match 2)
so the reduction is :
my_append([2],[3,4],[1,2,J])
where my_append([],[3,4],J) must be reduce.
my_append([],[3,4],K) => match 1)
so the reduction is :
my_append([],[3,4],[3,4]) where all variables are know/bind.
so we "de-stack"
K = [3,4] (base case)
J = [3,4] (recursion case)
I = [2,3,4] (recursion case)
L = [1,2,3,4] (call)