-
Notifications
You must be signed in to change notification settings - Fork 42
Le Tigre
Le Tigre is one of the syntaxes in The Ruleby DSL. It defines four parameters for the rule method:
- An optional
Symbol
that is a unique identifier for the rule - An optional
Hash
that contains rule attribute (such as priority) - A variable number of
Strings
representing patterns (the “left hand side”) - A block that will be executed if the rule is satisfied (the “right hand side”)
This syntax is designed to be very human readable, and as a result looks a little like SQL. The following is an example:
rule :hello, ‘For each Message as :m where #status == :HELLO’ do |context|
puts context[:m].message
end
This rule defines a pattern that checks for the existence of a
Message
object with a status property that is equal to :HELLO
. The block parameter will be executed for each object that this pattern matches.
The most important part of the example above is the content of the String parameter, which represents the left hand side of the rule. Each String begins with a universal quantifier. The example above uses 'For each'
but it could use one of the variations accepted by the DSL. These are explained later on in the article.
Following the universal quantifier is a class name and an optional variable name that it is bound to:
Message as :m
This binding allows us to reference the value in another part of the rule.
Following the class and its binding is the where
clause. This part of the pattern is optional. It is only needed if there are certain conditions you need to add to the pattern. The example above has one condition:
#status == :HELLO
The
#
symbol is used to denote that the following value is a method of the class we defined previously. If our rule had more conditions, we could string them together with the #&&
symbol. For example:
#status == :HELLO #&& #message == ‘Hello World!’
The
#&&
operator, which reads as ‘and’, provides a logical concatenation of the conditions.
We can bind the values that methods return to variables in the same way that we bound the object to a variable. This is done again with the as
command. For example:
#status == :HELLO as :s
This would allow us to reference the
:s
variable later on in the rule. For example:
#message == #:s
We again use the
#
character. But this time it is followed by a symbol rather than a method. This symbol is replaced by the value of the status
method when the pattern is compared to a fact.
The conditions in the where
clause are translated into Ruby blocks by the interpreter. Thus, the only requirement is that they must evaluate to true or false. Aside from that, we can do anything in these condition that we can do in Ruby. For example:
#status.reverse =~ /[aeiou](.)\\1/
You can also invoke methods on the variables you reference:
#message == #:s.capitalize
The block parameter to the rule method is less complicated that the left hand side. It is simply a block of Ruby code that will be executed each time the rule is matched. This block has two parameters:
- engine – a handle to the Engine object (used for asserting and retracting facts)
- context – a Hash of bound variables that can be used in the block.
Of course, these parameters can be named whatever the developer desires to call them.
The Hash
that contains the rule attributes typically looks something like the following example:
{ :priority => 5 }
At this time, the only rule attribute that Ruleby supports is
:priority
(a.k.a. ‘salience’).
The quantifiers that begin a pattern in Le Tigre are as follows:
-
For each
-
not?
-
If there exists
andexists?
The ‘For each’
quantifier means that the rule’s action (the right hand side) will be executed for each fact in working memory that the pattern matches.
The not?
quantifier is an “existential quantifier”http://en.wikipedia.org/wiki/Existential_quantification that checks for the non-existence of a fact in working memory.
‘If there exists’
and exists?
have the same semantics. They are existential qualifiers that check for the existence of something in working memory. With this qualifier, the rule’s action will only execute once regardless of how many facts in working memory match the pattern.
In no quantifier is specified, then Ruleby defaults to using 'For each'
.