Explaining itself to end-users is a key feature of Expert Systems, in some applications it's absolutely vital. It also sets them apart from the more popular machine learning methods.
The reasoning in Prolog is done through rules with sub-goals, these are essentially queries against a knowledge base. Prolog is homoiconic, meaning we can manipulate these rules (i.e. the language itself) as data in Prolog. Turning these rules into something we can reason about is known as reification. That's most of the jargon!
You'll find Prolog has inbuilt clauses for inspecting code, we're most interested in
clause/2, which unifies with the head and body of a predicate. A quirk of Tau-Prolog, which we're using in your browser means this only works with "public" predicates, therefore we need to declare the predicate as dynamic for the demo to work. Chances are in your dialect you won't need to do this.
That's some pretty ugly reading, but our contrived example covers a good deal of syntax, such that you shouldn't be feeling too lost when expanding on these ideas to make your own explainer.
There's two parts to being a homoiconic language though, not only do we need to be able to get at the language as data, but we need to be able to manipulate it as such. For this job we'll use a DCG.
We're not going to do a deep-dive on DCG's in this post, we do have a few DCG related posts here. If you're in need of a complete tutorial you might want to start with the Learn Prolog Now! DCG chapter.
To manipulate the rule as data into an explanation we need to choose what each of the operators mean in natural language. We can then pattern-match on the operators for the DCG rules. We can also call the body to ensure any variables that can be ground are ground.
A couple of things to watch out for:
We've only got maximum arity 2 predicates, you'll need to decide how you'll handle predicates with more.
Our phrase is not grammatically correct, we get lots of "and"s, should this bother you, it's a fun exercise to recurse through the generated list to replace all but the last "and" with a comma.
We're enforcing that
phrase/2is called only once; for the demo as we only need the first solution and want to aid readability. But this means the order of clauses in our DCG is important, you'll need to add additional guards for robustness.
Also, most Prolog's provide
phrase/3 predicates that should be used, Tau-Prolog doesn't (yet!), so we've written a proxy, chances are you won't need this.
So there we go! Our Prolog can explain itself! Well, to a degree anyway, you'll notice that most Expert Systems seem to implement their own domain-specific query language that they use to generate explanations and to query their data. As it stands our program fails to capture disjunctive clauses that aren't separated by ;. These query languages can limit the expressiveness of the query to what you can explain via your DCG or in some cases provide more operators such as implication, equivalence, universal and existential qualification. Should you wish to implement such a thing, the DCG here should provide you with a useful inspiration for generating your explanations.
Some useful exercises to expand on this further:
Make it grammatically correct without repeated "and"s
When should you end the sentence and start another?
If there's disjunction, can you only explain the succeeding sub-goals?
Can you flip it to explain why a goal doesn't succeed? By only the sub-goals that fail?