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 Scotch On The Rock (SOTR) 2010 (London) with:

What If ColdFusion Recognized More Truthy / Falsey Values

By Ben Nadel on
Tags: ColdFusion

I think all programming languages have the concept of a Boolean - that is, a value that can be either True or False. Most programming languages then additionally have Truthy / Falsey values - that is, values that can be implicitly converted into Boolean values. In ColdFusion, the number zero is considered a Falsey. In Javascript, a good number of values can be considered Falsey. I know that some people posit that Truthy / Falsey values make code harder to read; but, I love them. Sometimes, I wonder what ColdFusion would look like if more values could be implicitly converted to True and False.

To consider the appropriate Truthy / Falsey values, one must simply consider the conditional checks that he or she uses most often within a ColdFusion application. When it comes to control flow, here's what I check most often:

  • Whether or not a query has any rows.
  • Whether of not an array has a length.
  • Whether or not a string is empty.

Taking these conditions, we can now encapsulate them within a ColdFusion user defined function (UDF), truthy():

  • <cffunction
  • name="truthy"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I determine if the given argument can be converted / shoe-horned into a boolean value. By default, we will be trying to create a FALSE; everything else will be TRUE.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="value"
  • type="any"
  • required="false"
  • hint="I am the truthy / falsey value being converted into a Boolean value."
  • />
  •  
  • <!---
  • Check to see if the given argument can be converted to a
  • False value. The following will be considered falseys:
  •  
  • -- NULL / undefined value
  • -- False (boolean)
  • -- Empty queries
  • -- Empty arrays
  • -- Empty structs
  • -- Zero (numeric) [same as Boolean already]
  • -- Empty strings
  • --->
  • <cfif (
  • !structKeyExists( arguments, "value" )
  • ||
  • (
  • isBoolean( arguments.value ) &&
  • !arguments.value
  • )
  • ||
  • (
  • isQuery( arguments.value ) &&
  • !arguments.value.recordCount
  • )
  • ||
  • (
  • isArray( arguments.value ) &&
  • !arrayLen( arguments.value )
  • )
  • ||
  • (
  • isStruct( arguments.value ) &&
  • !structCount( arguments.value )
  • )
  • ||
  • (
  • isStruct( arguments.value ) &&
  • structKeyExists( arguments.value, "toBoolean" ) &&
  • !arguments.value.toBoolean()
  • )
  • ||
  • (
  • isSimpleValue( arguments.value ) &&
  • !len( arguments.value )
  • )
  • )>
  •  
  • <!--- This can be considered a false. --->
  • <cfreturn false />
  •  
  • </cfif>
  •  
  • <!---
  • If we made it this far, then the given value could not be
  • converted to a False; as such, it must be True (that which
  • is not false must be true - there is no room for anything
  • in between).
  • --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!--- Build up an empty query. --->
  • <cfset myQuery = queryNew( "id, name" ) />
  •  
  • <!--- Build up an empty array. --->
  • <cfset myArray = [] />
  •  
  • <!--- Build up an empty struct. --->
  • <cfset myStruct = {} />
  •  
  • <!--- Build up an empty string. --->
  • <cfset myString = "" />
  •  
  •  
  • <!--- Now, let's test each of these "empty" data types. --->
  • <cfoutput>
  •  
  • <!--- Null value. --->
  • <cfif truthy( javaCast( "null", "" ) )>
  • Null Value: True
  • <cfelse>
  • Null Value: False
  • </cfif>
  •  
  • <br />
  •  
  • <!--- Empty query. --->
  • <cfif truthy( myQuery )>
  • Empty Query: True
  • <cfelse>
  • Empty Query: False
  • </cfif>
  •  
  • <br />
  •  
  • <!--- Empty array. --->
  • <cfif truthy( myArray )>
  • Empty Array: True
  • <cfelse>
  • Empty Array: False
  • </cfif>
  •  
  • <br />
  •  
  • <!--- Empty struct. --->
  • <cfif truthy( myStruct )>
  • Empty Struct: True
  • <cfelse>
  • Empty Struct: False
  • </cfif>
  •  
  • <br />
  •  
  • <!--- Empty string. --->
  • <cfif truthy( myString )>
  • Empty String: True
  • <cfelse>
  • Empty String: False
  • </cfif>
  •  
  • </cfoutput>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • To make sure we aren't getting false Falses, let's run the
  • same tests with non-empty values.
  • --->
  •  
  •  
  • <!--- Build up a non-empty query. --->
  • <cfset queryAddRow( myQuery, 1 ) />
  •  
  • <!--- Build up a non-empty array. --->
  • <cfset myArray = [ "Joanna" ] />
  •  
  • <!--- Build up a non-empty struct. --->
  • <cfset myStruct = { tricia = "sexy" } />
  •  
  • <!--- Build up a non-empty string. --->
  • <cfset myString = "Outstanding" />
  •  
  •  
  • <!--- Now, let's test each of these "full" data types. --->
  • <cfoutput>
  •  
  • <br />
  • <br />
  •  
  • <!--- Non-empty query. --->
  • <cfif truthy( myQuery )>
  • Non-Empty Query: True
  • <cfelse>
  • Non-Empty Query: False
  • </cfif>
  •  
  • <br />
  •  
  • <!--- Non-empty array. --->
  • <cfif truthy( myArray )>
  • Non-Empty Array: True
  • <cfelse>
  • Non-Empty Array: False
  • </cfif>
  •  
  • <br />
  •  
  • <!--- Non-empty struct. --->
  • <cfif truthy( myStruct )>
  • Non-Empty Struct: True
  • <cfelse>
  • Non-Empty Struct: False
  • </cfif>
  •  
  • <br />
  •  
  • <!--- Non-empty string. --->
  • <cfif truthy( myString )>
  • Non-Empty String: True
  • <cfelse>
  • Non-Empty String: False
  • </cfif>
  •  
  • </cfoutput>

For the most part, you can see that this ColdFusion user defined function (UDF) simply brings the conditional logic into the function, allowing the core values to be seen as true or false depending on their runtime state. I've also added a check for structCount() to the list of possible truthy / falsey concerns; and, I've provided for a struct-like object to present a toBoolean() method.

NOTE: I think that Railo has these "magic functions" for component-based type casting; but, I could not find a reference to them.

After the UDF, I'm simply running a few tests to make sure that our Falseys are False and our Truthys are True:

Null Value: False
Empty Query: False
Empty Array: False
Empty Struct: False
Empty String: False

Non-Empty Query: True
Non-Empty Array: True
Non-Empty Struct: True
Non-Empty String: True

Truthy / Falsey values are pretty awesome. But obviously, having to use an intermediary user defined function in order to leverage these truthy / falsey values takes some of the allure out of it. Perhaps one day, ColdFusion will allow us to provide functions that define a more robust runtime datatype casting?




Reader Comments

I usually just use query_name.RecordCount and arrayLen(array_name) and len(string) when checking, because although they're not directly binary, it still has the zero/positive number duality.

I wonder what the difference in system resources is for each method.

Reply to this Comment

@Kate,

Yeah, that's what I typically use currently. But, sometimes, I really wish I could just do:

<cfif query> ... or
<cfif array>

Or maybe I'm just crazy :)

Reply to this Comment

@Lola,

You'd still have to know what kind of data types you are working with. This is just meant to be a short-hand for common use cases. For example, I could always use find() as such:

<cfif (find( .. ) neq 0)>

... but rather than using the explicit comparison, I would much rather (and do) use this:

<cfif find( ... )>

Here, the numeric index is being implicitly converted to a Boolean. Of course, I still need to know that the value coming back IS a number that that non-zero values are TRUE.

So, it works IF you know what you're dealing with - you can't get out of that :)

Reply to this Comment

Ben, you're definitely NOT crazy for wishing this. :) I have wished that Coldfusion would cast more values to boolean for a long time. I mean, it's already such a loosely-typed language, it would make a lot of sense to extend the loosely-typed nature to cast everything you mention above to boolean when a boolean is needed.

I switch back and forth between programming in Javascript and Coldfusion so much, and I use truthiness/falsiness in Javascript so much that I very often forget about CF's selectivity in this area. I can't count the number of times I've seen:

The value '' cannot be converted to a boolean

on my pages. :)

Reply to this Comment

@Darren,

Yeah, I love that Javascript will treat "" as a falsey! The whole concept of a truthy value is the closest thing to "Just do what I mean!" in programming :)

Reply to this Comment

A minor quibble Ben, in this section:

# (
# isSimpleValue( arguments.value ) &&
# !len( arguments.value )
# )

only works if the simple value is truly empty. What about when there is a space or similar value? For most practical purposes a value with an space is also empty. Accordingly I'd make a slight change to this section:

# (
# isSimpleValue( arguments.value ) &&
# !len( trim( arguments.value ) )
# )

regards,
larry

Reply to this Comment

Nice idea (and one that I also wish that CF would do more of) -- it gets tiring going through the whole test exist - test populated - test valid -- thing.

BUT, since I'm such a negative guy working for such a negative organization, we always assume falsey until proven otherwise. This way, we don't take any sort of good action to let you continue until your present a valid value, and so we'd never assume to return truth.

Reply to this Comment

@Ben,

I think JavaScript's ability to use "if (obj.prop)" as an existence test is transforming you and your expectations for CF. It makes "if (prop in obj)" seem like StructKeyExists, doesn't it?

Reply to this Comment

Ben, I completely agree with that these values are hard to read. There's a programmer here that always does:

<cfif len(string)>
...
</cfif>

Instead of <cfif string neq "">.

It works, but if you're reading the code fast, you will have to process that in your mind if you're not used to. Like:

1-) Well, here he is retrieving the length of a string
2-) The length can be any number
3-) The boolean for 0 is false, so if it is empty, then the result will be 0, which is false.
4-) if it is 1, then it will be true
5-) If it is more than 1.. then.. well, I think Coldfusion will interpret that as true too.

For me it would be much easier to read <cfif string neq "">, it wouldn't make me think too much.

Reply to this Comment

@Larry,

I wouldn't be opposed to adding a trim to the simple value.

@Brian,

Ha ha ha :)

@WebManWalking,

Yeah, the "in" is very much like structKeyExists(). And then Javascript also has the hasOwnProperty() object method as well.

I'm just trying to pull in ideas from the various languages we use.

@Henrique,

I think it all is a matter of what you are accustomed to seeing. It's like the first time I ever looked at Ruby code and thought it looked like someone just scribbled all over the screen; the syntax was so different and confusing. But, people clearly love it.

So for me personally, I almost never have to test the length of a string unless I'm validating against a max length. Typically, the kind of validation that I do is for non-zero lengths.

Reply to this Comment

@henrique

There is reason for using the len(trim()) or len() type of evaluation. Evaluating a boolean or a number is much more efficient and faster than doing an string evaluation like <cfif string neq "">.

Reply to this Comment

Very neat idea.

@Ben/Lola,
Being able to do "<cfif query>" would be handy if query wasn't always a query object. An example being if you declared a variable at the start of a function as 0, but the setting the variable to the value of a query was the result of some if logic (so you may not always want to make the database call, so a query object is only created when you are making a database call). That would save on writing:

<cfif isQuery(query) and query.recordCount>

Granted, if query were ever 1, that would bork things up. But that goes back to knowing what you're working with.

@Larry,
I like that idea for trimming strings, but I would probably make it an optional argument for the rare cases that you want to include whitespace as a valid character (similar to listToArray).

For some reason whenever I read "truthy" or "falsey" in this post, I think of some cartoon characters in a sunny field teaching children about boolean logic.

Reply to this Comment

Love truthy-falsies.

I've worked in very negative environments as described before also. In some languages, doesn't -1 evaluate to true? I found that confusing sometimes, because I generally often think... > 0 is true, 0 or below is false. And I think there is a language that evaluates it this way, but I am certain I recently got into a conversation with someone where we discussed the fact that -1 is true. I could see where this might would be a bit confusing to someone, especially someone who was used to negative, or "non-positive" numbers being false.

Reply to this Comment

@Anna,

I think most languages that allow -1 to be true is because for that language, any non-zero numeric value is true.

This made me realize that I tend to force the "false <= 0" logic whenever I have a numeric variable that could be negative. I don't think I've ever intentionally let -1 be true (unless I was testing for that specific value). I'm not necessarily opposed to it, I just don't seem to use it. There must be a case for that though, right?

Reply to this Comment

That -1 as a language primitive sounds very familiar -- I'll have to dredge around the headspace to see if I can find it.

My alternative point, @Ben, and you may have missed it through all the negativity is that in a secure computing environment, it is a Good Thing(TM) to assume falsey or denial until truthy is proven. Land Shark is a classic case of where you would want to deny anyone until they prove who they are (and no, "candygram" isn't enough)

Same thing with data -- Don't trust that a value has value until is proven otherwise -- sounds a little paranoid (and recursive), but has great value in keeping secure systems secure.

If I pass a structure, and it somehow passes the deliberative checks for being not being falsey, does that mean it's REALLY truthy? What if you haven't tested all possibilities for falsey? We're human (except for Camden, he's a Jedi), and I'd rather default return falsey but prove the truthy test internal -- unless I can guarantee that I've covered every falsey test AND nothing in future will ever come up that might trip it up.

Make sense? [I wrote this drivel, and it makes sense to me, but i work in a negative environent...]

Reply to this Comment

PHP has an empty(value) function that does something similar. Where we might write structKeyExists(URL,'param') a whole lot of PHP code is written !empty($_GET['param']) instead of the true equivalent of array_key_exists('param',$_GET).

I have an aversion to the empty()-style checks because it just looks like you're using a value before you're sure it actually exists, which is something I spend quite a bit of time reinforcing that my students shouldn't do. But if that's how the majority of PHP code is written, then that's how I'll teach it ...

Reply to this Comment

@Anna,

Yeah, as @Mike said, anything that is non-zero is true (in languages in which numbers can be implicitly converted to booleans).

@Brian,

I think, though, that there is a difference between data be considered valid from a business standpoint vs. true/false. For example, if you are presented with a Boolean value, there's no sense in assuming its False until proven that its True - the nature of a Boolean is to be either True or False. It's an entirely different thing to say is this *particular* boolean allowed to be True in this *particular* case, which is where you are going.

At the end of the day, whether or not a empty query is a truthy/falsey is completely different from whether or not YOUR application can allow for an empty query.

@Rick,

Speaking of checking values before they exist, isNull() is a fun example of that :) But, it is one that I have gotten used to. Of course, I am still a HUGE fan of CFParam - I like me some juicy defaults.

Reply to this Comment

@Patrick,

Ha ha. Man, I need to stop being a child and get myself on GIT. I want to be one of the cool kids!

Reply to this Comment

@Ben:

I'm not on GIT either. When you finally do it, would you please write an article here about how to set up an account and how it works?

Pretty please? :-)

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.