I appreciate you taking the time. Your approach shows the first steps towards building an inference engine in Python, but these are all very trivial examples. The difference between this and a real logic language will become apparent as the database of facts and rules becomes more complicated.
In order to follow this through, you would need to separate all of search code (i.e. the comprehensions) from the declaration of facts and rules. If you were successful in doing so, you will have made a basic logical inference engine in Python.
Consider one of the queries described in the family tree example: "M is the mother of X if she is a parent of X and is female"
In Prolog this might look like:
mother(X, M) :-
parent(X, M),
female(M).
Having built that rule, I can ask
1) Who is the mother of sophia? : mother(sophia, X).
2) Who is sophia the mother of? : mother(X, sophia).
3) Which mothers have male children? : mother(M, X), male(M).
The question is, how would you define the `mother` relation using comprehensions in a way that is agnostic to the specific questions that will be asked? You would inevitably need to write different loop for each variant of the question. The trick is to write an engine that, given a query, automatically generates the comprehensions required to find the answers. It isn't an easy problem at all.
To make this even clearer, consider your claim that the tools in part 4 are trivial in python, because they are all built-ins. But this is not true at all! Python's built-ins are one-way functions, rather than relations. In python I can ask "what is the sum of the elements in this list?" but I can't as easily ask "what is a list with 30 unique integer elements that sum to 100" To do this in python you would need to put some thought in to how you would generate lists using nested loops so that all of the properties were satisfied. In Prolog you just list the search criteria and it does the work for you:
size(L, 30),
unique(L),
sum(L, 100).
Edit: By the way, check out pyDatalog if you are interested in a fully featured inference engine built in python:
Prolog relaxes the requirement to think linearly, bottom-up. If you write all the facts and predicates bottom-up it's easy in Python. Dictionaries, sets, and comprehensions take care of everything. As you say, it's trivial.
If you want to write top-down, defining what a mother is before defining who are parents and who are female, then you'll need to write functions and it gets trickier in Python. The sticking point is when to transition from functions to dict/set -- what's a predicate and what's a fact. We could get fancy with decorators to create some funky predicate class, but let's stay away from metaprogramming for the moment.
I agree that Python (without getting fancy) requires the programmer to think through a bit more of the inferential logic than Prolog would under some circumstances. How often do those circumstances arise in practical problems? I'm not sure.
However, I've got to accept the challenge: "What is a list with 30 unique integer elements that sum to 100?"
First, there is no list with 30 unique positive integers that sum to 100.
>>> sum(range(30))
435
Allowing negatives would create an infinity of possible solutions. So, I'm going to rephrase the problem: Generate a list of N unique positive integers that sum to M.
>>> from itertools import combinations
>>> m = 30
>>> n = 5
>>> combos = (c for c in combinations(range(m), n) if sum(c) == m)
>>> next(combos)
(0, 1, 2, 3, 24)
In order to follow this through, you would need to separate all of search code (i.e. the comprehensions) from the declaration of facts and rules. If you were successful in doing so, you will have made a basic logical inference engine in Python.
Consider one of the queries described in the family tree example: "M is the mother of X if she is a parent of X and is female"
In Prolog this might look like:
Having built that rule, I can ask1) Who is the mother of sophia? : mother(sophia, X).
2) Who is sophia the mother of? : mother(X, sophia).
3) Which mothers have male children? : mother(M, X), male(M).
The question is, how would you define the `mother` relation using comprehensions in a way that is agnostic to the specific questions that will be asked? You would inevitably need to write different loop for each variant of the question. The trick is to write an engine that, given a query, automatically generates the comprehensions required to find the answers. It isn't an easy problem at all.
To make this even clearer, consider your claim that the tools in part 4 are trivial in python, because they are all built-ins. But this is not true at all! Python's built-ins are one-way functions, rather than relations. In python I can ask "what is the sum of the elements in this list?" but I can't as easily ask "what is a list with 30 unique integer elements that sum to 100" To do this in python you would need to put some thought in to how you would generate lists using nested loops so that all of the properties were satisfied. In Prolog you just list the search criteria and it does the work for you:
Edit: By the way, check out pyDatalog if you are interested in a fully featured inference engine built in python:https://sites.google.com/site/pydatalog/