<p>Well, these are the &quot;while loops&quot; of Prolog on the one hand, and the inductive definitions of mathematical logic on the other hand (See also: <a href="https://arxiv.org/abs/cs/9301109" rel="nofollow noreferrer">Logic Programming, Functional Programming, and Inductive Definitions</a>, <em>Lawrence C. Paulson, Andrew W. Smith, 2001</em>), so it's not surprising to find them multiple times in a program - syntactically similar, with slight deviations.</p>
<p>In this case, you just have a binary decision - whether something is the case or not - and you &quot;branch&quot; (or rather, decide to not fail the body and press on with the selected clause) on that. The &quot;guard&quot; (the test which supplements the head unification), in this case <code>member(X,Ys)</code> or <code>\+ member(X,Ys)</code> is a binary decision (it also is exhaustive, i.e. covers the whole space of possible <code>X</code>)</p>
<pre><code>intersect([X|Xs],Ys,[X|Acc]) :-   % if the head could unify with the goal
    member(X,Ys),                 % then additionally check that (&quot;guard&quot;)
    (...action...).               % and then do something

intersect([X|Xs],Ys,Acc) :-       % if the head could unify with the goal
    \+ member(X,Ys),              % then additionally check that (&quot;guard&quot;)
    (...action...).               % and then do something
</code></pre>
<p>Other applications may need the equivalent of a multiple-decision switch statement here, and so N&gt;2 clauses may have to be written instead of 2.</p>
<pre><code>foo(X) :-
   member(X,Set1),
    (...action...).

foo(X) :-
   member(X,Set2),
    (...action...).

foo(X) :-
   member(X,Set3),
    (...action...).

% inefficient pseudocode for the case where Set1, Set2, Set3
% do not cover the whole range of X. Such a predicate may or
% may not be necessary; the default behaviour would be &quot;failure&quot;
% of foo/1 if this clause does not exist:

foo(X) :-
   \+ (member(X,Set1);member(X,Set2);member(X,Set3)),
    (...action...).
</code></pre>
<p><em>Note</em>:</p>
<ul>
<li>Use <code>memberchk/2</code> (which fails or succeeds-once) instead of <code>member/2</code> (which fails or succeeds-and-then-tries-to-succeed-again-for-the-rest-of-the-set) to make the program deterministic in its decision whether <code>member(X,L)</code>.</li>
<li>Similarly, &quot;cut&quot; after the clause guard to tell Prolog that if a guard of one clause succeeds, there is no point in trying the other clauses because they will all turn out false: <code>member(X,Ys),!,...</code></li>
<li>Finally, use term comparison <code>==</code> and <code>\==</code> instead of unification <code>=</code> or unification failure <code>\=</code> <code>for delete/3</code>.</li>
</ul>
I have defined multiple predicates that seem to share a common form
<p>All of these predicates are defined in pretty much the same way. The base case is defined for the empty list. For non-empty lists we unify in the head of the clause when a certain predicate holds, but do not unify if that predicate does not hold. These predicates look too similar for me to think it is a coincidence. Is there a name for this, or a defined abstraction?</p>
<pre><code>intersect([],_,[]).
intersect(_,[],[]).
intersect([X|Xs],Ys,[X|Acc]) :-
    member(X,Ys),
    intersect(Xs,Ys,Acc).
intersect([X|Xs],Ys,Acc) :-
    \+ member(X,Ys),
    intersect(Xs,Ys,Acc).
</code></pre>
<pre><code>without_duplicates([],[]).
without_duplicates([X|Xs],[X|Acc]) :- 
    \+ member(X,Acc),
    without_duplicates(Xs,Acc).
without_duplicates([X|Xs],Acc) :-
    member(X,Acc),
    without_duplicates(Xs,Acc).
</code></pre>
<pre><code>difference([],_,[]).
difference([X|Xs],Ys,[X|Acc]) :-
    \+ member(X,Ys),
    difference(Xs,Ys,Acc).
difference([X|Xs],Ys,Acc) :-
    member(X,Ys),
    difference(Xs,Ys,Acc).
</code></pre>
<pre><code>delete(_,[],[]).
delete(E,[X|Xs],[X|Ans]) :-
    E \= X,
    delete(E,Xs,Ans).
delete(E,[X|Xs],Ans) :-
    E = X,
    delete(E,Xs,Ans).
</code></pre>