Feb 3, 2010

Making ActionScript Look Beautiful: Increasing Readability and Loosing Verboseness

After playing around with languages such as Ruby and Groovy, I’ve been yearning to loose a lot of the verboseness of ActionScript. I feel that many languages tend to be overly expressive, and hide the concepts of what your code is actually doing.

Lets take a simple example of a snippet of code that represents, Is tomorrow after today?

function isTomorrowAfterToday():Boolean
{
    var now:Date = new Date();
    var today:Date = new Date(now.setHours(0, 0, 0, 0));
    var tomorrow:Date = new Date(now.setDate(now.date + 1));
    return tomorrow.getTime() > today.getTime();
}

To me this is bloated, confusing and unreadable. Creation logic is obfuscated and not DRY. Variables are created to represent common concepts, and comparison operators are used that don’t represent the real domain. However, this is how I see most code in ActionScript written. It is the path of least resistance to getting your project done. Most languages are like this and give you the lowest levels of the API, without providing any means to working with them easier. Why not give the developers a proper toolkit to make this more sensible?

So lets try cleaning it up:

function isTomorrowAfterToday():Boolean
{
    return Date.tomorrow().isAfter(Date.today());
}

See how much better that is. So what have we done here?

  1. First, we’ve consolidated the creation logic (Date.tomorrow() and Date.today()), and they belong to the class they represent. Not only is it readable, but it’s DRY.
  2. Second, we’ve taken the greater than operator and made it into a method, Date.isAfter(). Is tomorrow greater than today? No! Dates have no concept of greater than or less than.
  3. Third we’ve removed accessors that return the primitive data that the class uses to store state. Why should my code have to know that Date.getTime() returns the primitive data used to compare dates? To selfishly put it; the only thing my code should care about is to ask the question, Is tomorrow after today?

Let’s take it one step further:

import flash.date.tomorrow;
import flash.date.today;
 
function isTomorrowAfterToday():Boolean
{
    return tomorrow().isAfter(today());
}

Getting closer. So what have we done? Well, we’ve taken the concept of creation, and have moved it once again. Creation can be generalized into the package of which the main class it represents. In this case, the Date class can be given it’s own package flash.date.

Taking it another step further:

import flash.comparison.is;
import flash.date.tomorrow;
import flash.date.today;
 
function isTomorrowAfterToday():Boolean
{
    return is(tomorrow().after(today()));
}

Perfect! Again, so what have we done here? We’ve abstracted the concept of equality into it’s own package. We can express other concepts of comparison such as not().

Like so:

import flash.comparison.is;
import flash.comparison.not;
import flash.date.tomorrow;
import flash.date.today;
 
function isTomorrowNotTheSameAsToday():Boolean
{
    return is(tomorrow().not(today()));
}

Lets take a whole different concept, such as distance, and model the simple question, Is 10 feet farther than 5 feet?

import flash.comparison.is;
import flash.distance.Distance;
import flash.imports.use;
 
function is10FeetFartherThan5Feet():Boolean
{
    use(Distance);
    return is((10).feet().fartharThan((5).feet());
}

You might be asking yourself, “Is this really ActionScript?” The answer is yes, and can be accomplished with the use of ActionScript’s Object.prototype static property. Again what is all of this doing?

  1. First, we’re telling our function that we’ll be using classes and methods for distances. This acts like an import statement, and adds prototype methods to Flash’s Number class.
  2. Second, we’re creating distances based off of a number. When we call 10.feet() we’re calling a prototype method that we’ve defined on Number to return a Distance object of 10 feet. In English, this reads much nicer.
  3. Third, we’re reusing the concepts that we’ve established earlier. is() is an equality concept that we defined within the flash.comparison package. We’re using method names used to compare objects of the same type, Distance.fartherThan(). Lastly, we’re comparing the primitive values of an object, by not having to compare the values of both distances explicitly with call like Feet.value.

Many of the concepts are things you can start using today. Begin by adding reusable and readable comparison methods, such as Person.isOlder() or Person.isYounger(). It’ll increase the reusability of your domain and make it much more rich. Also, add and chain together static builder methods in your classes, i.e. var dan:Person = Person.thatIs().male().age(24).name("dan").

Happy coding, and keep it clean!

6 Comments

  • wow i didn,t know that!
    Can you share some class/code?
    I like the way is done, but can,t figure how to start

    • Sure, I can throw something up to help you out. Is there anything in particular?

  • Nice read! I have to do a lot of refactor but the code will be very clear!

  • You should only waste time abstracting code if you see yourself using similar code again and again. For example, now you want to compare seconds, and minutes, and hours. you’ve got to now write the code for all that when you could have instead just making a minor edit to the first snippet of code you wrote.

    it’s also not saving any keystrokes because you now have to write those include states in each file that uses it.

    And how often to you find yourself comparing dates? seems like you could have saved yourself the some time and effort if you just used the first snippet.

  • @Vicapow

    You make some good arguments but you can also look at it from the point of view that remember methods like Dan has created is much easier to remember in the long term than what he has done in the first version of his isTomorrowAfterToday method. Of course, this won’t be for everyone but its a great suggestion!

    @Dan

    I love what you have done here. When dealing with something like dates and numbers methods like these can be super useful especially if complex logic is required. The type of date methods you have here reminds me of some of the capabilities in ColdFusion. Clear concise methods always win because its easier to remember than the code you have in the first version of isTomorrowAfterToday, for example. Or at least I think so. Reminds me of jQuery and so much easier to not only get things done but the code itself is pretty much self documenting.

    Is what you talked about here part of your Express library on GitHub? (https://github.com/danschultz/express)

    • Yup its in the Express project. It’s largely abandoned, but some of these concepts will make their way into Mesh, especially for dates.

      A lot of Express was injecting methods into an object’s prototype property. Unfortunately, unless an object was marked as dynamic, or you didn’t have compile time checking, you had to cast the object first (i.e. Object( date ).isBefore(today)).

      I think a better approach for AS3 is to use package level functions. It’s less OO, but removes the clutter of the casts. You’ll find examples of this in the mesh.core package of Mesh.

Leave a comment

About Dan

  • I'm the Lead Software Architect at Mixbook. A rapidly growing online photobooking and scrapbooking company.
  • I built Cayri, an online monitor for flight simulator networks.
  • I enjoy learning guitar, astronomy, beer, music, programming, and driving with no apparent destination.
  • I live in San Jose, CA.

Categories

Twitter

  • Could not connect to Twitter