Expanding "Unknown procedure" Errors

If I have a predicate

a(a, X) :- ...

and call

b(a,X)

I get an error. b(a,X) is not simply false, it is “my bad”.

If, however, I directly call

a(b,X), that’s not an issue. As coded, this will never match, I’ve either obfuscated a failure into my code, or I have a typo. Prolog semantics suggest the first is the case.

I’m wondering how to best instrument/meta-interpret/compile clauses such that it is an error to have a call that (prior to any unifications) cannot match a (non-dynamic) predicate.

Hello z5h,
if I get your point right you would like that calling a(b, X), having a fact like a(a, X) in your program/database, generates an error message of the kind you get when you call a nonexistent predicate, instead of simply getting a ?- false from the interpreter? Is that the point?

There are two ways. One is to rely on check/0 which performs several sanity checks on your code. One being whether it can find calls for which no clause matches. Of course, this does not deal with calls that are dynamically constructed (such as the toplevel goal).

The other is to use SSU, and write

a(a,X) => ...

SSU is a bit more involved but, for the sake of this discussion, it throws an error if no clause matches. It you want failure in that case you must add a final clause with all variables in the head and fail, e.g.

a(_,_) => fail.

This works fine for SSU as it uses committed choice, i.e., after the first matching head unification no more clauses will be tried. SSU is for writing (semi-)deterministic code.

Related, you can also use det/1:

:- det(a/2).

Where SSU demands a clause to unify, det/1 declares that the predicate must succeed and may not leave a choicepoint. Both are SWI-Prolog specific extensions. In my experience they greatly simplify writing and debugging deterministic code.

4 Likes