Image courtesy of Massimiliano Dessì
You can find the full source code for this blog post in the submarine-examples repository.
Different programming languages are better for different purposes. Imagine how hard would it be to query a database using an imperative language: luckily, we use SQL for that. Now, imagine how useless would a rule engine be, if defining rules were not convenient! This is the reason why Drools comes with its own custom language, the DRL. The Drools Rule Language is in a so-called domain-specific language, a special-purpose programming language specifically designed to make interaction with a rule engine easier.
In particular, a rule is made of two main parts, the condition and the consequence.
The condition is a list of logic predicates, usually pattern matches, while the consequence is written using an imperative language, usually Java.
Rules are what really make a rule engine. After all, that’s what a rule engine does: processing rules. Thus, it might sound kind of logical for the engine to be a bit entangled with the language for rule definitions. Our engine is not specially tied to the DRL; but it used to.
In the last year or so, we spent a lot of time unbundling the innards of the DRL from the guts of the Drools core. The result of this effort is what we called the Canonical Model; that is, an abstract representation of the components that make up a rule engine, including rule definitions. Incidentally, this also paved the way for supporting GraalVM and the Quarkus framework; but our goal was also different. We wanted to abstract our engine from the rule language.
as we said previously, this canonical model is described using Java code. While this representation is not currently intended to be hand-coded, it is very possible to do so. The following is a simple rewriting of the previous DRL rule.
As you can see, although the rule definition is now embedded in a Java “host” language, it still shows the main features of a DRL definition, namely, the logic condition and the imperative consequence (introduced by the on…execute pair) In other words, this is a so-called embedded or internal domain-specific language.
A small disclaimer applies: the code above works, but our translator takes extra steps for best performance, such as introducing indexes. In fact, one of the reasons why we do not intend this API for public consumption is that, currently, a naive rewrite like this may produce inefficient rules.
As part of our journey experimenting with our programming model, we wanted to see whether it was feasible to interact with our engine using different programming languages. DRL aside, the canonical model rule definition API is pure-Java.
Jokes aside, this experiment is a window over one of the many possible futures of our platform. The world of application development today is polyglot. We cannot ignore this, and we are trying to understand how to reach a wider audience with our technologies, be it our rule engine, or our workflow orchestration engine; in fact, we are doing the same experiments with other parts of the platform, such as jBPM.
jBPM provides its own DSL for workflow definition. Although this is, again, work in progress, it shows a lot of promise as well. Behold: jbpm.js!
The DRL has served its purpose for a very long time, but we are already providing different ways to interact with our powerful engine, such as DMN and PMML; but power users will always want to reach for finer tuning and write their own rules.
The canonical model API is still a work-in-progress, and, above all, an internal API that is not intended for human consumption; but, if there is enough interest, we do plan to work further to provide a more convenient embedded DSL for rule definition. Through the power of GraalVM, we will be able to realize an embedded DSL that is just as writable in Java as any other language that GraalVM supports.