tracker issue : CF-4203302

select a category, or use search below
(searches all categories and all time range)

Ternary Operators Act Counter-intuitively

| View in Tracker

Status/Resolution/Reason: Closed/Withdrawn/DesignLimitation

Reporter/Name(from Bugbase): SDC Web Team / ()

Created: 08/29/2018

Components: Language

Versions: 2016,11.0,2018

Failure Type: Incorrectly functioning

Found In Build/Fixed In Build: 308055 /

Priority/Frequency: Normal / All users will encounter

Locale/System: English / Win 2012 Server x64

Vote Count: 2

Problem Description:
When using a ternary operator to condition whether a struct's optional key is referenced, the "key is undefined in STRUCT" format of error appears even when the key isn't referenced. e.g.

myStruct = {
    foo = 'bar'
values = myStruct.keyExists( 'value' ) ? [myStruct.value] : [];

Since the Elvis operator exists, allowing a shorthand form of the code above, and a ternary operator is effectively just a 'flat' if-else statement, the code block for 'true' in this case should only be run if the condition is... well, true! Instead, the above code throws the error:
"Element value is undefined in MYSTRUCT"

The reason behind this is because CF initialises any possible variables before executing the code. So it tries to initialist myStruct.value regardless of whether it's ever going to be needed or not. This is illogical; If you use a ternary operator, the logistics of it are:

                Is this statement true? If yes – do this : If no – do this;

It’s counter-intuitive to have a situation where the statement is not true, but the ‘true’ part of the expression is evaluated regardless. It would be the equivalent of saying that this block...:

If( booleanCondition ){
  myValue = obj.value;
 myValue = “no value”;

...throws the “key VALUE doesn’t exist” error when booleanCondition resolves to false. But, the whole point of having an if-else condition (and a ternary operator is just a “flat” if-else condition by nature) is that you can operate using different values and variables completely depending on the boolean condition.

Programming should be done in a way which flows logically, and standards dictate that certain common operators and procedures behave the same across languages with mere syntactical differences between them – the ternary operator (as an extension of the ever-popular if-else block) is one of these.

e.g. if a shoe store started selling “size 9” shoes that were what other stores call “size 12” or even “size 6” that would be an inconsistency and people wouldn’t appreciate the seemingly-random deviation from the standard. Likewise, operating on the wrong conditional branch of code (as that’s what this is doing) wildly deviates from the standard and is not only misleading but, again, illogical.

It's understandable that this isn't an overnight fix, but it's something which really should be looked at due to the ternary operator's proximity in purpose to if-else blocks and the Elvis operator, both of which work in a different way to this.

Steps to Reproduce:
Run the following code:
myStruct = {
    foo = 'bar'
values = myStruct.keyExists( 'value' ) ? [myStruct.value] : [];

Actual Result:
Error: "Element value is undefined in MYSTRUCT"

Expected Result:
The variable 'values' gets assigned as an empty array (since the keyExists() condition evaluates to false)

Any Workarounds:
Using the long-form if-else block:
values = [];
if( myStruct.keyExists( 'value' ) ){
    values.append( myStruct.value );



Please fix!
Vote by James C.
29632 | August 29, 2018 09:27:37 AM GMT
The issue here is if you have a undefined value inside array initializer, CF puts an initializer statement for that which runs even before the ternary operator executes. This is how CF flow has been. There is nothing specific to ternary operator.  Run the below code snippet for elvis, you will see the same undefined error-   <Cfscript> myStruct = { foo = 'bar' }; values = myStruct.value ?: [myval]; writedump (values); </cfscript>     snippet2: without elvis and ternary operator usage, here also you will get the same error.   <Cfscript> myStruct = \{ foo = 'bar' } ; values = ""; if(myStruct.keyExists('values')) {   values = myStruct.value; } else { values = [novar]; } writedump (values); </cfscript>    
Comment by Vijay M.
30934 | September 12, 2018 07:13:51 AM GMT
Thank you for the explanation.
Comment by Simranjit S.
30954 | June 26, 2019 06:26:46 AM GMT