tracker issue : CF-4028653

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

[elvis] CFML: "Elvis" operator and null coalescing operators are two different things G'day:

| View in Tracker

Status/Resolution/Reason: Closed/Fixed/

Reporter/Name(from Bugbase): Adam Cameron / Adam Cameron (Adam Cameron)

Created: 07/30/2015

Components: Language

Versions: 11.0

Failure Type:

Found In Build/Fixed In Build: CF11_Final /

Priority/Frequency: Major / Some users will encounter

Locale/System: ALL / Platforms All

Vote Count: 9

Listed in the version 2016.0.0.297996 Issues Fixed doc
Verification notes: verified_fixed on August 23, 2019 using build 2016.0.01.298513
See http://blog.adamcameron.me/2015/07/cfml-elvis-operator-and-null-coalescing.html

Bottom line:

Now the CFML version:

// elvis.cfm

writeOutput("Elvis when using false: ");
testVar  = false;
writeOutput(testVar ?: "default");
writeOutput("<br>");

function nuller(){}

writeOutput("Elvis when using null: ");
writeOutput(nuller() ?: "default");
writeOutput("<br>");


Result:
Elvis when using false: false
Elvis when using null: default


Which is... wrong.

----------------------------- Additional Watson Details -----------------------------

Watson Bug ID:	4028653

External Customer Info:
External Company:  
External Customer Name: Adam
External Customer Email:  
External Test Config: My Hardware and Environment details:

Attachments:

Comments:

I look forward to finding whatever bugs you introduce when you release the fix for this.
Vote by External U.
6399 | July 30, 2015 09:11:01 AM GMT
..................................
Vote by External U.
6400 | July 30, 2015 11:17:04 AM GMT
***************************
Vote by External U.
6401 | July 30, 2015 03:36:44 PM GMT
+1 - please fix it right ...........
Vote by External U.
6402 | August 28, 2015 06:46:23 AM GMT
We can consider adding a boolean check in addition to checking for expr != null.
Comment by Vamseekrishna N.
6388 | September 25, 2015 05:21:44 AM GMT
um... I don't think that's a very good idea, unless you change the definition of a falsey value to include null. Why don't you just fix it? ?: is not supposed to do a null check. It simply shouldn't be doing that.
Comment by External U.
6389 | September 25, 2015 05:57:39 AM GMT
Come on Vamsee... keep the comms going. Don't let this lie around for another month or so before you deign to engage again!
Comment by External U.
6390 | September 29, 2015 07:15:28 AM GMT
............................................
Vote by External U.
6403 | September 30, 2015 04:22:28 PM GMT
Hello?
Comment by External U.
6391 | October 20, 2015 02:58:24 AM GMT
+1111111111111111111111111
Vote by External U.
6404 | October 20, 2015 06:51:51 PM GMT
The Elvis operator has been modified to consider binary equivalent and null coalescing both. Output for the following expressions will be: writeoutput( "Null coalescing when using false: "); writeoutput(false ?: "default"); writeoutput("Null coalescing when using undefined/null "); writeoutput( xyz ?? "default"); // Where xyz is not defined. You can pass a CF null object. This outputs: Null coalescing when using false: default Null coalescing when using undefined/null: default Similarly for true binary equivalent: writeoutput( "Null coalescing when using true: "); writeoutput(true ?: "default"); The output: Null coalescing when using true: true Adam, if you make elvis work as binary equivalent only, then the true condition will always output "true" which might not make sense in most of the cases. example: writeoutput("when customer is not defined, customerName: "); writeoutput( isDefined(form.custName) ?: "Guest costomer"); writeoutput("when customer is defined, customerName: "); writeoutput( isDefined(form.custName) ?: "Guest costomer"); when customer is defined, customerName:Guest costomer; when customer is defined, customerName:true // Not the desired behavior to put if/else case.
Comment by Awdhesh K.
6392 | October 27, 2015 12:52:56 AM GMT
You're missing the point, Awdhesh. ?: is *not supposed to be a null check*. It's supposed to be a truth check. This is the whole thing you've messed up. There's a compelling case for *also* having a null check operator, but that is *a different thing*, usually ?? (C#, PHP) Basically you cocked up your implementation of ?., and now need to fix it. At the same time you should migrate the code you currently have for your ?. implemented to a ?? operator. You guys messed up, and you need to own that properly, and deal with it. And do it quickly before too many people start using the thing!
Comment by External U.
6393 | October 27, 2015 03:46:11 AM GMT
+1 million. Fix this now while its not too late. Its things like this that destroy any credibility that cfml has as a language. If you dont understand how to implement a language feature, look at how other languages have implemented it. Dont just make stuff up, and when you get it wrong own up to the mistake and FIX IT.
Vote by External U.
6405 | October 29, 2015 10:07:04 AM GMT
Hi Awdhesh, Two questions: Regarding: -------------------------------------------- writeoutput( xyz ?? "default"); This outputs: Null coalescing when using undefined/null: default -------------------------------------------- 1) Is support for ?? added as part of this ticket's fix? Regarding: -------------------------------------------- writeoutput("when customer is defined, customerName: "); writeoutput( isDefined(form.custName) ?: "Guest costomer"); when customer is defined, customerName:true // Not the desired behavior to put if/else case. -------------------------------------------- 2) In this case, true is actually the desired output b/c isDefined(form.custName) returns true and the ?: above is a shortcut for isDefined(form.custName) ?isDefined(form.custName): "Guest costomer". What are you suggesting the desired output would be in this case? Thanks!, -Aaron
Comment by External U.
6394 | October 31, 2015 02:28:40 AM GMT
@Aaron: Regarding #1: That was a typo ,so ignore . Regarding #2, Yes it will output "TRUE". Let me know if you have any additional questions. Thanks, Suchika.
Comment by Suchika S.
6395 | November 04, 2015 03:46:22 AM GMT
Hi Suchika, Awesome! Thanks very much! -Aaron
Comment by External U.
6396 | November 04, 2015 12:12:44 PM GMT
We need to fix "no" and 0 cases . Hence re-opening the bug.
Comment by Suchika S.
6397 | December 11, 2015 03:22:55 AM GMT
This should have been fixed months ago.
Vote by External U.
6406 | February 03, 2016 06:31:39 AM GMT
This is still broken: it's still treating null as "falsey" which is WRONG. Repro: <cfscript> function returnsSomething(option){ if (option == "null") { return; } return option; } options = [true, false, "null"]; options.each(function(option){ CLI.writeln(""); CLI.writeln("***** TESTING OPTION: #option# *****"); CLI.writeln(""); CLI.writeln("using if"); try { if (returnsSomething(option)){ var result = "truthy"; }else{ var result = "falsey"; } CLI.writeln("Result: #result#"); }catch (any e){ CLI.writeln("Type: #e.type#"); } hr(); CLI.writeln("using ternary"); try { var result = returnsSomething(option) ? "truthy" : "falsey"; CLI.writeln("Result: #result#"); }catch (any e){ CLI.writeln("Type: #e.type#"); } hr(); CLI.writeln("using elvis"); try { result = returnsSomething(option) ?: "falsey"; CLI.writeln("Result: #result#"); }catch (any e){ CLI.writeln("Type: #e.type#"); } hr(); }); function hr(){ CLI.writeln(repeatString("=", 80)); } </cfscript> Please reopen the ticket and fix it properly.
Comment by External U.
6398 | November 23, 2016 03:57:33 PM GMT
CF11 is OK, but CF 2016 is not working correctly.
Vote by Bryan H.
6407 | June 27, 2017 06:11:06 PM GMT
This ticket was a mistake IMO. Elvis was implemented in CFML as a null coalescing operator and it was wrong of us to try and make it into a shortened ternary after the fact. CFML doesn't have the truthy/falsey support it would need to do that and make it work like everyone was thinking. Now Elvis is just a mixed up mess and frameworks like ColdBox can't even use it because it behaves too inconsistently.
Comment by Bradley W.
29277 | July 11, 2018 05:09:31 AM GMT
In other languages where elvis is a shortened ternary, it can only be used to default values when those languages have a redefined sense of truthy and falsey values. Unless Adobe adopts what is suggested in this ticket: https://tracker.adobe.com/#/view/CF-3616205 it cannot and should not try to mimic languages like Groovy or JavaScript's elvis operator which take a boolean expression.
Comment by Bradley W.
29278 | July 11, 2018 05:39:24 AM GMT
This was never a bug and never should have been changed. While I agree with Adam that "null coalesce" and "binary ternary" are not the same thing, that's where the agreement ends. The problem was not with CF. The problem is that Adam assumed that CF's implementation of ?: was binary ternary. It wasn't. It never was. It never should have been. Groovy: ?: = binary ternary, but NULL and FALSEY both replace the value Javascript binary ternary: || because short circuit replaces the values Kotlin: ?: = NULL coalesce. As CF was. You cannot use other languages to drive this decision, because first of all, if you look for the "Elvis" operator online, few people can agree on whether that operator is ?. or ?: When the language defines an operator it should be for the use of THAT language. I can see great benefits to a null coalescing operator in Coldfusion, because it removes the need for <cfparam> while ALSO dealing with Nulls, which can be introduced by safe-nav ?. I can see little to no benefit for a binary ternary... Plain ternary is good enough, and if you actually want a binary ternary mechanism, implement || (which can also be used for logic reasons. To wit, it is absolutely unacceptable that the documentation says: "In an expression, if the resultant value is not defined, then the object will be assigned to the left most part of the expression otherwise a default value (define at the right most part) will be assigned" (Note it says ABSOLUTELY NOTHING about whether the value is true or valse) However, var a = false; IsDefined("a")?a:"default" Returns false, but a?:"default" Returns "default" Which means your "bugfix" has violated the specification of the feature and never should have been done.
Comment by Joe G.
30911 | June 13, 2019 12:51:41 PM GMT
https://tracker.adobe.com/#/view/CF-4204512
Comment by Joe G.
30916 | June 13, 2019 03:06:56 PM GMT
Hi Adobe, I've verified this is fixed in CF2016 Update 1 (build 2016.0.01.298513). <cfscript> myArray = [true,false,-1,0,1,"yes","no"]; myArray2 = myArray.map(function(element, index, array) { return element ? element : "changed"; }); myArray3 = myArray.map(function(element, index, array) { return element ?: "changed"; }); writeOutput(serializeJSON(myArray2, false, false) & '<br>'); writeOutput(serializeJSON(myArray3, false, false)); </cfscript> CF11 Update 7 result: ----------- [true,"changed",-1,"changed",1,true,"changed"] [true,false,-1,0,1,true,false] ----------- CF2016 Update 1 & Update 11 result: ----------- [true,"changed",-1,"changed",1,true,"changed"] [true,"changed",-1,"changed",1,true,"changed"] ----------- CF2018 Update 0 & Update 4 result: ----------- [true,"changed",-1,"changed",1,"yes","changed"] [true,"changed",-1,"changed",1,"yes","changed"] ----------- Thanks!, -Aaron
Comment by Aaron N.
31158 | August 23, 2019 03:16:40 AM GMT