2006-08-03

More about the Rete implementation and object space

In my previous post I gave an example of what I think the Rete object space would look like.


>>> class Foo(object):
... def __init__(self, n):
... self.n = n

>>> rule keepFooNPositive:
... if ?obj:Foo.n <= 0:
... raise Exception('Not allowed')

>>> foo = Foo(42)
>>> foo.n
42
>>> foo.n = -1
Traceback (most recent call last):
File "", line 1, in -toplevel-
raise Exception('Not allowed')
Exception: Not allowed

>>> foo.n
42

There's tons of stuff happening on each line that's not visible to the user (that's the whole point of the object space) but that I feel I should explain anyway.

The first few lines, the Foo class declaration, is not very interesting. It's exactly the same as using the Standard Object Space.

>>> rule keepFooNPositive:
... if ?obj:Foo.n <= 0:
... raise Exception('Not allowed')

The rule declaration, however, is more interesting. What happens is that the rule is split into two pieces: conditions (the if part) and actions (the "then" part). I'm not sure about the syntax here. If it's too much of a hassle to modify the parsing I'll probably just use decorators and normal function declarations:

@rule
def keepFooNPositive:
variables = [('obj', Foo),]
if obj.n <= 0:
raise Exception('Not allowed')

This makes the rule declaration's syntax pass as any Python function and the decorator is left to handle the job translating the function to condition tuples that can be used in the Rule Engine. The translation code has to be written anyway, whether it's run as a decorator or as part of the parsing I don't think I'll care about.

The above function will be converted to something like (in CLIPS-like syntax):

(rule keepFooNPositive
((?obj type Foo)
(?obj n <= 0))
=>
("""raise Exception('Not allowed')"""))

>>> foo = Foo(42)

Creating an instance of the Foo class (foo) also triggers a conversion of the foo instance into WME tuples. The conversion is guided by the Rete alpha network which means that unless an object attribute is referenced in an alpha memory it's not converted. The above would be converted to something similar to:

(_g2383 instanceof Foo)
(_g2383 n 42)

and the Working Memory will have the _g2383 variable bound to the foo instance. Please note that if the Foo class would have contained a hundred other attributes it still wouldn't have created more WMEs since it's only the n attribute that's used in either of the asserted rules.

Apart from translating the foo instance into WMEs, each WME is also asserted and added to the Working Memory. The WME is marshalled by the rules but the actual foo instance isn't. The idea is that the object space intercepts all changes to an object instance, fires all rules and only after that, rewrites the object instance's attribute value. And that's why we get the below result from trying to change foo.n to a negative number (-1)

>>> foo.n = -1
Traceback (most recent call last):
File "", line 1, in -toplevel-
raise Exception('Not allowed')
Exception: Not allowed

>>> foo.n
42

Note that this kind of thing might make an application much more difficult to debug unless you always, always, always make sure that each rule's action contains an Exception to throw if it affects the binding of a value in some way.

Inga kommentarer: