This is one of the most magical things about Prolog.
From my functional/imperative programming experience, when originally picking up Prolog, I conceptualized the kind of operations you describe (e.g., “all splits”) as “running the function backwards” — going from output L to inputs X, Y.
But as you write, the truth is that Prolog is modeling the relation of concatenation.
- The head function (with X unknown and L known): concat([X],_,L).
- Produce all splits of a list (with X and Y unknown): concat(X,Y,L).
- Does this list start with the prefix [a,b]? If yes, let Y be the tail (Y unknown): concat([a,b],Y,L).
... and many more.