I need to know the type of variable in a loosely typed language

Word Count: 265

I have been working on some updates to Hyrule lately and I came across a situation where I had one of those "I wish I could do this" moments. ColdFusion (along with php,asp,etc...) is a loosely typed language. This just means that you don't have to declare a data type of a variable in the language. In other languages such as Java (strongly typed) you must assign a variable a data type. I am not going to get into the pros and cons of both because you can do a quick search and find that out for yourself.

So I am working with a component that has a single function, isValid(). From the name you can probably gather that its going to evaluate some data and return a boolean. The component we are going to use as an example here is the MinValidator.cfc. Don't worry to much about the component itself, lets focus on its task. This component accepts a struct that may look something like this. Now what we need to do is evaluate the value and see if passes some condition. I have always been taught that in these cases the condition should pass until you can find a reason for it not to pass. That is why we have created a variable valid and set it true. If we ran the component as it is everything would pass.

You may be eager to run off and start implementing your min behavior but any good artist doesn't start building without some kind of blueprint. Your first thought might be to write something like this. While this would work you have not stopped to think everything through and ask the important question. Why does min have to refer to just numbers? Min could be the minimum number of characters in a string, the minimum date, the minimum number of rows in a query and so on. With that I realized I needed to switch based on data types. In a loosely typed language such as ColdFusion we have no way to check the type. In a perfect world I would love to be able to do this. Based on a data type we can check the condition and the break out of the switch and return execution back to the caller.

Because we can't do that we have to get a little creative here. First off you should know the difference between simple and complex values. Simple values are strings,lists,numbers,dates,booleans,binary,xml. Complex values are arrays,structures,queries and objects. If I left anything out I am sorry, but you get the idea. The reason we need to separate the 2 is because of the dynamic nature of the language. Take the following example. This will satisfy both of the follwoing statements. So in our case we almost need to follow this logic.

  • If variable is simple data type
    • if numeric : if value < min return false;
    • if date : if value before min return false;
    • else : if string : count characters
The reason we return false is because we have no way of saying I am done get me out of here like we do in a switch (break). So with that this is what I came up with.

In the end its a little more work than my fairy tale scenario but it works. Please don't take this as a "I wish CF did this" because as I said earlier its just the nature of a loosely typed language. You could created your own method that figures out what type it is but in the end I didn't want to have to include that component in every validator so I just went with this. In fact I did create a simple component to do this so if you want to see it let me know. Please comment, thoughts suggests, am I missing something? I up for any kind of feedback on a better solution if its out there.

Comments

#1 Posted By: Henry Ho Posted On: 1/28/10 7:38 PM
If you really prefer the switch style, maybe you should abstract out the if's and else if's into your own function e.g. resolveType() or getType(), then problem solved. :)
#2 Posted By: Henry Ho Posted On: 1/28/10 7:39 PM
and then instead of

if(arguments.prop.value < min) {
11    return false;
12    }

just:

return arguments.prop.value >= min;

save yourself a break statement as well. :)
#3 Posted By: Nathan Mische Posted On: 1/28/10 8:13 PM
I think you could eliminate some of the conditional logic by testing these conditions in order:

isNumeric
isDate
isSimpleValue
isArray
isStruct
isQuery
#4 Posted By: Nathan Mische Posted On: 1/28/10 8:17 PM
I think you could eliminate some of the conditional logic by testing for these conditions in order:

isNumeric
isDate
isSimpleValue
isArray
isStruct
isQuery

This basically removes the if/else and makes the code a little easier to follow. Hyrule looks pretty cool btw.
#5 Posted By: Dan Vega Posted On: 1/28/10 8:33 PM |
Author Comment
Nathan - At first I was trying to do by just setting valid = false but then I switched to returning false right in the check.

Henry - I did that, just didn't want another component involved.
#6 Posted By: Henry Ho Posted On: 1/28/10 8:36 PM
didn't want another component involved? Then use that as a private function. It will greatly improve the readability of your isValid() function.
#7 Posted By: Dan Vega Posted On: 1/28/10 8:45 PM |
Author Comment
I agree but I know I am going to need it other places, decisions decisions..ha
#8 Posted By: Ben Nadel Posted On: 1/29/10 8:37 AM
This gets even MORE exciting when you remember the isNumericDate() validation function. In a typeless world, especially in ColdFusion, dates can be represented in both date and numeric format.

I suppose at some point, you have to make a rule that your components have to access the "right" data type, not a losely types version.
#9 Posted By: Dan Vega Posted On: 1/29/10 12:05 PM |
Author Comment
@Ben - I ended up going with this

BaseObject.cfc
component {

   public string function getType(data){

      if(isNull(arguments.data)){
         return "Null";
      }

      if( isSimpleValue(arguments.data) ){

         if(isNumeric(arguments.data)) {
            return "Numeric";
         }
         if(isDate(arguments.data)) {
            return "Date";
         }
         if(isBoolean(arguments.data)) {
            return "Boolean";
         }
         if(isBinary(arguments.data)) {
            return "Binary";
         }
         return "String";

      } else {

         if(isArray(arguments.data)) {
            return "Array";
         }
         if(isStruct(arguments.data)) {
            return "Struct";
         }
         if(isQuery(arguments.data)) {
            return "Query";
         }
         if(isObject(arguments.data)) {
            return "Object";
         }

      }
   }

}
#10 Posted By: Ben Nadel Posted On: 1/29/10 9:39 PM
Nice.


Post Your Comment

Leave this field empty







Show Captcha

If you subscribe, any new posts to this thread will be sent to your email address.

Copyright © 2007 Dan Vega | BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.