Delay Evaluation: What does the de() function do anyway?

Delay Evaluation: What does the de() function do anyway?

Posted by Brad Wood
Aug 28, 2008 09:10:00 UTC
If you have ever used the iif() (inline if) function in ColdFusion you have probably found yourself using the de() (delay evaluate) function as well. I remember using that function for quite some time without ever really understanding when I needed it and exactly what it did. I found myself explaining it to someone last week and thought it would be an interesting topic to blog.

What does it do?

I'll start by explaining what the function does because it is insidiously simple. I used to think there was some sort of deep black magic baked into the function. What kind of code could be so powerful to reverse the annals of time, disrupt the time-space continuum, and literally delay the inevitable evaluation of my code? I was quite disappointed when I finally read the docs on and found out:
[code] Escapes any double-quotation marks in the parameter and wraps the result in double-quotation marks.[/code]
What- simple string manipulation?? That's all it does? Yep. You could accomplish the same thing with a replace statement and a little concatenation. Take the following code. I'm using cfsavecontent so the double quotes required to create a string in a cfset won't get in the way.
[code]<cfsavecontent variable="my_string">Hello World</cfsavecontent>
<cfoutput>#de(my_string)#</cfoutput>[/code]
This simply outputs:
[code]"Hello World"[/code]
All it did was add double quotes around my string!
What about this?
[code]<cfsavecontent variable="my_string">We're going to use "laser" beams.</cfsavecontent>
<cfoutput>#de(my_string)#</cfoutput>[/code]
Output:
[code]"We're going to use ""laser"" beams." [/code]
Notice the double quote in the string got doubled up. Yep, you could basically use the following code to make your own de() function:
[code]<cfscript>
	function my_de(my_string)
		{
			return '"' & replace(my_string,'"','""','all') & '"';
		}
</cfscript>[/code]
Ok, sorry for the long review of such a simple concept, but I used CF for years and never realized all that. I know-- it's shaming...

Why do I need it?

The iff() function treats the second and third parameters as expressions, which basically means they are going to get evaluated twice before everything is said and done. I racked my brain for a realistic example of why you would actually want to do this, but it's late, so this is all I could come up with:
[code]<cfset favorite_food = "Tacos">
<cfset favorite_color = "Blue">

<cfset option_1 = "food">
<cfset option_2 = "color">

<cfoutput>#iif(1 eq 1,"favorite_" & option_1,"favorite_" & option_2)#</cfoutput>[/code]
The second argument to the function was evaluated twice. The first time around, it equaled favorite_food, which was the equivalent of:
[code]<cfoutput>#favorite_food#</cfoutput>[/code]
The second time around we are simply left with Tacos. Most of the time we aren't using a variable or expression which evaluates to another variable which evaluates to our actual string. We probably just want the contents of a variable:
[code]<cfset favorite_food = "Tacos">
<cfset favorite_color = "Blue">

<cfoutput>#iif(1 eq 1,de(favorite_food),de(favorite_color))#</cfoutput>[/code]
Or better yet, just a string itself:
[code]<cfoutput>#iif(1 eq 2,de("Tacos"),de("Blue"))#</cfoutput>[/code]
Those two examples are the same as:
[code]<cfoutput>#iif(1 eq 1,"favorite_food","favorite_color")#</cfoutput>[/code]
And
[code]<cfoutput>#iif(1 eq 1,"""Tacos""","""Blue""")#</cfoutput>[/code]
Because either way, you ultimately get:
[code]<cfoutput>#favorite_food#</cfoutput>[/code]
And
[code]<cfoutput>#"Tacos"#</cfoutput>[/code]
(Yes, you can put a quoted string in pound signs. They sort of cancel each other out) Man, this is getting confusing. I'd better press on :)

Do I absolutely HAVE to use it?

Nope. Look at my example above. I started with two code samples using de() and then provided two equivalent versions which didn't use it. If you want to manually wrap your string in double quotes and escape (double-up) any quotes in the string feel free to do so. The advantage that de() gives you is cleaner code. Counting quotes can be a downer. Check out this already hairy iff:
[code]<cfoutput>The quote for today is: #iif(dayofweek(now()) eq 6,de("""Thank goodness it's Friday."""),de("""I hate today."""))#</cfoutput>[/code]
Whose output on a Friday is:
[code]The quote for today is: "Thank goodness it's Friday." [/code]
It can be written to do the exact same thing without the de(), but look at all those double quotes!
[code]<cfoutput>The quote for today is: #iif(dayofweek(now()) eq 6,"""""""Thank goodness it's Friday.""""""","""""""I hate today.""""""")#</cfoutput>[/code]
That's 7 quotes.
A single double quote to enclose the string.
Two double quotes to delay its evaluation (escaped once).
Four double quotes for the actual double quote that is part of the string (escaped twice) Yeah, I'm going to bed. I know it gets hairy, but remember, de() is really pretty simple. You don't NEED it, but it can sure clean up at least one round of quotes for you. :)

 


Michael Dinowitz

Nice. You forced me to pull out an article I wrote on the subject 9 years ago. Man, I'm old. :)

http://www.fusionauthority.com/iif.htm

Ben Nadel

Good post. I remember having that realization... You mean that's ALL it does?!?!?

I think the biggest problem with this function is that it simply doesn't do what people expect it to do. I think, when people see, IIF(), what they are hoping for is a ternary operator (spelling??). For example, many people coming from Javascript love to use this operator:

( boolean-expression ? true : false )

For example:

// If you need a cool person, return Ben, else return Sally. var objPerson = ( needs_cool_person ? objBen : objSally )

Here, the expressions objBen and objSally are not evaluated, they are simply returned. I think this is really what people in ColdFusion want also. The problem with IIF() is that it doesn't just return the True or False arguments... its EVALUATES them first.

To me, this seems like the edge case. More often, people just want to TRUE / FALSE.

Brad Wood

@Ben: I think you are right. Most people are expecting the C/JavaScript style that doesn't do the extra evaluate. Yeah, you spelled it right. :) http://en.wikipedia.org/wiki/Ternary_operation I have seen your blog post as wekk on how iif evaluates any pound signs in an iff before anything else whcih can throw you for a loop. http://www.bennadel.com/blog/1270-Be-Careful-Using-In-ColdFusion-DE-Expressions.htm

@Michael: Thanks for the good article on iif.

ike

When I find myself wanting an iif, it's often not actually for string values, but rather for complex values... I do things like

arguments.blah = iif(isArray(arguments.blah),"arguments.blah","ArrayNew(1)");

Merely because it's more concise than

<cfif not isArray(arguments.blah)> <cfset arguments.blah = ArrayNew(1) /> </cfif>

ArrayNew may not be the best example of that either... depending on circumstance for example, the arguments might be method calls on the current cfc or a composed cfc

<cfset var condition = x /> <cfset var blah = iif(condition,"callMyFunction()","obj.callOtherFunction()") /> <cfset var somethingelse = blah + anotherthing />

I don't do it very often, but in this case for example, notice the var keyword. Because all the var keywords have to be grouped at the top, you're not allowed to use a standard if statement if you need the variable "blah" to set another var on the next line like the "somethingelse" variable in this example. Of course, you could always create a separate function that tests the condition and returns one or the other, but then that's more work and not always worth the extra lines.

In this example it's returning a number, which is still a simple value, but it could just as easily be returning a struct, query, array or object. I think I end up doing that more than returning strings actually.

I'm sure you already knew all that -- just thought some of the other folks reading might not have noticed, since the previous examples were all with strings. :)

Brad Wood

@Ike: Thanks for the additional examples!

ike

@Ben welcome.

I guess I didn't read through Ben's comment before either... so umm... yeah, iif() really is a ternary function, it's just that it throws you off if you're familiar with ternaries from other languages like JavaScript because you have to quote the return values in iif() whereas they're not quoted with JavaScript's ternary...

Which is I think what Ben was saying, and I'm not sure if my explanation is any clearer. :P

But it's difficult to write clear explanations of things once you get into the kind of recursive nature of something like iif() and de(). :P

That's only true however if the returned values are either strings or complex values -- if they're either numeric or boolean, they will short-circuit automatically, so you can also do things like:

iif(condition,5,8)

or

iif(condition,true,false)

And neither the numbers or booleans need to be quoted or have de() around them because evaluate(true) returns true and evaluate(8) returns 8.

I'm always a little torn about using iif in my code. I've never considered the performance argument to be a valid argument for not using them, but there are still times when I look at something I'm writing and worry about other people being confused by my use of an iif(). I've had a couple of jobs where my boss complained about the use of iif for that reason, and not because I was doing anything complicated, just because they felt iif was generally confusing. And then you've got folks on the other side like Barney Boisvert complaining about the fact that there's not a separate ternary operator in ColdFusion. Which leaves me with that "it's never good enough" feeling... but I guess you can't please everyone. :)

Dan

I read your description of de() and iff() and am very/more confused. I was ok up to the 'Why do I need it?' part. What is output from 7th yellow box?

You need to change you iff to be easier to understand. Something like: <cfset falseflag= false> <cfset trueflag= true> <cfoutput>true test is #iif(falseflag,"favorite_" & option_1,"favorite_" & option_2)#</cfoutput> <cfoutput>false test is #iif(trueflag,"favorite_" & option_1,"favorite_" & option_2)#</cfoutput>

And also show the output.

Dan

How would I code the following bad code, correctly? Do I need DE() ? <cfset joinedList=""> <cfset listone="1,2"> <cfset listtwo="A,B"> <cfset listthree="3,4"> <cfset joinedList="#joinedList##IIF((len(joinedList) gt 0),',',''#)##listone#"> <cfset joinedList="#joinedList##IIF((len(joinedList) gt 0),',',''#)##listtwo#">

<cfset myflag=true> <cfset joinedList="#joinedList##IIF(myflag,',',''#)##listthree#">

Dan

How would I change the following code to use IFF() and DE? <cfset MyValue = "1,2"> <cfif listLen(myList) is 0> <cfset myList = MyValue> <cfelse> <cfset myList = myList & ", " & MyValue> </cfif>

Brad Wood

@Dan: I'm sorry my examples confused you. I think 1 eq 1 is pretty obvious as an expression that equals true. As for the output, I did show the output after the 8th yellow box with this sentence: "The second time around we are simply left with Tacos." Don't be afraid to paste the code into a .cfm file yourself and try it. :)

As for your examples, I hate to say it, but you don't need iif at all. ColdFusion comes with a function called listAppend which takes care of all that messy comma stuff for you.

<cfset listone = "1,2"> <cfset listtwo = "A,B"> <cfset joinedList = listAppend(listone,listtwo)>

If you really, really, really want to use the iif and de functions, you could do the following. It concatenates the strings and only includes a comma in the middle if they both have length.

<cfset listone = "1,2"> <cfset listtwo = "A,B"> <cfset joinedList = listone & iif(Len(listone) && len(listtwo),de(","),de("")) & listtwo>

And the de isn't a requirement, it just makes it easier because your alternative is this:

<cfset joinedList = listone & iif(Len(listone) && len(listtwo),""",""","""""") & listtwo>

If it was up to me though-- I'd stick with the listAppend() here. :)

Site Updates

Entry Comments

Entries Search