I post here a few comments about toplevel behaviour that I hope you find useful during development and also for usability in later stages. These principles are adhered to in SWI-Prolog and have, among other benefits, helped to uncover many mistakes in user programs.
The basic guideline is the insight that answers by the toplevel ought to be, as far as possible, equivalent to the goal that is being posted.
For example, in SWI-Prolog, the query ?- true. yields true again:
?- true. true.
Note that the answer is again a valid Prolog query, just as the one that was posted. The same extends to any other goal:
?- false. false.
Importantly, this also extends to any type of constraint, such as equality and disequality of terms:
?- X = a. X = a. ?- dif(X, a). dif(X, a).
When multiple solutions are admissible which are reported on backtracking, then the respective residual goals of each disjunct are automatically separated by (;)/2. For example:
?- member(X, [a,b]). X = a ; X = b.
Thus, again, the answer is declaratively equivalent to the query:
?- X = a ; X = b. X = a ; X = b.
One of the most important features is that nondeterminism, i.e., the presence of choice-points is clearly indicated by the toplevel by emitting a space. For example:
?- X = a ; false. X = a .
In this case, I have interrupted the enumeration of any additional answers by pressing RET (instead of SPACE), and this is clearly visible from the output because a space is emitted after X = a. The full answer, available when pressing SPACE, is:
?- X = a ; false. X = a ; false.
For comparison, we can already from the transcript tell that the following succeeds deterministically:
?- X = a. X = a.
Note the absence of space before the dot. This feature has helped to detect many cases of unintended nondeterminism in library predicates, and continues to do so for all user programs.
Thanks for the recommendations Markus! They seem fairly straightforward, I'll try to get them implemented later today.
Awesome, thank you! For completeness, here are SWI commands on the toplevel:
; (n, r, space, TAB): redo t: trace & redo b: break c (a, RET): exit w: write p: print h (?): help
Now that I've given it some actual thought, adding a disjunction operator is not as straightforward as I originally believed.
Is it good practice to implement (;) as
Goal1 ; _Goal2 :- Goal1.
_Goal1 ; Goal2 :- Goal2.
as the SWI-Prolog documentation seems to imply? My guess is no. Currently my strategy is to add support for user-defined operators of all fixities, and to treat some of them as built-ins (among them, (;)) that are specially handled by the code generator.
For a slightly different approach, consider the quote from the SICStus documentation:
Ordinary disjunction,
(P;Q), is treated by the compiler as an anonymous predicate with two clauses, and the execution of a disjunction relies on backtracking to explore the two disjuncts.
Personally, what I would like from a good implementation of disjunction is automatic indexing of unifications. For example, when I use (X = a ; X = b), I would like to obtain deterministic code when X is instantiated to either a or b, while retaining full generality if X is still a free variable. This would make disjunctions a viable inline alternative for clauses to benefit from argument indexing. In this concrete case, you could compile it to:
anon_pred(a). anon_pred(b).
and replace the disjunction by anon_pred(X). First argument indexing (or better, JIT indexing) makes this call deterministic if applicable.
I have one additional comment on why I always mention SPACE explicitly: This is because ; (semicolon) is much harder to type than SPACE on most European keyboard layouts, so having SPACE also available to ask for alternatives makes interaction with the toplevel a lot more convenient for affected users.
I did try to switch ; over to SPACE a few weeks ago. The trouble is that Termion, the library that enables key captures, does not support capturing SPACE (yet, I hope). I might open an issue with the Termion maintainer on that.
The switch has been completed. Termion could detect spaces all along, I was just being very dense about it.
I think I will close the issue, if everything looks good.
Perfect, thank you!