OK, since it’s clear that not everyone is immediately convinced by the idea of the *=> operator, let me do a bit of promotion for it.
First, I find the => operator already quite compelling because it places a type check at the start of a predicate, which is very straightforward to write since I don’t need to use the must_be notation. Additionally, it allows me to easily extend the scope of a predicate by simply adding more rules.
Of course, the :- operator could achieve similar results if you accept a logical false instead of an error, but this often leads to silent and hard-to-debug failures.
From a software development perspective, I personally prefer receiving a hard error when my domain boundaries are clearly violated, rather than dealing with a silent failure that’s tricky to track down.
Moreover, the => operator makes it possible to create new predicates by combining existing ones, as illustrated in the following example:
p001(I, O), var(I) => O = var.
p002(I, O), nonvar(I) => O = nonvar.
catchmatch(GOAL) :- catch(GOAL, error(existence_error(matching_rule, _), _), false).
p003(I, O), catchmatch(p001(I, O)) => true.
p003(I, O), catchmatch(p002(I, O)) => true.
Now, imagine that the predicates p001 and p002 can produce multiple results. The *=> operator in rule p003 could simplify things significantly: you get all the results from p001 and p002 without extra effort, while still actively enforcing domain constraints.
p003(I, O), catchmatch(p001(I, O)) *=> true.
p003(I, O), catchmatch(p002(I, O)) *=> true.
Frank Schwidom