Expanding my comment, Logtalk's solution is simple. First, define a root object with a family relationship predicate:
:- object(familytree). :- public([ father/2, mother/2, sister/2, brother/2 ]). :- public([ parent/2, male/1, female/1 ]). father(Father, Child) :- ::male(Father), ::parent(Father, Child). mother(Mother, Child) :- ::female(Mother), ::parent(Mother, Child). sister(Sister, Child) :- ::female(Sister), ::parent(Parent, Sister), ::parent(Parent, Child), Sister \== Child. brother(Brother, Child) :- ::male(Brother), ::parent(Parent, Brother), ::parent(Parent, Child), Brother \== Child. :- end_object.
Note that the search for the definitions of male/1 , female/1 and parent/2 starts on its own, i.e. in an object, a database that will receive requests for family relationships. An example derived from your sample code:
:- object(simpsons, extends(familytree)). male(homer). male(bart). female(marge). female(lisa). parent(homer, bart). parent(homer, lisa). parent(marge, bart). parent(marge, lisa). :- end_object.
An example request could be:
?- simpsons::parent(homer, Child). Child = bart ; Child = lisa.
You can use as many family databases as you want, load them at the same time, and define their specializations as you wish. For instance:
:- object(simpsons_extended, extends(simpsons)). male(Male) :- ^^male(Male). male(abe). male(herb). female(Male) :- ^^female(Male). female(gaby). female(mona). parent(Parent, Child) :- ^^parent(Parent, Child). parent(abe, homer). parent(abe, herb). parent(gaby, herb). parent(mona, homer). :- end_object.
This solution meets all your requirements. SWI-Prolog is one of the supported Prolog compilers. You can install Logtalk using its installers. Alternatively, for SWI-Prolog you can simply enter:
?- pack_install(logtalk).
Update
In your commentary on this decision, you asked about entering a database into the logic of family tree objects. It is easy, but it also requires a different approach. First define familytree as:
:- object(familytree). :- public([ father/2, mother/2, sister/2, brother/2 ]). :- public([ parent/2, male/1, female/1 ]). :- multifile([ parent/2, male/1, female/1 ]). father(Father, Child) :- male(Father), parent(Father, Child). mother(Mother, Child) :- female(Mother), parent(Mother, Child). sister(Sister, Child) :- female(Sister), parent(Parent, Sister), parent(Parent, Child), Sister \== Child. brother(Brother, Child) :- male(Brother), parent(Parent, Brother), parent(Parent, Child), Brother \== Child. :- end_object.
Note that this is an alternative, we call male/1 , female/1 and parent/2 as local predicates, but they are also declared as set predicates. Now we need to โenterโ the family database into the familytree object:
:- category(simpsons). :- multifile([ familytree::male/1, familytree::female/1, familytree::parent/2 ]). familytree::male(homer). familytree::male(bart). familytree::female(marge). familytree::female(lisa). familytree::parent(homer, bart). familytree::parent(homer, lisa). familytree::parent(homer, maggie). familytree::parent(marge, bart). familytree::parent(marge, lisa). familytree::parent(marge, maggie). :- end_category.
Usage example (assuming familytree.lgt and simpsons.lgt files):
?- {familytree, simpsons}. ... yes
A few sample queries:
?- familytree::parent(homer, Child). Child = bart ; Child = lisa ; Child = maggie. ?- familytree::male(Male). Male = homer ; Male = bart. ?- familytree::father(Father, Child). Father = homer, Child = bart ; Father = homer, Child = lisa ; Father = homer, Child = maggie ; false.