When I first did my exploration of ColdFusion 9's isNull() function, I definitely came away thinking that the isNull() function was merely a short-hand, convenience notation for the existing structKeyExists() function. The other day, however, when blogging about creating proxy objects, I was dealing with a situation in which the base value that I was checking might not always be an object with a struct-like interface. In a scenario like that, my first instinct was to check for "struct-ness" before I checked for "null-ness:"
<cfif ( isStruct( someValue ) && !isNull( someValue.someKey ) )> <!--- Do some conditional logic here... ---> </cfif>
As you can see here, I'm using the short-circuiting nature of ColdFusion's conditional statements in order to check that the value was both a struct and that a key off of that struct was non-null. On a whim, I decided to see if I could remove the isStruct() condition. And, much to my delight, it turns out that isNull() works on simple values just as well as it works on structs.
<!--- Create a string value. ---> <cfset myString = "Boo ya!" /> <!--- Check for a property of it. ---> String Key Null: #isNull( myString.someKey )#
As you can see, we are checking for a property of a string value (which is typically a no-no in ColdFusion). When we run the above code, we get the following output:
String Key Null: YES
It correctly determines that the given key path cannot be gotten off of the given base value. It doesn't matter at all that the base value is a simple value and not a struct (or struct-like object).
Now, like I said, it was sort of my assumption that isNull() was a short-hand for structKeyExists(); however, if we try to rewrite the previous statement using structKeyExists():
<!--- Create a string value. ---> <cfset myString = "Boo ya!" /> <!--- Check for a property of it. ---> String Key Exists: #structKeyExists( myString, "someKey" )#
... we get the following ColdFusion error:
You have attempted to dereference a scalar variable of type class java.lang.String as a structure with members.
As you can see, when we explicitly treat the string as a struct, we get an error; but, if we let isNull() do the key-path checking, the data type doesn't matter.
I know this is a really minor point, but if you think that isNull() is nothing more than a short-hand for structKeyExists() - just as I sort of did - then you really aren't leveraging isNull() to its full potential. ColdFusion 9's isNull() function allows you essentially confirm both data type and key path existence within a single conditional statement.
I love nuances like this one. Thanks for teasing it out. I had the same take that you did.
Heck yeah - it's all about understanding what the language can really do ... and leveraging it :)
Is this a new behavior in CF9 or has it existed previously? I'd love to use this instead of StructKeyExists(), but can't if it's not backwards compatible.
The isNull() function is new in CF9. For CF8, you have to use structKeyExists() or isDefined().
For what it's worth, I use IsDefined("struct1.struct2.struct3.key") all the time without first checking struct1, struct2 or struct3. I can't recall ever using StructKeyExists. But then, that's just me. StructKeyExists probably compiles to much more efficient Java.
To be honest, I don't use isDefined() enough to know the rules behind it. That's cool that you can use arbitrary data types as well. I know there are some differences between isNull() and isDefined(), specifically when checking for scope existence:
It seems that isNull() isn't quite like structKeyExists() and it isn't quite like isDefined(). It's some magical beast unto itself :)
You know how Evaluate() translates a string (with possible #-sign substitutions if it's a string literal) into a variable reference, right? Well basically, for any string, if IsDefined(string) is true, Evaluate(string) will not crash.
In the IsDefined("struct1.struct2.struct3.key") example, I don't really care why it failed: struct1 may not exist, or struct1 may not be a struct, or struct1.struct2 might not exist, etc, etc. All of those possibilities result in the IsDefined() call returning false.
I think you've hit on something with IsNull(). It's sort of similar to a Java HashMap or TreeMap. If you try to get() a key you never put(), the get() returns false. It's like IsDefined("ref") with quotes is equivalent to IsNull(ref) without quotes.
I should have said, if you try to get() a key you never put(), the get() returns null.
Exactly... except for scope evaluation.