Why you need to VAR-scope your variables
Yes, the use of the var scope is idiotic. I haven’t met a developer yet (in person) who thinks that Macromedia did it “right” with that keyword. But Steve’s opinion that you don’t need it because a CFC only needs two scopes isn’t fully fleshed-out, I think.
By default, the attributes that a VAR’ed variable acquire should come naturally to all variables defined in a method body either A) without a scope or B) in the variables scope. That is, the variables scope should be local to the method body.
So we all agree we need a method-local variables scope and we don’t want the hassle of defining and var-”scoping” them like we have to do now. But we also need a CFC scope, visible across the entire CFC. Right now, the variables (unscoped) scope does that. You set a variable in a method with no scoping and no “var” keyword and it’s available in any other method. Very handy for instance variables.
So, we need a method-local scope without a goofy syntactic-exception-case “var” keyword, and we need a cross-method scope like the current “variables” scope in a CFC. The “this” scope is already botched. Adobe can’t change the behavior of it without hosing thousands of developers who rely on it. (Yes, you OO purists, it’s okay to use that scope. We’re not writing Java apps here.)
Adobe can’t modify the behavior of the variables scope since nearly everyone I know uses it in their CFCs for CFC-wide variables, mostly of the “instance” variety.
So what are the language experts to do? Formalize the init() method of a CFC to expose any variables defined there as CFC-wide variables while restricting undefined variables in other method bodies with the var-scope behavior? That wouldn’t work because we have many instances of code that uses these features.
The bottom line is that Adobe cannot change the behavior of the scoping and “var” keyword without breaking thousands of developers’ code. Nearly everyone knows the implications of not VAR-ing your variables and knows how to apply variables across a CFC.
The problem is that we’re lazy. ColdFusion doesn’t make us declare variables ANYWHERE ELSE. Yes, Macromedia messed up pretty bad, but the mess is made, and we’ve all adapted to the mess. We’ll just have to keep it up for all eternity.








See i think they can modify the var keyword without breaking everyone’s code. If they basically deprecated the var keyword so it did nothing i.e.: <cfset var myvariable="hello world"> is the EXACT same thing as <cfset myvariable="hello world"> how many apps would that actually break? I’d be willing to bet it would break exactly zero.
Granted, we’re talking to a very small audience, but I have not heard a single person say "hey wait I use un-var’d variables ALL THE TIME! And i do it on purpose".
I’m not sure what you’re asking for isn’t already there. the THIS scope does exactly what you’re talking about. Set a variable in the THIS scope and it available to the other methods (assuming you return this and update the object instance). Even though it offers the added feature of being publically editable, which Brian pointed out as a bad idea, that doesn’t mean it won’t do what you’re asking for.
Why can’t you do what you’re talking about with the THIS scope?
Comment by Steve Nelson — February 5, 2007 @ 12:00 am
btw, you mentioned some horror story with not var’ing. What happened?
btw, i meant: <cfset> in the last post.
Comment by Steve Nelson — February 5, 2007 @ 12:00 am
Actually Steve, that change would probably break a lot of code. Everyone who un-var-scoped a variable in a method who DELIBERATELY meant for it to go into the CFC’s variables scope would end up with broken code.
The THIS scope and the VARIABLES scope are exactly the same, except that THIS is public and VARIABLES is private. Which is why using the THIS scope is not an option.
Comment by Brian Kotek — February 5, 2007 @ 12:00 am
I disagree. That code would put everything in "variables" scope, which is "A Bad Thing".
As to why, here is my horror story:
I had the following code in an Application-scoped component (never mind why I was looping through a list this way, it worked well in my situation).
<cfscript>
var mylist = arguments.items;
for ( i = 1; i LTE ListLen(mylist); i=i+1 ) {
item = ListGetAt(mylist,i);
//other code that uses "i" and "item"
}
</cfscript>
I tested it and it worked fine ("mylist" can be set with or without var for this example, it doesn’t matter).
Then, days later I get an error on this line:
item = ListGetAt(mylist,i)
The error says that "i" is higher than the number of items in the list. No way that is possible, right?
Wrong.
The code was executing at roughly the same time for two different requests. "i" was getting set as a CFC-level variable on each loop. So, the value of "i" in one run got set by the loop in another.
That is why "var" is essential.
Comment by Steve Bryant — February 5, 2007 @ 12:00 am
Out of curiousity, did that method <cfreturn this>? Possibly updating an application variable? Also were there other methods in that CFC that used the variable i?
If not, how would one i value from one user affect another user, regardless of the var’ing? It almost seems like an underlying CF error that goes beyond the var keyword itself.
Comment by Steve Nelson — February 5, 2007 @ 12:00 am
Yeah, Steve, what Brian said. Do you have excessive wax build-up in your ears? I *DO* use un-var’ed variables ALL THE TIME, and I do it on purpose! They’re called "instance variables".
Comment by Nat Papovich — February 5, 2007 @ 12:00 am
No. The method did not return "this". No the method did not update the value of an Application variable. No other methods used an un-var’ed "i" variable.
Comment by Steve Bryant — February 5, 2007 @ 12:00 am
It might be wax, or it just might be a lack of understanding. Neither would surprise me. Let me see if I understand what you mean.
in some component:
<cfcomponent>
….
<cffunction name=somemethod>
<cfset somevariable="hello world">
</cffunction>
</cfcomponent>
then:
<cfinvoke method=somemethod component=somecomponent>
<cfoutput>#somevariable#</cfoutput>
Is that what you mean? You WANT to do that? Are you serious? Please tell me you’re kidding. Please tell me I misunderstood. Please tell me it’s a cruel joke. Please!
Comment by Steve Nelson — February 5, 2007 @ 12:00 am
Where you’re going wrong Steve is in your second bit of code. This:
<cfcomponent>
….
<cffunction name=somemethod>
<cfset somevariable="hello world">
</cffunction>
</cfcomponent>
Will end up creating a variable named variables.someVariable within the CFC. This is a PRIVATE variable, nothing outside the CFC can access it. It’s the same as doing this:
<cfcomponent>
….
<cffunction name=somemethod>
<cfset variables.somevariable="hello world">
</cffunction>
</cfcomponent>
Comment by Brian Kotek — February 5, 2007 @ 12:00 am
Steve (Bryant) why would var do anything to fix that? I don’t doubt that it does fix it. But why would it? Unless the variable is somehow shared aross requests, why would var’ing give you different results unless there is some type of bigass memory leak in CF? This sounds completely illogical to me.
Now if both requests were updating an application scope variable that was somehow unlocked, I could see this occurring. But you’re saying that’s not the case. Does this happen with a simple cfset statement? Is that all it takes?
Comment by Steve Nelson — February 5, 2007 @ 12:00 am
Ok so for anyone keeping track (i’m trying to, with great difficulty)…
request scope: exists in the calling cfm pages and ALL cfc methods
this scope: exists in the calling cfm template with the prefix of the name of object instance. i.e. <cfset myobject=createobject(….)> then: #myobject.somevariable# Then inside the cfc it is: #this.somevariable#
variables scope or no scope: when no cfcs are used (cfm only) it is available across all included cfm files. When set inside of a cfc it is available across all methods. Not available outside cfc.
var’d variables scope or var’d no scope: available only to the immediate method.
My opinion… get rid of var scope, make un-var’d variables/no scope variables local to individual methods the way a var’d variable is right now. Then add another scope "local" or "cfc" that acts like an un-var’d variables/no scope does right now.
Brian, is that right?
Comment by Steve Nelson — February 5, 2007 @ 12:00 am
Steve (Nelson),
This code works:
<cfscript>
var mylist = arguments.items;
var i = 0;
for ( i = 1; i LTE ListLen(mylist); i=i+1 ) {
item = ListGetAt(mylist,i);
//other code that uses "i" and "item"
}
</cfscript>
The reason is that if i is set with a "var" keyword, it is stored in the variables scope of the component (private variables that persist across the component).
Because the component is stored in Application scope, all variables-scoped variables in the component are effectively Application-scoped variables.
So, effectively, you are right that an Application-scoped variable is in play. This isn’t a memory leak and it isn’t poor thinking on Macromedia’s part.
In my data access components, I can use "variables.datasource" to store the datasource. If that didn’t persist with the component, then I would only be able to use that variable during the request in which the variable was created – which would be of little use.
Using "var" fixes that for the exact same reason that it you should use it in UDFs – it makes the variable a function-scope variable. That is to say that the variable only exists within the function call that created it. It isn’t just local to the function (in the way variables is local to the CFC), it is local to that run of the function.
Comment by Steve Bryant — February 5, 2007 @ 12:00 am
Check out the PDF that Ray Camden put together on scopes in CFCs:
http://ray.camdenfamily.com/downloads/cfcscopes.pdf
Comment by Steve Bryant — February 5, 2007 @ 12:00 am
I’d settle for being able to use "var" anywhere in a method body. Perl programmers are used to doing this with "my". I don’t mind having to type "var", I just hate having to go back to the top of the method and put in a bogus initialisation.
Comment by Jaime Metcher — February 5, 2007 @ 12:00 am
Jaime –
One aspect that has so far escape specific coverage is exactly what you mention – the bogus "initialization". Can we all agree that the var keyword must remain, but the bogus initialization need not remain?
Comment by Nat Papovich — February 5, 2007 @ 12:00 am
Every programming language with procedures/functions/methods has a local scope for said operations. This isn’t because everyone that’s ever designed a language is "idiotic." Quite the contrary, in fact. It’s because the local scope is <i>necessary</i>. If anything, poo-poo Macromedia for <b>not</b> having the var scope in MX 6.0.
Welcome to the 21st century, Nelzian. Your lack of understanding of elementary CompSci and OO principles is truly astounding, even by Armenian standards.
Comment by Paul — February 5, 2007 @ 12:00 am
This is not 21st century technology. This is 20th century technology at best. This "var" BS has been around since the early 90s. We no longer have to worry about pointers and garbage collection, hell we no longer have to worry about cflock. New fangled 21st century languages handle this for us.
I want local variables.
I want global variables.
I simply want the language to deal with these variables in a consistent, clean way. In ColdFusion we have scopes. The "var" keyword is NOT a scope, but it acts like a scope. It is UN-ColdFusion. It needs to go away and be replaced with something that IS consistent with the ColdFusion language.
IMO, the variables scope and the un-scoped-scope was already defined as a "local" scope in a cfmodule. That scope concept should, IMO, transfer to cffunction as the local scope.
That would be like saying application scopes work differently in cfcs than they do in cfms. It just doesnt make any sense.
Will this happen overnight? Of course not. Until then we have to make sure everything is in fact var’d. It means we ALL need to use tools like Mike Schierberl’s VarScoper http://www.schierberl.com/varScoper/
Comment by Steve Nelson — February 5, 2007 @ 12:00 am
I’ve learned something today. I didn’t not know that an unscoped variable in a method is equivalent to the same as using the variables scope. I read a description of the dangers of not var scoping in cfc methods and it was either a bit off or I made some incorrect assumptions.
I was under the impression that unscoped variables were private to the method but not to the instance of the method. For example, I thought the problem was that if two requests hit the same method in the same persistently scoped cfc instance, there could be problems with race conditions. However, I didn’t think that there could be a conflict with two different methods using the same unscoped variables name.
I’ve been using unscoped variables in methods for constants, values that are set once. However if they are in fact variables scoped then there is a chance I will hit some kind of bug following this misguided, though admittedly infrequent, practice.
ARGGG
…and not in the fun pirate way
…more like in the depressed pirate way
Comment by Daniel Roberts — February 5, 2007 @ 12:00 am
Yes Daniel, if you are not properly var-scoping your variables and especially if the CFC is cached into the application scope, you ABSOLUTELY WILL end up with bizarre errors due to threading issues.
Comment by Brian Kotek — February 5, 2007 @ 12:00 am
There is one more thing that is slightly annoying about the way CF currently does the VARIABLES scope thing in CFCs. When tags like cfhttp are used, the tag-specific struct they create ends up in the variables scope. To make it var scoped this is necessary:
<cfset var CFHTTP = "">
<cfhttp ….>
While I don’t mind the var keyword generally, I do find this an annoyance. It’s not the end of the world though.
Comment by James Holmes — February 5, 2007 @ 12:00 am
@Steve:
You wrote:
"See i think they can modify the var keyword without breaking everyone’s code. If they basically deprecated the var keyword so it did nothing i.e.: <cfset var myvariable="hello world"> is the EXACT same thing as <cfset myvariable="hello world"> how many apps would that actually break? I’d be willing to bet it would break exactly zero."
Actually, ever single CFC I’ve ever written would break.
You see I use the "instance" struct variable as a way to declare private data for my CFCs. All my getters and setters have keys in the "instance" struct.
So, my code looks like:
<cfcomponent>
<cfset instance = structNew() />
<cfset instance.private = "Dan Switzer" />
<cffunction name="getPrivate" returntype="string">
<cfreturn instance.private />
</cffunction>
<cffunction name="setPrivate" returntype="void">
<cfargument name="arguments.input" />
<cfset instance.private = arguments.input />
<cfreturn />
</cffunction>
</cfcomponent>
If all variables suddenly become local variables only, I have no way to access the "instance" variable. I don’t want to use the "this" scope, because that’s a public property accessible from the pages that invoke the component. I want my data private and only accessible to my CFC logic.
So, while the "var" keyword definitely feels kludgey, it has its uses. Yes, they could have solved it a different way, but that road was crossed along time ago.
As Mark Mandel pointed out, I just wish I could "var" a variable anywhere w/in my function (like I can in most other scripting languages.) That certainly would help me to keep my bugs down from unscoped variables.
Comment by Dan G. Switzer, II — February 6, 2007 @ 12:00 am
2nd world war wasn’t the end either
Comment by robs — February 6, 2007 @ 12:00 am
I don’t think it would break your app Dan, assuming it’s built in a smart way. That instance struct was defined outside of any single cffunction. Seems to me that it should be ‘local’ to the entire CFC, not a single single function.
Whereas if you had done this:
<cffunction name="getPrivate" >
<cfset instance = structNew() />
<cfset instance.private = "Dan Switzer" />
</cffunction>
Then the instance variable *should* be local to that one function. As if you had typed: <cfset var instance = structNew()/>
Since we’re just dreaming here, to make things even more fun…
<cffunction name="getPrivate" >
<cfset instance = structNew() />
<cfset instance.private = "Dan Switzer" />
<cfreturn this/>
</cffunction>
I think when a function returns THIS, then the local (function level) "instance" variable should become local (CFC level, still not public from the calling template). In other words when you return THIS it should work just like when you defined it before cffunctions.
Granted, this is all just a pipe dream and it’s never going to happen.
Well… not with that attitude.
Comment by Steve Nelson — February 6, 2007 @ 12:00 am
@Steve:
The problem, as I see it, is that the setter by default would set a local variable–not the global variable. How is CF supposed to know that you want to set the global and not a local variable? Anything you do to automate the process brings in scoping issues.
At least w/the var keyword you have explicit control over whether the variable is global or local to the current function.
I guess the thing that doesn’t bother me is that most other scripting language would behave this way–which is probably why Macromedia choose this method. The problem is, CF isn’t really like most other scripting languages.
Comment by Dan G. Switzer, II — February 6, 2007 @ 12:00 am
I’m not sure why this would be difficult for Adobe to build.
A variable cfset above the functions is local to the CFC. A variable set inside of a function checks to see if it already exists at the CFC level, if so it updates the value and keeps it at that level. If it does not exist at the CFC level it automatically gets var’d behind the scenes.
Is this really any more complex than a single (maybe a few) if statements? I can picture how to do the logic in my head using structkeyexists().
If CF *were* like other scripting languages why would we pay $1200 for a copy of it?
Comment by Steve Nelson — February 6, 2007 @ 12:00 am
Steve what you don’t seem to be acknowledging here is that anyone who was already relying on the fact that unscoped variables go into the variables scope would have their apps explode. You do see this, right?
Comment by Brian Kotek — February 6, 2007 @ 12:00 am
Nelson,
I think the approach that you are suggesting makes thing less consistent. It smacks of having the language make decisions/assumptions for me.
Not only that, but it could still break code. Many times variables-scoped variables are first set in an init() method. Now, mine are all explicitly put in variables scope for clarity, but there is bound to be plenty of code where that isn’t the case.
Comment by Steve Bryant — February 6, 2007 @ 12:00 am
Yes I understand that Brian. I was referring specifically to Dan’s issue with setting an "instance" struct above the functions.
Steve (Bryant), CF makes all sorts of decisions and assumptions for you. If I thought hard enough for 5 minutes I could probably list out 50 assumptions it makes for you.
Comment by Steve Nelson — February 6, 2007 @ 12:00 am
OK cool Steve, your post just didn’t seem to be taking that into account and I wanted to be sure. Thx.
Comment by Brian Kotek — February 6, 2007 @ 12:00 am
I think the two key issues I have with the var functionality have been touched on.
1) Let me var anywhere:
<cfscript>
for (var i = 0; i LTE 10; i++)
</cfscript>
2) automatically created variables should be automatically var’ed. If I want them available beyond the function, then I’ll do that.
Oh, and how about
3) How about letting me do the following:
<cfquery name="var PeopleSelect">…
Comment by Calvin — February 6, 2007 @ 12:00 am
I agree with Steve Nelson that the need to declare all your local variables in a language in which you normally create variables on the fly is very unlike the way the rest of coldfusion works and a right royal pain in the backside to boot.
A solution occurs to me and I don’t think it would break existing applications:-
A new attribute in the CFCOMPONENT tag that tells coldfusion that you want all new variables within functions to be created in the var scope. e.g <cfcomponent funcvarlocal="true"….> Then if you wanted to asign something in the variables scope of the cfc you could either explictly reference the scope or trust coldfusion to find the right scope if you had previously declared it.
The result is that var is not depricated but would not need to be used in cfcs with the new attribute. Everybody wins!
Comment by Hamish Charles — February 6, 2007 @ 12:00 am
"A new attribute in the CFCOMPONENT tag that tells coldfusion that you want all new variables within functions to be created in the var scope. e.g <cfcomponent funcvarlocal="true"….>"
Ah, yes, if the default were false, this would indeed solve the problem. I’d use it.
Comment by James Holmes — February 6, 2007 @ 12:00 am
Yes, I didn’t say that explicitly James but the default would need to be false. Also it might be useful as a cf administrator setting, cfapplication setting, or cfsettings parameter, or all three.
Another idea occurred to me in bed last night as I was dropping off to sleep. To help us cope with the current situation and avoid having to go back to the top of the function everytime a new variable is required:
Declare <cfset var my = StructNew()>. Then use this structure for any local simple variables or queries: <cfquery name="my.qryNew"…>, <cfloop index="my.i"….> etc.. You might still want to declare separate var scope variables for your return structure or other complex objects.
This idea came out of what Jaime Metcher said above about the way Perl uses ‘my’ as a way of declaring local variables.
Comment by Hamish Charles — February 7, 2007 @ 12:00 am