Have you ever found yourself implementing your own indexing, caching, or database logic inside of your application code? Then surely you know how painful things become when your team experiences bottlenecked performance and obscure production bugs that send waves of anxiety across your entire company.
Perhaps you will be pleased to learn that this wheel has already been invented. A decent enough "elevator pitch" for CLIPS:
CLIPS is a programming language that provides an indexed and cached database automatically based on your application logic. It's algorithmically the most efficient way to implement your application's business logic, full stop.
Have I caught your attention?
I am making these assumptions based on my own experiences working in the software industry for the last 12-ish years. In that time, I've worked at 7 companies at which I've observed common patterns that arise while building web applications:
Has your team implemented a custom:
How about some common organization-wide issues that arise, such as:
If the answer to these questions is "Yes," allow me to introduce you to CLIPS by way of an example.
I'll demonstrate why CLIPS is so powerful with a very simple code example:
CLIPS> (defrule foo (a $?a) (b $?b) (c $?c&$?a|$?b) =>) CLIPS> (watch all) CLIPS> (assert (a 1 2 3 4) (b a b c d) (c a b c d)) ==> f-1 (a 1 2 3 4) ==> f-2 (b a b c d) ==> f-3 (c a b c d) ==> Activation 0 foo: f-1,f-2,f-3 <Fact-3> CLIPS> (assert (a 1 2 5 4) (b a b c l) (c a b c e)) ==> f-4 (a 1 2 5 4) ==> Activation 0 foo: f-4,f-2,f-3 ==> f-5 (b a b c l) ==> f-6 (c a b c e) <Fact-6>
In the above code, I define a rule foo
that detects when 3 facts are in working memory:
(a
with 0 or more values $?a
(b
with 0 or more values $?b
(c
whose values match either $?a
or $?b
We then assert 6 facts into working memory.
Already, we see how CLIPS indexes our data
with ids 1-6
and patterns defined in our foo
rule
as indicated by the Activation
debug message.
In the first set of 3 facts that we assert,
we see that the values a b c d
following
(b
and (c
match.
In the second set,
we see that (a
once again does not match
$?c
. That means facts
4, 2 and 3 activate rule foo
.
Thus, the algorithm determines that foo
needs to run twice based on the data in our application.
What's more: these are only activations. The rules have not yet
run. We can see what's about to happen with (agenda)
:
CLIPS> (agenda) 0 foo: f-4,f-2,f-3 0 foo: f-1,f-2,f-3 For a total of 2 activations.
When we use (run)
, first foo
will FIRE
based on facts 4, 2 and 3, then it will FIRE
for
1, 2 and 3:
CLIPS> (run) FIRE 1 foo: f-4,f-2,f-3 FIRE 2 foo: f-1,f-2,f-3 <== Focus MAIN 2 rules fired Run time is 0.00307297706604004 seconds. 650.834665218403 rules per second. 6 mean number of facts (6 maximum). 0 mean number of instances (0 maximum). 1 mean number of activations (2 maximum).
Also, note the (watch all) command. This causes the helpful debugging messages built into CLIPS to display information about everything happening within the rules engine. They make it super easy to diagnose what's going on when something inevitably behaves unexpectedly.
Finally, CLIPS remembers what has already run for which facts in working memory:
CLIPS> (agenda) CLIPS> (facts) f-1 (a 1 2 3 4) f-2 (b a b c d) f-3 (c a b c d) f-4 (a 1 2 5 4) f-5 (b a b c l) f-6 (c a b c e) For a total of 6 facts.
Asserting a new fact will match on existing facts, but it will not re-activate for previously matched sets of facts:
CLIPS> (assert (a a b c d)) ==> f-7 (a a b c d) ==> Activation 0 foo: f-7,f-2,f-3 ==> Activation 0 foo: f-7,f-5,f-3 <Fact-7>
This demonstrates a very basic example of the caching mechanisms inherent in CLIPS provided by the Rete algorithm.
You should learn CLIPS. You could be writing web-based applications using CLIPS. However, you're creating a complex in-app cache, which is causing your production application to become increasingly sluggish. Relinquish your iron-tight grip on your code and take back control of your application.
- ryjo