Bruce Silver's blog post - Make Way for B-FEEL
Bruce Silver
Blog

Make Way for B-FEEL

By Bruce Silver

Read Time: 5 Minutes

The FEEL language has been criticized for not being quite as business-friendly as its originators hoped.

One particular target of this criticism has been FEEL’s use of the value null to mean both a missing value and an execution error. Now DMN 1.6 tries to improve things with the introduction of a new dialect of FEEL called B-FEEL, for “Business-Friendly Expression Language”. The B-FEEL grammar – the formal rules of parsing the language in a compiler – are the same as for regular FEEL, but the semantics – the computed value – of certain expressions involving null or invalid arguments are different, more in line with what most users expect.

Here is a simple example:

In FEEL, the expression “a” = 1 returns null  It is an execution error, since you cannot compare a text value to a number. In B-FEEL, it returns false, since the values are clearly not equal. B-FEEL corresponds more closely to business user expectations.

Regular FEEL is not deprecated; it still works. Tools provide a choice as to whether expressions are to be evaluated as FEEL or B-FEEL. n Trisotech Decision Modeler, the choice is made for the model as a whole in two ways.

1

In the Home ribbon selecting Details/Model

2

In the Execution ribbon selecting FEEL/Dialect

Both will open the Details panel as per below:

Differences from FEEL

Differences between FEEL and B-FEEL occur in the following types of expressions:

  • Functions and operators returning Boolean values
  • Built-in functions returning a number
  • Built-in functions returning a string
  • Built-in functions returning a calendar type – date, time, date/time, or duration
  • Built-in functions returning a list
  • Built-in functions returning a range
  • Arithmetic expressions with non-number arguments

Boolean Functions and Operators

All of the following expressions in FEEL return null but in B-FEEL return false:

  • Comparison of a number to text or other non-number type

    “a” = 1 => false

  • Comparison of null to a non-null type

    16 > null => false

  • The not() function with non-Boolean argument

    not(0) => false

  • Logical operators (and, or) with non-Boolean arguments

    X = 4 and X+4 => false

  • The in or between operator comparing a non-number value to a numeric range or a non-date/time value to a date/time range

    “1” in [0..1] => false

  • Invalid argument in any built-in function returning Boolean result

    matches(SSN, "[0-9]{3}-[0-9]{2}-[0-9]{4}") => false

    (Note: second argument of matches() must be a literal string.)

Built-In Functions Returning a Number

All of the following expressions in FEEL return null but in B-FEEL return a zero with null or invalid arguments.

  • Rounding functions (e.g., decimal(), floor(), round up()) with non-number arguments

    decimal(162.623, “2”) => 0

  • string length() with non-text argument

    string length(11) => 0

  • Day number functions (e.g. day of year()) with invalid argument

Functions on collections returning a number ignore null and non-number items in the collection:

  • Statistical functions (e.g., sum(), mean(), stddev(), all except count()) with non-number arguments in the collection argument

    sum([1,2,null]) => 3

    mean([1,2,null]) => 1.5

    sum(1,2,”3″) => 3

    sum([]) => 0

Built-In Functions Returning a String

All of the following expressions in FEEL return null but in B-FEEL return the empty string “”:

  • String modification functions (e.g., substring(), lowercase()) with invalid arguments.

    substring(“myString”, “S”) => “”

  • string() with null argument

    string(null) => “”

  • day of week() with invalid argument

Built-In Functions Returning a Calendar Type

All of the following expressions in FEEL return null but in B-FEEL return the default date/time value based on the ISO8601 string value 1970-01-01T00:00:00+00:00, i.e. January 1, 1970, midnight UTC:

  • date(), time(), date and time() with null or invalid argument. (Note: the argument must be a literal string.)

    date(“2024-06-31”) => date(“1970-01-01”)

The following duration functions in FEEL return null but in B-FEEL return a duration value of zero, such as duration(“PT0S”)

  • duration() with null or invalid argument
  • years and months duration() with null or invalid arguments

Built-In Functions Returning a List

Functions that normally return a list in FEEL and return null with invalid arguments return the empty list [] in B-FEEL:

  • split() with invalid delimiter

    split(“a,b,c”, 2) => []

The mode() statistical function ignores null or non-number arguments.

Range() Function

The range() function converts a range string to a FEEL range. An invalid argument in FEEL returns null but in B-FEEL returns the empty range (0..0).

Semantics of Addition and Subtraction

The + operator means arithmetic addition when the operands are numbers and string concatenation when they are strings. The – operator means subtraction when the operands are numbers and returns the empty string when they are strings. When operands have different types, however, FEEL often returns null, but B-FEEL converts one of the operand types so that addition or concatenation can proceed normally. Which operand type gets converted is subject to precedence rules, so that the following order is followed:

1

String conversion. If one operand is a string and the other is not, the non-string operand is converted to a string using B-FEEL and concatenation applies.

1.
“Today is ” + today() => “Today is 2024-07-15”
2.
“The total is ” + 162.04 => “The total is 162.04”
3.
“The total is ” + null => “The total is “
4.
“abc” – 2 => p
2

Number conversion. Else, If one operand is a number and the other is not, the non-number operand is converted to zero using B-FEEL and arithmetic applies.

1.
1 + null => 1
2.
date(“2024-07-15”) + 7 => 7
3

Date and time, date, or time. Else, if one operand is a date and time, date, or time and the other is not, the other value is converted to a zero duration using B-FEEL and arithmetic applies.

date(“2024-07-15”) + null => date(“2024-07-15”)

4

Duration. Else, if one operand is a duration and the other is not, the other value is converted to a zero duration using B-FEEL and arithmetic applies.

Semantics of Multiplication and Division

If one operand is a number and the other is not, the other is converted to zero.

Benefits for Modelers

B-FEEL is brand new, and I admit I have not had much time to apply it in my own work, but here are what I believe will prove to be the main benefits.

Meets “Common-Sense” Expectation in Boolean Expressions

The primary motivation for it originally was to meet the common-sense expectations of business users that an expression like

a > 0

returns false, not null, when a is null or, say, a text value.

It certainly does that, but an experienced FEEL modeler might not have the same expectation. In particular, if a is a text value, that implies an error, either in the input data or the decision logic, and a null result indicates that in a way that false does not.

Simplification of null Handling

Even experienced modelers will appreciate the simplification of many expressions when values could be null.

For example, suppose a loan application or a tax form includes a list of income items. In DMN that would be a variable incomeItems, a collection of tIncomeItem with a number component value. To find the total income value, in FEEL you would have to write

if incomeItems.value[item!=null] = [] then 0 else sum(incomeItems.value[item!=null])

That’s because the sum() function returns null if its argument is an empty list or includes any null values. B-FEEL simplifies the expression considerably:

sum(incomeItems.value)

In B-FEEL, null items are converted to zero in functions returning a number, so the sum is zero in that case, and it is zero if there are no income items at all. Situations like this occur all the time.

Here is another example. Suppose we have a field totalIncome that we want a Boolean expression testing if is greater than zero. In FEEL that requires something like this to handle the possibility that totalIncome is null:

if totalIncome = null then false else totalIncome>0

In B-FEEL it is simpler:

totalIncome>0

Explanatory statements, including error messages, returned by a decision service typically concatenate fixed text and calculated values. In FEEL you might have something like this:

“The value of your account on ” + string(today()) ” + ” is ” + string(account.value) + “.”

Actually, if there is a possibility that account.value is null, it’s worse:

“The value of your account on ” + string(today()) ” + ” is ” + if account.value=null then “0.” else string(account.value) + “.”

In B-FEEL it is simpler:

“The value of your account on ” + today() + ” is ” + account.value + “.”

Helpful Warnings

You might be concerned that typos and other errors in your expressions might be overlooked if testing does not return null. On the Trisotech platform, they are still reported as warnings, which is quite helpful.

The Bottom Line

In time, I believe most DMN models will use B-FEEL, but we have to acknowledge that it is not backward-compatible with a lot of existing models, many deployed in production. And it will take time for B-FEEL to make its way into DMN books and training. So the migration will likely be gradual.

Follow Bruce Silver on Method & Style.

Blog Articles

Bruce Silver

View all

All Blog Articles

Read our experts’ blog

View all

Learn how it works

Request Demo

Confirm your budget

Request Pricing

Discuss your project

Request Meeting
Graph