Code Assertively
Someone recently asked me about the difference between using Evaluate() vs. using array notation to get a dynamically-named query column value. I argued for using array notation. On one hand, I believe that it is minutely faster; but, the bigger issue is that using array notation is a more assertive coding style. After responding to that, I kind of wanted to get this off my chest.
NOTE: The following is just my opinion, it's not personal.
Coding style is important. And I'm not talking about readability and maintainability (although those are huge factors). Your coding style says a lot about you and your understanding of the features of a language. To me, use of non-assertive code such as ColdFusion's Evaluate() and IsDefined() methods, subliminally tell me that you don't know how to properly param, reference, or scope variables. These are weak, code-by-coincidence statements and I think they reflect poorly upon the coder.
Just as with the English language, using a more assertive style makes you seem like a more confident and knowledgeable programmer. For a second, take a moment to think about a non-programming environment. Think about working out in the gym with a training partner. In my experience, there are two types of training partners: assertive and non-assertive. Let's say you're getting ready to do a set of bench press. Here's what a non-assertive partner might say:
"All right, let's see how many you got!"
Now, let's compare that to what an assertive training partner might say:
"Let's go big man! You ain't getting off this bench till you give me 8 fucking reps!"
Which one of these training partners do you think is going to motivate you more? Which one is gonna help you push yourself hard to make gains and continual progress? This isn't really a subjective question; the non-assertive partner doesn't provide a reachable task. "See how many you got" does not provide a structured environment with tangible goals. On the other hand, "Give me 8 fucking reps!" is a highly tangible, black-and-white task that is within reach (if for no other reason than the fact that it can be distinctly measured).
Now, jumping back to programming, the same principles apply. Let's say you code something non-assertively like:
IsDefined( "form.first_name" )
To personify it, that's akin to being like, "Excuse me, has anyone seen my variable, form.first_name? Hello? Is anyone listening to me? First_name? Anyone seen it? No? Yes? Is it over there? First_name - starts with 'form'?" This is a really weak style and it comes across as being unsure about how the code is supposed to function.
On the other hand, if you code something assertively like:
StructKeyExists( FORM, "first_name" )
To personify it, that's like saying, "First_name, you better be in the FORM scope, otherwise some shit's gonna go down!" This is a much stronger statement and demonstrates that:
- You know where your variable is supposed to be.
- You know how structures work.
- You understand scoping mechanisms.
- You understand the difference between a "variable name" and a scoped variable.
Think about this code for a second:
<!--- Set variable. --->
<cfset VARIABLES[ "form.how_sexy" ] = "Too Sexy" />
<!--- Check variable existence. --->
#IsDefined( "form.how_sexy" )#
Do you have any doubt as to what this will return? Any doubt at all? A shadow of a doubt? I know that I did. I've been coding ColdFusion for 8 years and I just ran a test to see what the above code would do because I wasn't sure. And why was I not sure? Ignorance? Matter of fact, yes. But also, this code is potentially ambiguous. The VARIABLES scope should be searched before the FORM scope, so it could be argued that ColdFusion should have looked in the VARIABLES scope for the named variable before it looked in the FORM scope.
But really, the point here is not so much that I should have known how the code worked and more so that the code was not assertive. Had the code been the stronger statement:
#StructKeyExists( FORM, "how_sexy" )#
I would have had no doubt as to what it was trying to accomplish.
Ok, time to stop ranting and start billing clients. But just remember, assertive coding is better. Now, that's not to say that there are no use cases for Evaluate(), there are; I am just saying that in the cases where it is not required, it would reflect more favorably upon you NOT to use it. As far as IsDefined() - I am not sure I have ever seen a situation where this could not be replaced with more assertive code.
</rant>
Want to use code from this post? Check out the license.
Reader Comments
I think you're putting a lot of pressure on structKeyExists() to perform.
I also think this may not be the best example. isDefined() is appropriate in many cases, although in your example structKeyExists() is more...precise. For my tastes, the latter is better in this specific instance because of that precision rather than because of its "assertiveness".
When multiple options are available, I prefer the least ambiguous. In this case, structKeyExists() offers more insight into the variable.
As you said, just my opinion, though. :-)
@Rob,
Yeah, I just want to stress the point that this is MY opinion and a standard that I try to hold myself to. I don't want to come off that I am looking down on people - intonations are lost in text - I want to get people excited about stronger coding style :)
That disclaimer, I would be interested in where you might find IsDefined() an appropriate choice? I think maybe it has to do with personal preferences. I usually param all my passed variables, so it is very rare that I have a variable that doesn't have a default value, so I am curious as to what other people are doing (I have already found that I use the CFParam tag much differently than most people - www.bennadel.com/index.cfm?dax=blog:594.view ).
No offense taken. We're just having a pleasant discussion here. You didn't present your opinion in any inflammatory manner (actually, you went out of your way to avoid doing so) so I didn't take it as such. :-)
As for when isDefined() is appropriate. I tend to use cfparam for most things myself (maybe it's some sort of legacy need to explicitly declare variables?), but there are times when I'll set a variable during processing if and only if some condition is triggered. An error condition is a good example:
I may set an "errorMsg" variable if I reach and error condition and then use isDefined() to decide whether to display the message.
Summarized, I guess my thought would be that isDefined() is perfectly acceptable for dealing with scalars. For complex types, I prefer a more precise syntax. Whether to parameterize scalars an eliminate the need for isDefined() is a different discussion.
I think like many, many things in coding, that this mentality is good as a guide or a goal, but not at a rule.
I have almost always been able to figure out a way to recode to avoid using evaluate. That is until the other night. I ran into a challenge that required it. Not much I could do but use it.
So you shoot for your applications to be 100% adherent to these standards so that when you have to break one of them you're going down to 99% adherent, and not setting for 80% could you didn't try.
Exactly. It's not a law, its just a goal. There are times when you simply can't do anything else but use Evaluate() - or times when NOT using Evaluate() adds so much overhead that it's simply not worth coding around.
I thing evaluate() and isdefined() make good newbie-tags, but those shouldn't be taught anymore. Back in the day they were standard CF documentation examples that everybody copied of.
I try not to use them, but at times you're tied to old ways, and I don't have anybody telling me to "change that evaluate() and give me 8 fucking reps!"... it's a discipline and I'm glad you're here to set the record straight.
Another funny thing: my Outlook is telling me we are crossposting each-others blogs... guess we haven't got anything better to do :)
I'm glad that we are both concerned with a "better" way to do things :) I like your list.
<cfset var result = callSomeFuncThatCanReturnNull() />
<cfif isDefined("result")> <cfreturn result /> </cfif>
Other than that instance, I use structKeyExists().
Having said that, a couple of people picked up on the fact that Fusebox generates code containing isDefined() because - in the general case - it cannot tell whether using structKeyExists() would behave the same way (due to the way some expressions are structured).
There are trade-offs to just about any approach.
Let's say "firstName" can be passed in by a form, url or attribute. In this case
<cfif isDefined("firstName")>
is more efficient than
<cfif structKeyExists(form,"firstName") OR structKeyExists(url,"firstName") OR structKeyExists(attributes,"firstName")>
Another interpretation of your training buddy example might be:
Non-Assertive:
"All right, let's see how many you got!"
"Cool! I did 12 reps! I didn't know I had 12 in me!!!"
Assertive:
"Let's go big man! You ain't getting off this bench till you give me 8 fucking reps!"
"There's your 8 reps ! Now fuck off!!!"
The point is, coding style is important... but there are many different and highly effective coding styles. Be careful judging someone else's skill level and understanding simply because their style is different than what you would use.
@Gus
"<cfif isDefined("firstName")>
is more efficient than
<cfif structKeyExists(form,"firstName") OR structKeyExists(url,"firstName") OR structKeyExists(attributes,"firstName")>"
More efficient for you, the compiler, or in execution? I would think it's only more efficient for you since your making CF do a scope search. But don't take my word for it, check the docs:
http://livedocs.adobe.com/coldfusion/6/Developing_ColdFusion_MX_Applications_with_CFML/Variables7.htm#1134503
@Dustin
I was referring to both the coder, and those coming in behind the coder.
I agree, structKeyExists() is more efficient on the server side, however in many, if not most cases this would likely fall under the rubric of "premature optimization".
Gus
@Sean,
Yeah, working with the local scope of a function is tricky. I don't think there is an "obvious" way to get at it; someone posted about get the active local scope from the PageContext object, but I have never used that one.
To get get around that one, I have been creating a LOCAL pseudo-scope:
<cfset var LOCAL = StructNew() />
And then putting my stuff in that. It make for a bit more typing, and you have to be careful about using [] in query of queries. But, I am used to it now. Sometimes I flip-flop on the extra typing, but for now I am going to stick with it.
@Gus,
I agree - certainly there are lots of ways to look at a situation. Take, for example the FirstName coming from URL or FORM scopes. I would say that you could have:
<cfparam name="FirstName" type="string" default="" />
And then it will always be available. At that point, it becomes a business question: Does having an empty FirstName value differ from having an undefined FirstName value?? The answer to that is beyond the scope of this conversation, but I am just agreeing with you that there are many ways to look at a situation.
Alternately, with things like FuseBox (I think), FORM and URL scopes are all copied into one pseudo scope, Attributes. In that case, you could always reference the Attributes-scoped value when necessary.
Sometimes, I even param both:
<cfparam name="URL.FirstName" type="string" default="" />
<cfparam name="FORM.FirstName" type="string" default="#URL.FirstName#" />
This way, you can still refer to all form-values as FORM scoped (assuming that the majority of values will be only available via the FORM, not both).
Again, all to say, yes there are lots of different styles on how to solve a problem. And, not every solution style is applicable to every situation. I have used IsDefined(), I have used Evaluate()... What I really am trying to say is that your style says something about you whether observed at a conscious level, and that I think coding in a more "Assertive" manner will portray you in a more positive light.
So the lesson here is to drop some f-bombs in the code? ;)
Actually I still fail to see how isDefined("scope.varname") is any less precise. Nor do I see how it asserts less of an understanding between a variable that exists in a scope (a goofy thing on CF's part) and that you just don't think the variable's name is "scope.varname". If we're talking about isDefined("varname") with no scope, sure. But worry about checking the scope's structure feel's a bit like German engineering. Is it fancy? Sure. But at what point is it just adding a tad of complexity to something that doesn't require it?
For me, at least, the precision argument is most applicable for "non-static" structures. Form, URL, etc. are just there and they're reserved. They exist, are well known and are few in number. I don't think precision is lost because they are reserved words with very specific meanings.
For user defined variables, though, I think structKeyExists() just offers more insight into the variable composition than isDefined(). That's the precision that I'm looking for in my own code.
As has been said, just personal preference. It's a bit of an inane discussion since both ways work and, as far as I know, neither offers and *real* benefit over the other. We're really just talking about and offering justification for our own preferences.
Nice post :)
Good point, Rob. We shouldn't loose track that the original post isn't saying it's better. I may have lost that a bit with my comments. I don't want to ever discourage these sort of discussions because, and maybe it's the cave I live in, don't have them often enough. There's not really a right / wrong way but in jumping into addressing it we can still all learn more.
For something that wasn't meant to be personal, this seems like an overly harsh attack on anyone that might prefer using these other functions:
"To me, use of non-assertive code such as ColdFusion's Evaluate() and IsDefined() methods, subliminally tell me that you don't know how to properly param, reference, or scope variables. These are weak, code-by-coincidence statements and I think they reflect poorly upon the coder."
I'm not sure what using something like isDefined() has to do with knowing how to scope variables, and I certainly don't think we should automatically look down on anyone that use it, as this seems to imply. I think both ways have their place and there are times when I may pick one over the other according to which seems most intuitive to use.
@MJ,
Certainly, as part of a "rant" I will use language that is too strong for the requirements of the situation. I did not mean this as an attack on people. I just want to get people to stop and think about the way they code.
But, that aside, certainly, I cannot help it when I get a gut feeling when I see certain styles of code. Even beyond what I might consider right and wrong, I cannot stop the instinct.
Ben --> I commend you for getting us to think about how we code. Too often we're in jobs that don't have code reviews, best practices and such. We get into habits and we don't even remember why we're doing them in the first place.
A service I would pay for is someone who could constructively critique my code and help me make it more efficient, more precise and get into useful habits.