PrologHub

Reification By Example

2019-08-21
Paul Brown

Reification from Strings/Atoms

This is so common in Prolog that you might not even realise you're doing it. Let's say we've got a program and we want it to tell us the calories of various foods.

% A writeln substitute for the browser
:- use_module(library(js)).
writeln(Text) :-
    prop(alert, Alert),
    apply(Alert, [Text], _).

calories(milk) :- writeln('Milk has 110 calories').
calories(egg) :- writeln('An egg has 50 calories').
calories(bagel) :- writeln('A bagel has 354 calories').
calories(milk).

OK, we get the answer we were looking for, but we can't do any reasoning with the calorie information. For example if I have egg on a bagel for lunch with a glass of milk, how many calories would I have consumed? We've lost our data in a string (or for this dialect of Prolog, an atom), we need to reify our calorie count to a number.

% A writeln substitute for the browser
:- use_module(library(js)).
writeln(Text) :-
    prop(alert, Alert),
    apply(Alert, [Text], _).

% Our reified calorie data
cal(milk, 110).
cal(egg, 50).
cal(bagel, 354).

% Using our reified data
sum_calories([], 0).
sum_calories([Food|Tail], Cal) :-
    sum_calories(Tail, C1),
    cal(Food, C2),
    Cal is C1 + C2.

calories(Food) :-
    sum_calories(Food, Cal),
    atomic_list_concat(Food, ', ', Foods),
    atomic_list_concat(['The calorie count for', Foods, 'is', Cal], ' ', Message),
    writeln(Message).
calories([milk, bagel, egg]).

Now we've reified the calorie information, it's something we can use to reason with.

Reification of Relations

As we discussed in Framing Frames, functors in Prolog can't be variables: there's no way to query ?- HowRelated(bee, honey). HowRelated = produces. That's just not valid Prolog. So if you need to reason about relations, as we discussed in Reflexive, Symmetric and Transitive Relations in Prolog, you'll need to reify them:

triple(bee, produces, honey).
triple(bee, livesIn, hive).
triple(bee, armedWith, sting).
triple(bee, HowRelated, honey).

Reification for Multiple Arguments

In knowledge representation it's common to use predicates to denote relations between things, such as for the relationship between a thing and its location: location(spoon, kitchen). When terms get more complicated, for example if our spoon could move, we end up with more than 2 arguments: location(spoon, kitchen, 10). Other common cases are including a probability forecast(wales, rain, high)` and capturing trajectory track(iss, 50, 19, 264, 17135, 2, 1, -0.1, 11:52) .

I've chosen an example that should be quite easy to read as "the location of the spoon is in the kitchen at 10am", but the "at" part of the relation is implicit. In the wild, this implicit relation can cause ambiguity, such as in the track/8 example which contains location, speed, direction and time information for the International Space Station.

So what does reification do for us? In this example we can bundle arguments together into things that describe them. So a location at a time is a 4D interpretation of time: location(spoon, spacetime(kitchen, 10)). Now we have made our 4D representation into a spacetime/2 predicate we can treat that like an object and reason about particular spaces at particular times.

location(spoon, spacetime(kitchen, 10)).
location(spoon, spacetime(diningroom, 11)).
location(fork, spacetime(kitchen, 10)).
% location(fork, spacetime(_, 11)) not defined
location(spade, spacetime(shed, 10)).
location(spade, spacetime(shed, 11)).

% Some spacetime reasoning
location_continues(spacetime(L, T1), spacetime(L, T2)) :-
    T2 > T1.
location_changed(spacetime(L1, T1), spacetime(L2, T2)) :-
    L1 \= L2, T2 > T1.

% Examples using spacetime as a thing
moved_thing(X) :-
    location(X, ST1),
    location(X, ST2),
    location_changed(ST1, ST2).

not_moved_thing(X) :-
    location(X, ST1),
    location(X, ST2),
    location_continues(ST1, ST2).
    
co_located(X, Y, ST) :-
    location(X, ST),
    location(Y, ST),
    X @< Y.

Note, we can't say if the fork has moved, or not moved by 11, we don't know if it even continues to exist at 11, hence the not_moved_thing/1 predicate rather than \+ moved_thing/1. The spoon has moved, the spade has not, and the spoon and fork are co-located at spacetime(kitchen, 10).

moved_thing(X).
not_moved_thing(X).
co_located(X, Y, ST).

Probability has an accepted representation as P(rain) = 0.8 but we don't have functions and we want qualitative values in this example. So we do our weather forecast as: forecast(wales, p(rain, high)). As for the ISS tracking, we have co-ordinates and a vector: track(iss, spacetime(coord(50, 19, 264), 11:52) vector(17135, direction(2, 1, -0.1))). Still not pleasant reading, but much more explicit about the relations between the numbers and we gain all the other reification advantages of being able to now treat these meaningful combinations of arguments as things in their own right.

Conclusion

There are multiple strategies to reification, which is why it can be hard to grasp what is meant by it. In the end it is just turning something you can't reason about into something you can. Fortunately learning Prolog helps to gain an understanding of it because it'll tell you when you can't reason about something, often with a syntax error or by finding yourself stuck at not being able to define what you need.


Tags: reification