It’s “compare if definitely able, else fail”. Throwing an error sounds excessive.
This seems reasonable:
compare_able(Comp, X, Y) :-
compare_able_([X], [Y], Comp).
compare_able_([], Ys, Comp) :-
compare_able_empty_(Ys, Comp).
compare_able_([X|Xs], Ys, Comp) :-
compare_able_ys_(Ys, Xs, X, Comp).
compare_able_empty_([], =).
compare_able_empty_([_|_], <).
compare_able_ys_([], _, _, >).
compare_able_ys_([Y|Ys], Xs, X, Comp) :-
( X == Y
-> compare_able_(Xs, Ys, Comp)
; ground(X),
ground(Y)
% Can compare with confidence
-> compare(C, X, Y),
compare_able_map_(C, Xs, Ys, Comp)
; nonvar(X),
nonvar(Y),
% Deconstruct
X =.. Xs0,
Y =.. Ys0,
append(Xs0, Xs, Xs1),
append(Ys0, Ys, Ys1),
compare_able_(Xs1, Ys1, Comp)
).
compare_able_map_(<, _, _, <).
compare_able_map_(>, _, _, >).
compare_able_map_(=, Xs, Ys, Comp) :-
compare_able_(Xs, Ys, Comp).
Results:
?- compare_able(C, X, X).
C = (=).
?- compare_able(C, a(X), a(Y)).
false.
?- compare_able(C, a(X), b(Y)).
C = (<).
?- compare_able(C, f(X,9,Y), f(Y,3,Y)).
false.
?- compare_able(C, f(X,9,Y), f(X,3,Y)).
C = (>).
?- compare_able(C, [1,2,X], [1,5,X]).
C = (<).