Who is a child of whom, who is allowed, and how a “no” is handled honestly.
graphs.pl · output · proof · check · try it in the playground
We have a small family record: Alice is the parent of Bob and of Carol, and Bob is blocked (say, from some service).
The last two need something new: saying “not” and saying “all”.
The data is written as triples: subject, relation, object.
base(alice, parent_of, bob).
base(alice, parent_of, carol).
base(bob, blocked, true).
t(S, P, O) :- base(S, P, O).
t(C, child_of, P) :+ t(P, parent_of, C).
allowed(C) :+ t(C, child_of, alice), \+ t(C, blocked, true).
children(P, Children) :+ base(P, parent_of, _), findall(C, t(C, child_of, P), Children).
base is the original data, left untouched; t is a combined view of the
data plus what follows from it. \+ means not. findall gathers all
answers into a list.
t(bob, child_of, alice).
t(carol, child_of, alice).
allowed(carol).
children(alice, [bob, carol]).
Bob and Carol are Alice’s children. Only Carol is allowed, because Bob is
blocked. And the list of Alice’s children is [bob, carol].
t — rule 4.absent).findall.[bob, carol] — rule 7.Steps 3 and 5 are different from the others:
Eyedia does not hide this. Each one is recorded as an obligation: a step taken on trust, named in the report.
A separate checker read all 10 steps against the program:
absent (Carol is not
blocked) and one collected (the list [bob, carol] is complete);Verdict: checked_with_obligations.
node bin/eyedia.js examples/graphs.pl # the answers
node bin/eyedia.js --proof examples/graphs.pl # with their proof
Or open it in the playground.
Change base(bob, blocked, true). to base(carol, blocked, true). and run
again: now allowed(bob) appears instead of allowed(carol).
“Not” and “all” are claims about what is missing, and a proof cannot display a missing thing. Eyedia still uses them — and tells you exactly which conclusions rest on them, so you know what you are trusting.