Did Actionscript 3.0 Miss The Boat?

Sadly, I haven't had quite the chance to dig into AS3 (and Flex2) that I had hoped. My one big-ish project went out the window (an FTP client), as I had almost no time to work on it (I got the basics going, connection, but not much beyond there). I have mucked about a bit, but...alas, such is life. We recently got a copy of the Actionscript 3.0 Cookbook in the office, so I thought it would be a good opportunity to bang out some subway time and do some reading to get up to speed.

And, I have to ask, did Actionscript 3.0 miss the boat?

Ok, yes, AS3 is way, way better than AS2. Proper access modifiers, reflection, sockets, you name it. It's big, it's wild, and it is seriously, phenomenally better than 2. DisplayList? Kick. Ass. Regular Expressions? Bloody brilliant.

So why, I ask you, why is it, with a complete and total redux of the Flash "BCL", that the API is so unusable?

Let's start by looking at events. Yes, EventDispatcher has been beefed, and is now a base class for visual object (Sprites, MovieClips, etc). But why is it still using the same broken API it used in AS 2.0?

From the AS3 Cookbook, on Handling Events (p. 10,11):

The type parameter is the type of event you to listen to. In this case, it would be the string, "enterFrame". However, using string literals like that opens your code to error that the compiler cannot catch. If you accidentally typed "enterFrane", for example, your application would simply listen from an "enterFrane" event. To guard against this, it is recommended that you use the static properties of the Event class. You should already have the Event class imported, so you can call the addEventListener method as follows: addEventListener( Event.ENTER_FRAME, onEnterFrame );
Now, if you accidentally typed Event.ENTER_FRANE, the compiler would complain that such a property did not exist.

I agree - constants are better than magic numbers or strings, absolutely. However, we've still got an API that asks for a string - it does not require, for example, a valid EventType type. You can still subscribe to Event.SOUND_COMPLETE, which, while a valid constant of the Event class, is not an event that a visual object generates. Not to mention that the compiler will still not generate an error if you pass in any string variable, like, say, this.toString()? Wouldn't it be better (at the very least) for the constants to be maintained on a class that generates the event? For example, Sound.SOUND_COMPLETE, DisplayObject.ENTER_FRAME? (Don't even get me started on the SCREAMING CAPS...)

Further, couldn't this have been built such that classes have Events, and the Events can be subscribed to? Do away with addEventListener entirely! Do what .NET does, and make Events first class objects (yes, I know there is an Event class, but it's unfortunately not the same thing). Would the following not be a simpler API entirely?

  1. package
  2. {
  3.      class MyBroadcaster
  4.      {
  5.           public event SomethingHappened( args:SomethingHappenedEventArgs );
  6.  
  7.           public static function Main() : void
  8.           {
  9.                var broadcaster:MyBroadcaster = new MyBroadcaster();
  10.                broadcaster.SomethingHappened.addListener( myHandler );
  11.           }
  12.  
  13.           public static function myHandler( args:SomethingHappenedEventArgs ) : void
  14.           {
  15.                /* ok, deal with the event */
  16.           }
  17.      }
  18. }

Boom. No need to subclass EventDispatcher (or import flash.events.Event). No need to worry about symbolic constants (which, incidentally, are not only declared on the Event class, leading to potentially more confusion - where do you find the constants for events a Socket can generate, for example?). As above, Classes hold events as properties typed as "event", and events can be subscribed to, and, oh - we might even get some type safety on the event arguments.

As another example, let's look at the Sound class. We're probably all familiar with the Sound class - we could loadSounds in AS2, and all was well. And in AS 3, we still can. There is, however, a slight change in the API. The old method signature looked like this:

  1. var aSound:Sound = new Sound();
  2. aSound.loadSound( "path/to/my.mp3" );

Pretty simple, yes?

Well, this changes in AS3. The method name has changed to load (which I do approve of - Sound.loadSound seems kind of redundant to me), and so has the parameter. Instead of passing the pass to the file to load, we are now to pass a URLRequest.

  1. var aSound:Sound = new Sound();
  2. var request:URLRequest = new URLRequest( "path/to/my.mp3" );
  3. aSound.load( request );

Now, I'd say loading a sound from a URL is pretty standard practice. It's going to happen quite a lot, and a lot of people are going to write little "SoundUtility" classes that do something like:

  1. package com.jn.utility
  2. {
  3.      class SoundUtility
  4.      {
  5.           public static function CreateAndLoad( uri:String ) : Sound
  6.           {
  7.                var s:Sound = new Sound();
  8.                s.load( new URLRequest( uri ) );
  9.                return s;
  10.           }
  11.      }
  12. }

So that we can now simply import our package and call "CreateAndLoad( "path/to/my.mp3" )". So why does the API not simply do this natively?

To paraphrase one of Brad and Krzysztofs points in "Framework Design Guidelines": make simple scenarios simple to implement. Yes, it's only one extra line of code, but why can't the Sound class handle that without me caring about it? Or knowing about it, for that matter. It could be an overload, at the least - either pass a URLRequest or a string.

And we've got Regular Expressions. Hallelujah! So why is there still no trim method on the String object? We've got replace, finally. Why not trim? It's a trivial regular expression to implement, actually:

  1. function trim( value:String ) : String
  2. {
  3.     var re:RegExp = new RegExp( "^([\s]*)([^\s].*[^\s])([\s]*)$", "i" );
  4.     return value.replace( re, "$2" );
  5. }

(To be fair, I'm pretty sure that I've messed up the syntax on that - possibly even the regex.)

The point is, it's an obscenely common usage scenario. The AS3 Cookbook included it's own utility class for Strings that includes trimming methods (which work by traversing the chars in the string - not sure which is better, to do that or use the RegExp, either way...). So why would such a commonly needed method not be included on the String class?

Again, I'm not saying AS3 is horrible, and that you should ignore it and switch to XAML/.NET immediately. Hardly! I think Flash is and will continue to be the de facto standard for RIAs, and AS3 is actually a really incredible upgrade/update. I'm just wondering if the opportunity the Flash/Actionscript team had with the new AVM and the new code model has not been capitalized on to the extent that it could have. It's a huge paradigm shift for most Flash people - so why not go the extra mile and leave behind the legacy of Actionscript 2?

[Of course, yes, I realise that much of this has to do with ECMAScript compliance, Events in particular...but again, behind the scenes, the compiler can generate whatever code it wants, right? Why not give developers that extra bit of syntactic sugar [plus type safety, etc] to make it impossible for them to make silly mistakes, instead of recommending that they try to avoid them?]

What do you think? Are their other instances where the API could be cleaned up? Am I totally off my rocker?

Share me: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati

Comments (8) left to “Did Actionscript 3.0 Miss The Boat?”

  1. Keith Peters wrote:

    “Of course, yes, I realise that much of this has to do with ECMAScript compliance”

    There you go right there. And ECMAScript is about the language itself, not the code generated by the compiler (as far as I know). I wouldn’t say they missed the boat. Maybe left a few dinghies on the dock, but the cup is 3/4 full. (How’s that mixed metaphor? :))

  2. jason wrote:

    Actually, to be fair, the only part of the above rant that is covered by the ECMAScript compliance is the event model. And regardless of that, the location of the constants for the event types could have been better implemented.

    And I do take issue with the ECMAScript implementation of events - to my mind, events are better served from an API perspective as first class members of a language, not as an add-on.

    I agree that the cup is 3/4 full, and I’m definitely happy with it. Ranting just seems to in my nature. :)

  3. jason wrote:

    Another example: flash.net.URLRequest
    Description (from livedocs):
    “A URLRequestHeader object encapsulates a single HTTP request header and consists of a name/value pair.”

    I just have to ask: if it “encapsulates a single HTTP request header”, why is it not called HttpRequestHeader? If you read the rest of the docs, HTTP request shows up numerous times. URL Request: not once (outside of class names).

  4. anoni wrote:

    Have you ever try to catch a releaseOutside event in AS3?… It belongs to the Juggler’s World.

  5. David Backeus wrote:

    Found the link to your rant on flashcomguru and I totally agree with what you bring up. I just started using AS3 a few days ago and I got frustrated with the exact same issues. Loading an XMLDocument is even worse than Sound because you have to create an HTTPLoader, URLRequest and then follow up with an XMLDocument instead of just running load on an XML object. And since it’s no longer godo practice to add functionality to base classes I end up creating a whole bunch of utility / helper classes that I have to import for the most trivial of tasks. It all reminds me of this one: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

  6. Ali tan Ucer wrote:

    i think, you guys are missing the point on URL Request. Keep in mind that, soon flash can be used to create desktop applications through Apollo. i think it is a necessary to distinguish the type of the call, since the user can load something from the hard-drive also.
    Just trying to make sense.
    Ali

  7. jason wrote:

    Ali: be that is it may, I don’t think requiring an argument of type URLRequest is necessary to distinguish where something is being loaded from - browsers do fine by changing the protocol (http: == web request, file: == local hard drive). Additionally, Flash has always been able to load things off a hard drive - using a Projector, for example, you could load files off a CD (forgetting that a .swf can load files from the hard drive).

    To me, that is not justification for making the API more complicated. And if the URLRequest was required for Apollo and/or in specific situations (making a request for a file on the User’s HD, as you mentioned), then there should be an overload. One overload could take a FileRequest, one could take a URLRequest, and one could take a string - the string could be parsed for protocol, or the method could behave differently based on whether the .swf is executing in the context of a web page or an Apollo application.

  8. Tim Kindberg wrote:

    I think that AS3 went in the right direction, strictness. But I agree that now that they have nearly perfected that system of coding, they need to think again about loosening up and adding fun and convenient shortcuts and sugar. Its the old adage “Know the rules, then you can break them”. Well, we know them now.

Post a Comment

*Required
*Required (Never published)