Our first task will be to reify the truth value from the success and failure of a predicate in Prolog. So if a predicate succeeds, it should tell us it is true:
tvl(P, true) :- call(P).
That's the easy one.
When a predicate fails in Prolog it can be because that fact can't be proven, or because it is known to be false and encoded to fail.
False values will need to be encoded as such, we shall do so by appending false to their predicates:
same_colour(red, green, false). tvl(P, false) :- call(P, false).
So we're left with only unknown predicates, which in our definition are those that can't be proven. For the sake of backtracking, we'd better make this explicit:
tvl(P, unknown) :- \+ call(P), \+ call(P, false).
As we've defined it, we've not made it explicit that something can't be both true and false! We might have contradictory facts in our database:
grasping(block). grasping(block, false).
If you can guarantee facts can't be contradicting, the above implementation will work. Otherwise we'll need to be more explicit.
We've got our logic values! Now let's add some operators so we can build more complex queries. It's logic, so let's encode the truth-tables for three-valued logic. This is quite interesting and reveals how open-world reasoning can make life difficult: an unknown fact can propagate quickly into many unknown facts in a query.
This kind of reasoning really shines when you integrate it into an expert system that can identify the unknown fact so that an attempt to discover it can be made, or at least alert the human to the missing information. An idea well worth playing with.