I wanted to verify that some predicates have their “primary keys” (in the SQL sense) set up properly … this is the result of 15 minutes of work (with some more time, I could make it a bit easier to use, but I’m lazy). It turns out that subtract/3 doesn’t do quite what I want (it assumes unordered sets but I wanted unordered multisets), so I rolled my own.
%! no_dups(?Tmpl, ^Goal) is semidet. % Looks for duplicate Tmpl's when backtracking over Goal. % Fails if Goal has no results. % Throws an error if duplicate Tmpls. % e.g.: verify that X,Y results are all unique: % no_dups(a(X,Y), Z^pred(X,Y)). :- meta_predicate no_dups(?, ^). no_dups(Tmpl, Goal) :- bagof(Tmpl, Goal, Results), % fails if Goal fails msort(Results, ResultsSorted), sort(Results, ResultsSortedNoDups), ( ResultsSorted == ResultsSortedNoDups -> true ; list_diff(ResultsSorted, ResultsSortedNoDups, ResultsDiff), throw(error(dups(Goal:ResultsDiff), _)) ). no_dups(Goal) :- throw(error(dups(Goal), _)). %! list_diff(+List, +Delete, -Result) is det. % A bit different from library(lists):subtract/3 list_diff(List, , List) :- !. list_diff(List, [D|Ds], Result) :- ( select(D, List, List2) -> list_diff(List2, Ds, Result) ; list_diff(List, Ds, Result) ).