Tracking new instances and instance changes should ideally cover both, (a) instance creation and change by Pyke rules (using the oo knowledge base) and (b) creation and change by the Python code run/called by the rules.
Using back-door methods would support the instance creation and change case (b) from above. Ideally, what I'm even aiming at is something like a combination of decorators and my classes in order to reduce the burden on the python code to call these methods when changing any attribute members or within the __init__ methods of a class. But I'm still new to Python and I'm just thinking how this could work. (By the way, in C++ you should not pass an object around until the ctor has completed. Is this the case with Python as well?)
Another issue would be whether calling the back-door methods directly or indirectly from an assert clause affects the forach clause of rules using the new facts. The Pyke compiler is able to build up a dependency graph between fc rules in order to trigger dependent fc rules with the right pattern bindings. But this would'nt work with the back-door methods since it is a runtime issue. Nevertheless, I think it would be feasible to implement this.
[...]
I think that the new knowledge base tracking the instances would pretty much do this (as explained above). But we might also always want an "instance" (or "_instance_", or ???) entity, such that "Feature.instance($feat)" would enumerate the instances (i.e., succeed multiple times, binding "$feat" to a different instance each time). Then when a new instance is created, this would rerun any fc_rules containing it with just the one new instance for "$feat" so that all of the prior instances don't need to be re-processed.Here we would have an implicit fact dependency between Feature.__init__ and Feature.instance which can be resolved at compile time. Just a remark of mine.
[...]
I heard about the metaclass facility in Python and I like your metaclass example. From my first search on the web I could figure out that you can add class methods as well by using metaclasses. Is it somehow possible to add object methods like __setattr__ to classes?
In order to avoid having to subclass from a certain pyke class. In C++ multi-inheritance is estimated as bad possibly implying several problems.
# multi_test.py
class top(object):
def foo(self, indent = 0):
print ' ' * indent + "top.foo"
def bar(self):
print "top.bar"
class left(top):
r'''
>>> l = left()
>>> l.foo() # here left.foo calls top.foo
left.foo
top.foo
>>> l.bar()
top.bar
'''
def foo(self, indent = 0):
print ' ' * indent + "left.foo"
super(left, self).foo(indent + 4)
class right(top):
r'''
>>> r = right()
>>> r.foo()
right.foo
top.foo
'''
def foo(self, indent = 0):
print ' ' * indent + "right.foo"
super(right, self).foo(indent + 4)
def bar(self):
print "right.bar"
class bottom(left, right):
r'''
>>> b = bottom()
>>> b.foo() # here left.foo calls right.foo
bottom.foo
left.foo
right.foo
top.foo
>>> b.bar() # gets right.bar, not left->top.bar
right.bar
'''
def foo(self, indent = 0):
print ' ' * indent + "bottom.foo"
super(bottom, self).foo(indent + 4)
def test():
import sys
import doctest
sys.exit(doctest.testmod()[0])
if __name__ == "__main__":
test()
By the way, is there any good documentation/tutorial on the web about metaclasses in Python?
As you mentioned we have to track somehow attribute changes within the ctor. In these cases we would not like to notify the knowledge base and entity.
We have to take care of attribute changes by the fc assert clauses done by the object kb. The object kb has to assign the new attribute values and implicitly the __setattr__ call tries to update the object base again. Could be that we need an _ignore_kb attribute for each object class.
[...]
Ok. I got it now. But when are dependent foreach clauses triggered? Immediately after updating the knowldedge entities? Or at the end of each assert clause?
As far as the optimizations go, I'd also like to see a real situation
where optimization is needed. In thinking about this, it seems that
what "optimal" would be might differ for different rule sets. For
example, when rule C asserts fact X that re-fires rule A, which asserts
fact Y, which causes rule B to need to be re-fired, should rule B be run
right away? At the end of rule A's assert clause? Or after all of the
other rules affected by rule C's assert clause have been run? Or should
a master list of rules to re-fire be kept for rule C and each new rule
be inserted into the list in the order that it appears in the .krb file
and at the end of rule C's assert clause always execute the first rule
in the list until the list is empty? And each of these different kinds
of "optimizations" seem like they would work better for some rule sets,
but make things worse for others.
So if you have some real examples that you think need optimization help,
we can take a look at them to get a better idea of where the problems
are and how to solve them.
I've been doing some thinking about fact retraction too. I'll post this
in a separate post...
-bruce