Compile-Time Constants
Alec McEachran’s latest article about constants reminded me of a trick I’ve recently learned and became a big fan of. His article expresses the pain endured by those who wish for both speed and maintainability in their AS3 apps. The solution the article doesn’t reach though is that of compile-time constants. This is truly the best of both worlds, so let’s learn about it.
Firstly, and perhaps the technique’s greatest downside, is that the constants are defined outside of the AS3 code. Instead, they are passed as arguments to MXMLC like this:
mxmlc -define=NAMESPACE::constantName,"expression"
You get to pick what NAMESPACE, constantName, and expression are. For example, here’s a way better way to access pi than Math.PI:
mxmlc -define MATH::pi,"3.14159265"
Then your AS3 code would look like this:
function circleArea(radius:Number): Number { return MATH::pi * radius * radius; }
The MATH::pi part is replaced by the constant you define on the command line, so you literally get this code compiled:
function circleArea(radius:Number): Number { return 3.14159265 * radius * radius; }
If you want to change it, you just change it in one place: the command line. More likely you are using Ant or Make or the like to manage your build, so simply change it in a properties file or the like. Now, let’s see a related math function:
function circleCircumference(radius:Number): Number { return 2 * MATH::pi * radius; }
Which yields the code:
function circleCircumference(radius:Number): Number { return 2 * 3.14159265 * radius; }
But 2 * 3.14159265 can be reduced since it too is a constant. So let’s define one:
mxmlc -define MATH::pi,"3.14159265" -define MATH::twoPi,"MATH::pi*2"
Notice how we do not even need to duplicate the constant in other compile-time constants. Now we can re-write our code like this:
function circleCircumference(radius:Number): Number { return MATH::twoPi * radius; }
Which yields:
function circleCircumference(radius:Number): Number { return 6.28318531 * radius; }
And we have now reached the optimal speed possible. We are not accessing any variable, even a local variable, we have the definition of the constant defined in one place, and we have easily readable code. It would be nicer if we could have these definitions defined in the AS3 source (like #define) rather than on the command line, but this seems like a quibble.
Well there you have it: the answer to a lot of trouble with constants. No more shall you worry about the speed of static access, that constants really just get compiled to variables, and the like. You can have true #define-style constants right now in AS3. For more on the subject, I leave you to read Adobe’s (rather hidden) documentation about this. As a bonus, it also covers conditional compilation (which I may too cover at some point) and how to use string constants. It’s only three pages, so I recommend it for everyone to read. Enjoy!
#1 by Alec McEachran on November 18th, 2009 ·
I thought this is where you were going, Jackson ;) Thanks for the thorough analysis, though.
I disagree with you though that a lack of #define is a quibble. A major concern of mine is that code should be as readable as possible. Although a constant name should be readable in itself, in this case the constant’s value is separated from the code into a command line argument. If the constant was the sort of thing that migth be tweaked from time to time (unlike PI!); how do I go about changing that? If I’m a coder new to the project, how do I find it? It adds a layer of complexity that I dislike.
Annoyingly too, if you’re using FDT (and probably other editors) you’ll litter your code with errors making discerning the real errors harder to discern and devaluing the tool.
It is good to know about, but I don’t like it because of these concerns. Speed ain’t everything!
#2 by jackson on November 18th, 2009 ·
Speed definitely ain’t everything, which is why I bothered to point out the annoyance caused by having your constants outside of the AS3 source. However, I think you could mask this pretty well since the constants you define are actually AS3 code that is simply evaluated at compile time. For example, you can easily have a simple name-value pair file that is read in as an Ant properties file. Here’s an example math.properties:
This should be understandable by any programmer (or non-programmer) but still, as you point out, it’s something “special” that a newbie would have to learn. I think that, for a one-minute lecture on where the constants are and why, the payoff is huge! Integrating it into an Ant build should be easy too:
To me it seems like, in the grand scheme of a project, a quibble. Of course everyone is free to decide for themselves just how big of a deal this is for their project.
#3 by John Lindquist on November 18th, 2009 ·
@Alec – if you’re using FDT, wrap compiler constants with /*FDT_IGNORE*/ to make the errors disappear.
#4 by Nick Bilyk on July 22nd, 2010 ·
FDT’s lack of conditional compilation argument support is a deal breaker for me. FDT_IGNORE tags are problematic for obvious reasons.
#5 by jackson on July 22nd, 2010 ·
A deal breaker for using FDT or compile-time constants? Even with the annoyance of FDT_IGNORE, compile-time constants can have huge payoffs in very hot areas of the code that may outweigh the inconvenience.
#6 by Alec McEachran on November 18th, 2009 ·
You’re right, of course. Sorry for making my point so clumsily, and thanks for the tip John.
I suppose my main gripe is to do with the compiler; I hate to see code readability sacrificed for compilation issues. Compiler’s should be artful enough to sort this out without workarounds.
#7 by jackson on November 18th, 2009 ·
You’re right, too. :) None of this would be necessary if the compiler just inlined constants for you. Instead, const is just a synonym for var other than some error checking to make sure you don’t inadvertently change the value. Maybe in a future version of MXMLC they’ll address this and we can get rid of workarounds like these.
#8 by c1 on March 31st, 2012 ·
I thought that you would like to know that you can in fact define constants from within an Actionscript file.
See this link for more information :
http://wiki.ecmascript.org/doku.php?id=proposals:program_configuration
#9 by Clark on June 27th, 2013 ·
Thanks Jackson! As always I end up here when I search for something. Imagine you have 2 classes. GirlThemeAssets and BoyThemeAssets both embed a huge jpeg.
You have App::Theme with a string “b” or “g” set by ant in your build script.
If ( App::Theme == “b”)
{
_theme = new BoyThemeAssets();
}
else
{
_theme = new GirlThemeAssets();
}
Are both these classes embedded into the swf or is this dealt with at compile time?
#10 by jackson on June 27th, 2013 ·
As long as you’re not using the embed class anywhere else it could technically be removed by the compiler as an optimization to your SWF/SWC file size. However, I don’t think the optimizer will normally do this. For example, I’ve put in lines like this and never referenced them but they still kept classes around:
To find out for sure, try using the command-line
swfdump
tool from your Flex SDK. It’s in thebin
folder. That will tell you all kinds of information about your SWF. For example, this will give you disassembled bytecode:If your asset class is still in the bytecode, it didn’t get removed.
#11 by Clark on July 3rd, 2013 ·
Thanks Jackson!
I got around to testing this and you are right! The class is still included along with the assets for both.
Seems I will need a couple of entry points here with ANT instead.
Never used swfdump before, I was embarrassed as it appears to have been around for 12 years.