ARL rules examples

The ARL we are going to describe in this section is only used to describe rules to help users to disambiguate BAL.

We describe the most common statements corresponding to the BAL constructs in BAL constructs.

From parameters

BAL and ARL are equivalent in meaning, as you can see in the following comparison. The same rule condition is presented side by side:

BAL ARL
definitions 
   set b to 'the borrower';
if
   the credit score of b is less than 200
then
   add "Credit score below 200" to the messages of 'the loan' ;
   reject 'the loan';
rule `eligibility.minimum credit score` {
   ilog.rules.business_name = "minimum credit score"
   ilog.rules.dt = ""
   ilog.rules.package_name = "eligibility"
   status = "new"
   when {
   miniloan.Borrower() from $EngineData.this.borrower;
   b : miniloan.Borrower() from $EngineData.this.borrower;
   evaluate ( b.creditScore < 200);
   }
   then {
   $EngineData.this.loan.addToMessages("Credit score below 200");
   $EngineData.this.loan.reject();
   }
}

In the previous BAL example, a new variable (b) is defined by writing the following expression: ‘set b to 'the borrower';’. In the Miniloan ServiceParameters, one can see that ‘the borrower’ is the verbalization of the variable ‘borrower’, which is an instance of the BOM class Borrower. Notice also that ‘the loan’ is a verbalization of an instance of Loan.

If you are familiar with Java™, you can see from the previous example that ARL has some particularities:

  • The identifiers are written in free text between back-quotes: as `eligibility.minimum credit score`.
  • The keywords rule and when are unique to ARL. Other examples are: ruleset, signature, evaluate, from, ruleset, match, many, in, flowtask, ruletask, and aggregate. For a complete list of keywords, see ARL keywords.
  • The syntactic construction $EngineData.this allows BOM objects and ruleset parameters to be referenced. See Execution class mapping.
  • The declaration b : miniloan.Borrower() from $EngineData.this.borrower; is called a binding. It means b is a new variable whose value is the ruleset parameter ‘borrower’ referenced by $EngineData.this.borrower.

The ‘if’ expression in BAL corresponds to the ‘when’ expression in the ARL translation. Both languages have a then expression:

BAL ARL
the credit score of b is less than 200
 miniloan.Borrower() from $EngineData.this.borrower;

    b : miniloan.Borrower() from $EngineData.this.borrower;

    evaluate ( b.creditScore < 200);

ARL is designed to express pattern matching, which is the operating principle of the underlying rule engine. The expression miniloan.Borrower() from $EngineData.this.borrower; means that the ruleset parameter ‘borrower’ must be an instance of the class Borrower, the binding: ‘b : miniloan.Borrower()’ means that if an instance of Borrower is matched, it is referred to as ‘b’, finally the expression: ‘evaluate ( b.creditScore < 200);’ returns the value of the expression: ‘b.creditScore < 200’. Considering that the semicolon behaves like a logical and, end-to-end these expressions mean the same as ‘the credit score of b is less than 200’ in a java-like dialect.

Let’s go back to the notation ‘$EngineData.this’. It’s necessary to use such a notation because there are many structures used below the keyword ‘rule’, in particular:

  • EngineData to give access to BOM and ruleset parameters object
  • RunningEngine to give access to methods of the underlying engine

For this reason, the keyword ‘this’ becomes ambiguous. A simple way to lift this ambiguity is to use the prefix ‘this’. See Execution class mapping.

The action of the conditions in both languages (introduced by then) are self-explanatory: If the condition, introduced by when or if, is true, then the corresponding actions are taken, in the ARL version, a series of calls to methods of the ‘Loan’ instance ‘loan’ which is, as told above, a ruleset parameter.

Match object in working memory

What happens when you modify the definitions section? Notice that the from keyword, previously used to reference a parameter, vanishes. Additionally, the binding b : miniloan.Borrower(); acts on each instance of Borrower found in the working memory. (The working memory is the set of objects to which the rule engine is applied.)

BAL ARL
definitions 
   set b to 'the borrower';
if
   the credit score of b is less than 200
then
   add "Credit score below 200" to the messages of 'the loan' ;
   reject 'the loan';
rule `eligibility.minimum credit score` {
  ilog.rules.business_name = "minimum credit score"
  ilog.rules.dt = ""
  ilog.rules.package_name = "eligibility"
  status = "new"
  when {
   b : miniloan.Borrower();
   evaluate ( b.creditScore < 200);
   }
   then {
   $EngineData.this.loan.addToMessages("Credit score below 200");
   $EngineData.this.loan.reject();
   }
}

Collections

Imagine a scenario in which a single loan has several co-borrowers. The loan could be refused if one of the co-borrowers has a low credit score.

BAL ARL
definitions 
   set b to a borrower in 'several co-borrowers' ;
if
   the credit score of  b is less than 200
then
   add "Credit score below 200" to the messages of 'the loan' ;
   reject 'the loan';

 
rule `eligibility.minimum credit score` {
   ilog.rules.business_name = "minimum credit score"
   ilog.rules.dt = ""
   ilog.rules.package_name = "eligibility"
   status = "new"
   when {
   b : miniloan.Borrower() in $EngineData.this.borrowers;
   evaluate ( b.creditScore < 200);
   }
   then {
   $EngineData.this.loan.addToMessages("Credit score below 200");
   $EngineData.this.loan.reject();
   }
}

The variable ‘several co-borrowers’ is the verbalization of the variable ‘borrowers’ defined as a collection of borrowers. Noticed that as borrowers is a collection the binding, ‘b : miniloan.Borrower() in $EngineData.this.borrowers;’ is written with the keyword in instead of from because in this case it is an inclusion.

Binding and loops

The following rule condition defines the variable risky borrowers, which corresponds to borrowers whose yearly income is below 1000. If this list is not empty it prints all its elements. In the following ARL example, the definition of this variable is made by using a binding. The variable name followed by a colon (:), and an expression:

`risky borrowers`:aggregate {
   collect_class_1 : miniloan.Borrower($EngineData.this.borrower.yearlyIncome < 1000);
}

The expression used to define `risky borrowers` is an aggregate (see below). In the action part of the expression, there is a loop iterating over the elements of the just created variable: it has the same syntax as in Java.

Similar to the example above, in the following example, the variable b is defined in ARL by using the binding: b : miniloan.Borrower() in $EngineData.this.borrowers;:

BAL ARL
definitions 
   set 'risky borrowers' to all borrowers 
where
   the yearly income of 'the borrower' is less than 1000; 
if
   the number of objects in 'risky borrowers' is more than 0
then
   for each borrower called b, in ‘risky borrowers’:
   - print  the name of b;
 
rule `doc.called` {
  ilog.rules.business_name = "called"
  ilog.rules.dt = ""
  ilog.rules.package_name = "doc"
  status = "new"
  when {
    miniloan.Borrower() from $EngineData.this.borrower;
    risky borrowers:aggregate {
      collect_class_1 : miniloan.Borrower($EngineData.this.borrower.yearlyIncome < 1000);
    }
    do {
      java.util.ArrayList<miniloan.Borrower>{collect_class_1};
    }
    evaluate ( `risky borrowers`.size() > 0);
  }
  then {
    {
      for (miniloan.Borrower b : `risky borrowers`){
        ilog.rules.brl.System.printMessage(b.name);
      }
      
    }
    
  }
}

Evaluate

The constraints expressed in BAL on a variable are often translated in ARL by using the operator evaluate::
definitions 
   set b to a borrower in 'several co-borrowers' ;
if
   the credit score of  b is less than 200
then
   add "Credit score below 200" to the messages of 'the loan' ;
   reject 'the loan';
 

The constraint on the credit score b is expressed in ARL as evaluate ( b.creditScore < 200);.

The same constraint could be done directly in the binding: b : miniloan.Borrower(b.creditScore < 200) in $EngineData.this.borrowers; This way of expressing constraints is often used in aggregates.

Aggregates

As in database aggregations, aggregates allow to group multiple values together to form a single value. There are various types of aggregates depending on the way values are grouped. The following aggregates are often used in BAL to ARL translations: collections, count, exists, match any.

Collections

Values can be accumulated in collections. For example, in the following rule we want to create a collection regrouping all borrowers whose yearly income is below a given threshold of 1000. To create this collection in ARL, we create a collection aggregate: collect_class_1 : miniloan.Borrower($EngineData.this.borrower.yearlyIncome < 1000); and add it to the ArrayList:

do {
   java.util.ArrayList<miniloan.Borrower>{collect_class_1};
}

The result returned by the previous do statement is bound to the variable of the binding.

BAL ARL
definitions 
   set 'risky borrowers' to all borrowers 
   where
   the yearly income of 'the borrower' is less than 1000; 
if
   there is at most one borrower in 'risky borrowers' 
then
   print "there are risky borrowers";
rule `doc.Collect` {
   ilog.rules.business_name = "Collect"
   ilog.rules.dt = ""
   ilog.rules.package_name = "doc"
   status = "new"
   when {
      miniloan.Borrower() from $EngineData.this.borrower;
      risky borrowers:aggregate {
      collect_class_1 : miniloan.Borrower($EngineData.this.borrower.yearlyIncome < 1000);
      }
      do {
      java.util.ArrayList<miniloan.Borrower>{collect_class_1};
      }
      evaluate ( `risky borrowers`.size() <= 1);
      }
   then {
   ilog.rules.brl.System.printMessage("there are risky borrowers");
   }
}
Count

Aggregation functions such as the count function can be used, for example, in the following rule condition expression to determine whether there are 8 borrowers with a yearly income superior to 1200. The aggregate count function counts the number of borrowers whose yearly income is more than 12000. The where clause attached to the aggregate is similar to the evaluate operator already defined. The boolean results of the where clause is bound to the variable of the binding:

BAL ARL
if
   there are 8 borrowers
   where the yearly income of each borrower is more than 12000,
then
   print "there are at least 8 of them";
rule `doc.There are more` {
   ilog.rules.business_name = "There are more"
   ilog.rules.dt = ""
   ilog.rules.package_name = "doc"
   status = "new"
   when {
      aggregate_1:aggregate {
      collect_1 : miniloan.Borrower(this.yearlyIncome > 12000);
        }
      do {
      count{collect_1};
      }
      where (aggregate_1 == 8);
      }
   then {
      ilog.rules.brl.System.printMessage("there are at least 8 of them");
   }
}

Exists

The operator exists tests that there is at least one item in a collection of items. So, in the following rule testing there is at least one borrower is translated by an 'exists' clause:

BAL ARL
if
   there are 8 borrowers
   where the yearly income of each borrower is more than 12000,
then
   print "there are at least 8 of them";
rule `doc.There is at least one` {
  ilog.rules.business_name = "There is at least one"
  ilog.rules.dt = ""
  ilog.rules.package_name = "doc"
  status = "new"
  when {
     exists {
     miniloan.Borrower(this.borrower.yearlyIncome > 12000 && this.borrower.yearlyIncome < 1000000);
     }
     }
  then {
  ilog.rules.brl.System.printMessage("it exists");
  }
}

Match many

The match many instruction is used to translate decision tables. In the decision table below, the match many instruction follows its semantic. The first case in the first match many corresponds to the first condition debt to income of the first two lines. If the condition is satisfied, an embedded match many evaluates the corresponding conditions on the credit score lines. If the first condition on the credit score line is satisfied (a credit score between 0 and 200), then a then clause describes the action to be taken: the loan is refused.

The other match many evaluates the other conditions of the table and takes the corresponding actions. The conditions corresponding to disabled actions in the decision table do not have corresponding cases in the match many instruction:

The decision table in Rule Designer.

ARL translation of the decision table:

rule `eligibility.repayment and score` {
   ilog.rules.business_name = "repayment and score"
   ilog.rules.dt = "eligibility.repayment and score"
   ilog.rules.package_name = "eligibility"
   status = "new"
   when {
      miniloan.Borrower() from $EngineData.this.borrower;
      evaluate ( $EngineData.this.borrower.yearlyIncome > 0);
      }
   match many {
      case ($EngineData.this.loan != null && ($EngineData.this.borrower != null && [0,30[.contains(($EngineData.this.loan.yearlyRepayment * 100) / $EngineData.this.borrower.yearlyIncome))) : match many {
         case ($EngineData.this.borrower != null && [0,200[.contains($EngineData.this.borrower.creditScore)) : then 1{
            $EngineData.this.loan.addToMessages("debt-to-income too high compared to credit score");
            $EngineData.this.loan.reject();
         }
      }
      case ($EngineData.this.loan != null && ($EngineData.this.borrower != null && [30,45[.contains(($EngineData.this.loan.yearlyRepayment * 100) / $EngineData.this.borrower.yearlyIncome))) : match many {
         case ($EngineData.this.borrower != null && [0,400[.contains($EngineData.this.borrower.creditScore)) : then 3{
            $EngineData.this.loan.addToMessages("debt-to-income too high compared to credit score");
            $EngineData.this.loan.reject();
         }
      }
      case ($EngineData.this.loan != null && ($EngineData.this.borrower != null && [45,50[.contains(($EngineData.this.loan.yearlyRepayment * 100) / $EngineData.this.borrower.yearlyIncome))) : match many {
         case ($EngineData.this.borrower != null && [0,600[.contains($EngineData.this.borrower.creditScore)) : then 5{
            $EngineData.this.loan.addToMessages("debt-to-income too high compared to credit score");
            $EngineData.this.loan.reject();
         }
   }
      case ($EngineData.this.loan != null && ($EngineData.this.borrower != null && ($EngineData.this.loan.yearlyRepayment * 100) / $EngineData.this.borrower.yearlyIncome >= 50)) : if ($EngineData.this.borrower != null && [0,800[.contains($EngineData.this.borrower.creditScore)){
         then 7{
            $EngineData.this.loan.addToMessages("debt-to-income too high compared to credit score");
            $EngineData.this.loan.reject();
         }
      }
   }
}