Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Shawn Slaughter

Default CFParam Expressions Are Always Executed In ColdFusion

Posted by Ben Nadel
Tags: ColdFusion

Yesterday, my world was somewhat rocked when I discovered that default CFArgument expressions are executed only as-needed in the ColdFusion runtime. This was awesome news; and, it made me wonder if I've been wrong about other contexts that can employ default values. The one that immediately jumps to mind is the CFParam tag which can assign a default value to a variable reference if it is not already defined.

To test this, I set up a similar situation - I am employing a getDefaultValue() function in my CFParam tags; but, I am only requiring it (theoretically) for 2 out of 5 parameters:

  • <cfscript>
  •  
  •  
  • // I return a default value for use in an optional parameter.
  • numeric function getDefaultValue() {
  •  
  • return( ++defaultValue );
  •  
  • }
  •  
  •  
  • // ------------------------------------------------------ //
  • // ------------------------------------------------------ //
  •  
  •  
  • // Since the defaultValue is pre-incremented above, we will be
  • // able to see how many times the getDefaultValue() method is
  • // actually invoked at runtime.
  • defaultValue = 0;
  •  
  • // Setup THREE out of FIVE optional parameters.
  • url.a = "exists-already";
  • url.b = "exists-already";
  • url.c = "exists-already";
  •  
  • // Now, param five values. Since we have the three above, only
  • // two of the five below should receive the default value.
  • param name = "url.a" type = "any" default = getDefaultValue();
  • param name = "url.b" type = "any" default = getDefaultValue();
  • param name = "url.c" type = "any" default = getDefaultValue();
  • param name = "url.d" type = "any" default = getDefaultValue();
  • param name = "url.e" type = "any" default = getDefaultValue();
  •  
  • // At this point, "defaultValue" will report the number of
  • // times the getDefaultValue() expression was executed.
  • writeOutput( "Script Result: " & defaultValue );
  •  
  • // Output the URL values at this point, so we can see which
  • // values were defined.
  • writeOutput( "<br />" );
  • writeOutput( "Url: #a#, #b#, #c#, #d#, #e#" );
  •  
  •  
  • </cfscript>

When I run this code, I get the following page output:

Script Result: 5
Url: exists-already, exists-already, exists-already, 4, 5

Here, we see a different behavior than we saw yesterday with the CFArgument tag. In the CFParam context, the default expression is executed for every CFParam statement; however, the generated value is only applied to the last 2 CFParam tags that do not have pre-defined variable references.

This is more in alignment with my long-standing notions about default-expression evaluation. Though, I have to admit, I much prefer the way it's handled in the CFArgument tag. As a final note, I should mention that this same behavior is exhibited in a Tag-context, but I didn't bother showing it.




Reader Comments

Dammit! I've got a blog article half-written on this very subject. How weird is that (in the sense of "coincidence"). Will have to come up with something else now. Damn you, Nadel. ;-)

--
Adam

Reply to this Comment

@Adam,

Ha ha, sorry!

If you want something to look into, I just tried running this in both Adobe / Railo using CFLive.net. They have two very different outcomes. I don't know *anything* about the Railo engine, so I am not sure what that means. I know you poke around with Railo, so maybe that will make more sense to you.

Reply to this Comment

Yeah, this is a fun one. It makes sense and is sooo frustrating at the same time.

It would be really cool if a "deferred" attribute could be added to cfparam so that it basically did an EVAL of the default value expression if the variable needed to be set, and did NOTHING if the variable existed already.

Reply to this Comment

The difference on Railo has to do with scopes. I think Railo is implying a local scope on the variable in the function and default initializing it to 0. When you replace defaultValue with variables.defaultValue both ACF and Railo return the same results.

Reply to this Comment

@Tom,

Ah, interesting. I am not sure how I feel about that. It seems to go against what other languages [that I use] do, ex. JavaScript.

Reply to this Comment

@Ben,

I fully agree that the Railo functionality is wrong. CFML is supposed to look for variables in existing scopes before creating new ones. It can make the code hard to read, but that's the way it's work with ACF since as far as I can remember.

Reply to this Comment

@Tom,

Yeah, true, it searches like 8 different scopes or something before creating them, from what I can remember.

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.