Have you heard about the addition of the ternary operator in AL? Some developers are happy, while others say that it is awful. In some ways, I am closer to those who believe that there is more harm than good in it. However, I still believe that the tool itself has a neutral connotation. It all depends on who uses it and how they do it. I want to thoroughly analyze the pros and cons of using the ternary operator with examples. Where it is worth using and where it is better never to use it.
The ternary conditional operator is essentially a type of conditional expression, the most basic of which is the familiar if
The question immediately arises: why is this operator needed? Why not just use if-then-else? The answer is actually very simple: this syntax simplifies some scenarios for writing and reading code. Take Case statements, for example; we all understand when it's better to write a Case statement instead of a series of if-then-else. The same applies to the ternary operator. Of course, developers who have only worked with AL or C/AL may not be used to this syntax and might find it unusual to read/write such code. Fortunately, it is a fairly simple construct and easy to get used to.
So why are there developers who are unhappy with the introduction of the ternary operator in AL? There are several reasons for this, let's consider some of them:
Therefore, in my personal opinion, every good developer should adhere to some good manners rules when working with the ternary operator. For me, these rules are quite simple. Let me share them:
In this section, we will explore some useful features of the ternary operator with examples of how it can be used. This is the light side of the ternary operator.
I have prepared three functions that clearly demonstrate the benefits of the ternary operator. Let's start by looking at their classic if-then-else form.
local procedure GetMinNumberClassic(a: Integer; b: Integer): Integer
begin
if a < b then
exit(a)
else
exit(b);
end;
local procedure GetAgeInfoClassic(Age: Integer): Text
begin
if Age >= 18 then
exit('Adult')
else
exit('Minor');
end;
local procedure AbsIntegerClassic(InputInteger: Integer): Integer
begin
if InputInteger < 0 then
exit(-InputInteger)
else
exit(InputInteger);
end;
When using the ternary operator, these functions can look different. These functions can be called ternary inline return.
local procedure GetMinNumber(a: Integer; b: Integer): Integer
begin
exit(a < b ? a : b);
end;
local procedure GetAgeInfo(Age: Integer): Text
begin
exit(Age >= 18 ? 'Adult' : 'Minor');
end;
local procedure AbsInteger(InputInteger: Integer): Integer
begin
exit(InputInteger < 0 ? -InputInteger : InputInteger);
end;
I think this is a good way to use the ternary operator. But that's not all. Let's look at other applications. For example, instead of writing a function, we can immediately use the ternary operator for variable assignment. Let's call this ternary inline assign.
local procedure TestInlineAssign(a: Integer; b: Integer; Age: Integer; InputInteger: Integer)
var
AgeInfo: Text;
MinNumber: Integer;
begin
MinNumber := a < b ? a : b;
AgeInfo := Age >= 18 ? 'Adult' : 'Minor';
AbsInteger := InputInteger < 0 ? -InputInteger : InputInteger
end;
In addition, we can pass the ternary operator as a function parameter. I would say this is already on the edge of good manners, but I think it still has the right to exist. In any case, let's consider this example, which we will call ternary inline parameter.
local procedure TestInlineParams(a: Integer; b: Integer; Age: Integer; InputInteger: Integer)
begin
ProcessMinNumber(a < b ? a : b);
ProcessAgeInfo(Age >= 18 ? 'Adult' : 'Minor');
ProcessAbsInteger(InputInteger < 0 ? -InputInteger : InputInteger);
end;
local procedure ProcessAbsInteger(AbsInteger: Integer)
begin
//Do something with integer
end;
local procedure ProcessAgeInfo(AgeInfo: Text)
begin
//Do something with AgeInfo
end;
local procedure ProcessMinNumber(MinNumber: Integer)
begin
//Do something with MinNumber
end;
I believe that without knowing the dark side, it is impossible to fully understand the power. Like yin and yang symbolizing the completeness and inevitability of the presence of both dark and light sides, we must explore the dark side to better understand the light side. Therefore, in this chapter, we will examine harmful and bad examples of using the ternary operator and remember that this is not the way to do it.
Let's start with a lesser evil, and it might even seem acceptable to you. But no, this is bad, and a case statement would be much more suitable here. Please do not use nested ternary operators!
procedure GetDayName(Day: Integer): Text
var
DayName: Text;
begin
DayName := (Day = 1) ? 'Monday' :
(Day = 2) ? 'Tuesday' :
(Day = 3) ? 'Wednesday' :
(Day = 4) ? 'Thursday' :
(Day = 5) ? 'Friday' :
(Day = 6) ? 'Saturday' :
(Day = 7) ? 'Sunday' :
'Invalid Day';
exit(DayName);
end;
Better:
procedure GetDayName(Day: Integer): Text
begin
case Day of
1:
exit('Monday');
2:
exit('Tuesday');
3:
exit('Wednesday');
4:
exit('Thursday');
5:
exit('Friday');
6:
exit('Saturday');
7:
exit('Sunday');
else
exit('Invalid Day');
end;
end;
Do you have a large and complex ternary operator that doesn't fit on one line? Then you don't need a ternary operator here!
procedure CalculateBonus(Salary: Decimal; YearsWorked: Integer; PerformanceRating: Integer): Decimal
var
Bonus: Decimal;
begin
Bonus := (YearsWorked > 10 and PerformanceRating > 8) ? Salary * 0.2 :
(YearsWorked > 5 and PerformanceRating > 6) ? Salary * 0.1 :
(YearsWorked > 3 and PerformanceRating > 4) ? Salary * 0.05 :
0;
exit(Bonus);
end;
Better:
procedure CalculateBonus(Salary: Decimal; YearsWorked: Integer; PerformanceRating: Integer): Decimal
var
Bonus: Decimal;
begin
case true of
(YearsWorked > 10) and (PerformanceRating > 8):
Bonus := Salary * 0.2;
(YearsWorked > 5) and (PerformanceRating > 6):
Bonus := Salary * 0.1;
(YearsWorked > 3) and (PerformanceRating > 4):
Bonus := Salary * 0.05;
else
Bonus := 0;
end;
exit(Bonus);
end;
A good example of bad code: do not use the ternary operator to return boolean flags. There is no point in using the ternary operator to return a boolean if we can return the boolean in one line without the ternary operator.
procedure IsEligibleForPromotion(YearsWorked: Integer; PerformanceRating: Integer): Boolean
var
Eligible: Boolean;
begin
Eligible := (YearsWorked > 5) and (PerformanceRating > 7) ? true : false;
exit(Eligible);
end;
Better:
procedure IsEligibleForPromotion(YearsWorked: Integer; PerformanceRating: Integer): Boolean
var
Eligible: Boolean;
begin
Eligible := (YearsWorked > 5) and (PerformanceRating > 7);
exit(Eligible);
end;
Please don't... I'll leave you this example without the "better" version, as it is an interesting problem to think about.
procedure CalculateShippingCost(OrderAmount: Decimal; Destination: Text[30]; IsExpress: Boolean): Decimal
var
ShippingCost: Decimal;
begin
ShippingCost := (Destination = 'US') ? (IsExpress ? (OrderAmount > 100 ? 10 : 15) : (OrderAmount > 100 ? 5 : 10)) :
(Destination = 'EU') ? (IsExpress ? (OrderAmount > 100 ? 20 : 25) : (OrderAmount > 100 ? 15 : 20)) :
(Destination = 'APAC') ? (IsExpress ? (OrderAmount > 100 ? 30 : 35) : (OrderAmount > 100 ? 25 : 30)) :
50;
exit(ShippingCost);
end;
So, the ternary operator can be a very convenient tool that simplifies reading and writing code. But bad developers can abuse it and create more problems, more bad code. At the same time, we need to remember that it is just a tool, and bad developers will generate bad code with or without the ternary operator. So let's strive to be good developers.