Running Python Code¶
The rules in the previous example (see Getting Started) were pure CLIPS rules, that did not involve any Python code during the engine run. Rulu also provides easy ways to integrate with Python code, as explained here.
Python Actions¶
We can write the entire action portion of a rule in python. For example, compare the declarative (CLIPS) rule from the previous section:
IsGrandfatherOf = RuleDef(
match(IsFatherOf[1].son, IsFatherOf[2].father),
action(Assert(grandfather=IsFatherOf[1].father, grandson=IsFatherOf[2].son, degree=0))
)
With the following procedural (Python) form:
IsGrandfatherOf = RuleDef(
fields(grandfather=String, grandson=String, degree=Integer),
match(IsFatherOf[0].son, IsFatherOf[1].father)
)
@IsGrandfatherOf._python_action
def action(assert_, IsFatherOf):
assert_(grandfather=IsFatherOf[0].father, grandson=IsFatherOf[1].son, degree=0)
Notes:
- Python actions are defined using the
_python_action
decorator. - The function’s parameters include an
assert_
function to create new facts, as well as all the input facts contained in the rule (in this caseIsFatherOf
accessible as a dictionary). - Note the explicit fields declaration. In the previous example, Rulu could automatically
deduce the target fields from the
Assert
statement. Here the fact assertion is done in run-time, so the fields cannot be deduced up front and need to be declared.
Python Functions¶
We can use Python code even when writing rules using the declarative (CLIPS) form. The functions need to be registered in advance, as follows:
@RuleFunc
def increment(x):
return x+1
@RuleFunc
def take_father(x):
return x.father
RuleDef(
target(IsGrandfatherOf),
match(IsFatherOf.son, IsGrandfatherOf.grandfather),
action(Assert(grandfather=take_father(IsFatherOf), grandson=IsGrandfatherOf.grandson, greatness=increment(IsGrandfatherOf.greatness)))
)
Note: in this case the function return types can be deduced from context. In other cases
they have to be declared explicitly using @IntegerRuleFunc
, StringRuleFunc
etc.