D365 Business Central : Lazy Evaluation

Hi and Happy New Year 2023 !!
For the start of the year, let us talk about Lazy Evaluation. What is Lazy Evaluation ? If you have never heard of lazy evaluation, it is basically a technique where the compiler do not process an expression until it’s needed. What does that mean ? Let’s take a look at example in AL conditional statements.

if <Condition1> and <Condition2> then
  DoSomething();

Using the above example, DoSomething() will run only if both Condition1 and Condition2 return true. Let’s do a sample where we have two conditions. Condition1 is called FastProcessButFalse() which runs for 1 second and always returns false. Condition2 is called SlowProcessButTrue() which runs for 5 seconds and always returns true.

local procedure TestEvaluation()
begin
    if FastProcessButFalse() and SlowProcessButTrue() then
        Message('OK');
end;

local procedure FastProcessButFalse(): Boolean
begin
    Sleep(1000); // 1 second
    exit(false);
end;

local procedure SlowProcessButTrue(): Boolean
begin
    Sleep(5000); // 5 seconds
    exit(true);
end;

The above TestEvaluation() procedure will never output OK message because FastProcessButFalse() procedure returns false. When we are talking about ideal performance, SlowProcessButTrue() procedure does not need to be processed because FastProcessButFalse() procedure returns false.

Evaluating SlowProcessButTrue() procedure is useless and waste of performance because it is not used. This is the Lazy Evaluation concept. It is being “lazy” and stop evaluating other Conditions when we already have enough information to know the result. This concept is especially important when we have slow running procedure. The opposite approach is called Strict Evaluation.

We would normally expect the AL to adopt the Lazy Evaluation approach. Unfortunately, that is not the case. When we run this in AL, it will process both Conditions and run for 6 seconds. Let’s check this by adding duration into it.

local procedure TestEvaluation()
var
    StartDateTime: DateTime;
    EndDateTime: DateTime;
    TimeDuration: Duration;
begin
    StartDateTime := CurrentDateTime();
    if FastProcessButFalse() and SlowProcessButTrue() then
        Message('OK');

    EndDateTime := CurrentDateTime();
    TimeDuration := EndDateTime - StartDateTime;
    Message('%1', TimeDuration);
end;

local procedure FastProcessButFalse(): Boolean
begin
    Sleep(1000); // 1 second
    exit(false);
end;

local procedure SlowProcessButTrue(): Boolean
begin
    Sleep(5000); // 5 seconds
    exit(true);
end;

When we run the TestEvaluation(), we get 6 seconds duration which is the sum of both Conditions.


Now we know that AL does not do Lazy Evaluation. What should we do with this information and how to improve our code?

The first one is to make use of nested if. It is especially useful when we don’t have “else” statement. We also need to make sure the fastest Condition is evaluated first.

Instead of:

if <Condition1> and <Condition2> and <Condition3> and <Condition4> then
  DoSomething();

Do this:

if <Condition1> then 
  if <Condition2> then 
     if <Condition3> then
        if <Condition4> then
          DoSomething();

Another good way to do it is to wrap all the conditions into one procedure and use “if not” to exit early.

if CheckConditions() then
  DoSomething();

local procedure CheckConditions(): Boolean
begin
  if not <Condition1> then
    exit(false);

  if not <Condition2> then
    exit(false);

  if not <Condition3> then
    exit(false);

  if not <Condition4> then
    exit(false);

  exit(true);
end;

Let’s try it on AL.

local procedure TestIfEvaluation()
var
    StartDateTime: DateTime;
    EndDateTime: DateTime;
    TimeDuration: Duration;
begin
    StartDateTime := CurrentDateTime();

    if CheckIfConditions() then
        Message('OK');

    EndDateTime := CurrentDateTime();
    TimeDuration := EndDateTime - StartDateTime;
    Message('%1', TimeDuration);
end;

local procedure CheckIfConditions(): Boolean
begin
    if not FastProcessButFalse() then
        exit(false);

    if not SlowProcessButTrue() then
        exit(false);

    exit(true);
end;

local procedure FastProcessButFalse(): Boolean
begin
    Sleep(1000); // 1 second
    exit(false);
end;

local procedure SlowProcessButTrue(): Boolean
begin
    Sleep(5000); // 5 seconds
    exit(true);
end;

When we run the TestIfEvaluation(), we get 1 seconds duration which is what we want.


What about using Case statement ?

local procedure TestCaseEvaluation()
var
    StartDateTime: DateTime;
    EndDateTime: DateTime;
    TimeDuration: Duration;
begin
    StartDateTime := CurrentDateTime();

    if CheckCaseConditions() then
        Message('OK');

    EndDateTime := CurrentDateTime();
    TimeDuration := EndDateTime - StartDateTime;
    Message('%1', TimeDuration);
end;

local procedure CheckCaseConditions(): Boolean
begin
    case false of
        FastProcessButFalse(),
        SlowProcessButTrue():
            exit(false);
    end;
    exit(true);
end;

local procedure FastProcessButFalse(): Boolean
begin
    Sleep(1000); // 1 second
    exit(false);
end;

local procedure SlowProcessButTrue(): Boolean
begin
    Sleep(5000); // 5 seconds
    exit(true);
end;

When we run the TestCaseEvaluation(), we get 1 seconds duration which is only for Condition1 similar to “if not” statement.


That’s it. Make sure to pay attention when you have multiple conditions on your if statement. You can then use “if not” or “case” statement to improve the performance.

You can also find the sample on GitHub.

Here other blogs that are talking about Lazy Evaluation as well for your reference.
https://www.hougaard.com/al-is-not-a-lazy-language/
https://nataliekarolak.wordpress.com/2022/12/21/lazy-evaluation-in-al/

thatnavguy

Experienced NZ-based NAV Developer and Consultant with 15+ years of experience leading multiple IT projects, performing business analyst, developing, implementing, and upgrading Dynamics NAV and Business Central. Passionate to deliver solution that focuses on user-friendly interface while keeping high standard of compliance with the needs.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *