Feeds:
Posts
Comments

In ActionScript Puzzlers I presented a list of 10 puzzlers meant to test your knowledge of the language. Without starting there, this post isn’t going to make a lot of sense. I also encourage you to try running the puzzler code yourself before coming back here. If it doesn’t do what you expect, try stepping through or modifying the code until you understand what’s going on.

In this post I’ll reveal the answers to each puzzler, and ways to avoid similar pitfalls in your own coding.

Puzzle 1: Looping

Here’s the output of a call to loopy():
BEE,BOP,BOP,BOO

Why the extra BOP? This code demonstrates the lack of block scope in ActionScript. Although it appears that caps is defined only within the for loop, it is actually defined at the scope of the function. Therefore for the second null in the array, caps was not overwritten, but instead retained its value from the previous iteration of the loop.

The ActionScript compiler effectively shifts all the variable declarations to the top of the method before running your code. This explains why the compiler doesn’t complain when you use a variable before declaring it, or why it gives you a warning when you declare the same variable in two separate loops. This can be especially confusing for programmers with a C# or Java background.

The same issue holds true for JavaScript. Yahoo’s JavaScript architect Douglas Crockford advocates declaring all your local variables at the start of your functions. This avoids any ambiguity about the scope of your local variables. This advice holds true for ActionScript as well.

For the somewhat contrived puzzler example, the following rewrite produces just the one BOP.

private function loopy() : String {
	var names : Array = [ null, "bee", "bop", null, "boo" ];
	var name : String;
	var result : Array = [];
	var caps : String;

	for each (name in names) {
		caps = null;

		if (name != null) {
			caps = name.toUpperCase();
		}
		if (caps != null) {
			result.push(caps);
		}
	}
	return result.join(',');
}

You could further reduce the for loop as follows…

	for each (name in names) {
		if (name != null) {
			caps = name.toUpperCase();
			result.push(caps);
		}
	}

Puzzle 2: Sorted

Here’s the output of a call to sorted():
[ -22, -5, 1, 1, 22, 33, 4, 71 ] (length=8)

By default, Array.sort() sorts using Unicode values, so 4 appears after 22 and 33. To search numerically replace line 3 with:
var sorted : Array = values.sort(Array.NUMERIC);
This produces [ -22, -5, 1, 1, 4, 22, 33, 71 ] (length=8)

A cursory look at the API, may make you think values.sort(Array.NUMERIC | Array. UNIQUESORT); would eliminate the duplicate 1. In actual fact, that call does something far less useful – it returns 0 to indicate the array contains a duplicate.

The new Vector class has an almost identical list of methods to Array, except they wisely chose to replace the confusing sort methods with one that simply takes a typed compare function.

Puzzle 3: Delimiters

Here’s the output of a call to delimiters():
[ 1, 2, 3 ] (length=3)
[ 1, 2, 3 ] (length=3)
[ 1, 2, 3, ] (length=4)
[ , 1, 2, 3 ] (length=4)
[ , , 1, 2, 3, ] (length=6)

ActionScript is inconsistent in how it handles delimiters in array literals. A trailing comma is ignored. A leading comma, or whitespace followed by a comma within the list results in a undefined element added to the Array.

Puzzle 4: Indecisive

Here’s the output of a call to responseOrError(indecisive):
finally wins

In fact finally always ‘wins’ with any combination of try, catch or finally blocks throwing exceptions or returning values. It does make for confusing reading though. It’s generally best to only have a single return call in any function and for it to be the last statement. This may feel like a step backwards, but there’s almost always an elegant way of doing this.

Puzzle 5: Illusive dates

Here’s the output of a call to dates(): start=2009 08 31 finish=2010 03 15

Much like Java, the ActionScript Date class is nasty.

  1. day of the month is 1-indexed , but month is 0-indexed
  2. there’s no static constants for days of the week or months of the year to aid readability
  3. confusing property names: date is day of the month, time is milliseconds since epoch
  4. timezoneOffset is read-only so it’s impossible to cleanly represent timezones other than the local timezone

At first glance, the start date looks like 31st July, but it’s actually 31st August due to month being 0-indexed. finish was messed up because we applied the new date incrementally. See the comments added below for the value of date after each statement.

private function dates() : String {
	var formatter : DateFormatter = new DateFormatter();
	formatter.formatString = "YYYY MM DD";

	var date : Date = new Date( 2009, 7, 31); // 31st Aug 2009
	var result : String = "start=" + formatter.format(date);

	date.fullYear = 2010; // 31st Aug 2010
	date.month = 1;       //  3rd Mar 2010
	date.date = 15;       // 15th Mar 2010
	result += " finish=" + formatter.format(date);

	return result;
}

When setting the month, the day of the month is too big to match any valid day in February. It’s over by 3, so the date is rolled ahead by this amount into March. Finally the day of the month is overwritten with 15.

To avoid surprises like this in your code, add constants for the months, and make your date changes atomically.

public static const JANUARY : int = 0;
public static const FEBRUARY : int = 1;
public static const MARCH : int = 2;
public static const APRIL : int = 3;
public static const MAY : int = 4;
public static const JUNE : int = 5;
public static const JULY : int = 6;
public static const AUGUST : int = 7;
public static const SEPTEMBER : int = 8;
public static const OCTOBER : int = 9;
public static const NOVEMBER : int = 10;
public static const DECEMBER : int = 11;

private function dates() : String {
	var formatter : DateFormatter = new DateFormatter();
	formatter.formatString = "YYYY MM DD";
	var date : Date = new Date( 2009, AUGUST, 31);
	var result : String = "start=" + formatter.format(date);
	date.setFullYear(2010, FEBRUARY, 15);
	result += " finish=" + formatter.format(date);
	return result;
}						

The updated function call now returns: start=2009 08 31 finish=2010 02 15

Puzzle 6: Sparsity

Here’s the output of a call to sparse():
Array: length=101 count=3 Vector: length=101 count=101 ArrayCollection: length=101 count=101

Arrays can be sparse, as in this example, where setting a value at a high index does not fill in all the values in between. Effectively Array is just like a normal Object, except:
a) it has a bunch of handy collection orientated methods you have at your disposal
b) it supports the very handy array literal format

Vectors, in contrast, are typed, are not sparse, and do not support the addition of properties with names other than positive integers (this is enforced at runtime, not compiler time). These limitations make live much easier for the VM. Lookups should now be constant time operations.

Even though ArrayCollection wraps an Array, it does not retain the appearance of being sparse. When you loop through the collection, it ‘fills in’ the gaps with null values.

Puzzle 7: Mixed up

If you ran the following code, you would find that mixedUp() did not return anything at all, but instead throws the following error:
TypeError: Error #1010: A term is undefined and has no properties.

What’s gone wrong? Despite first appearances, the objectLiteral() function does not in fact return an Object, but instead returns undefined. This is due to ActionScript repeating the mistake made in JavaScript; making semicolons optional at the end of statements. Here the compiler interpreted the code as an empty return statement, following by a block containing two labels. I was actually a little bit sneaky here, as that is not even a valid object literal, since I missed out the comma.

To avoid this happening in your code, whenever splitting a statement over multiple lines always finish any line with a symbol that couldn’t possibly be the end of a statement. For instance: || && , + - * / .
You should also consider using the C/Java style syntax for blocks having the { at the end of the previous line, rather than on a line on its own. I’ve used this style in all my examples.

Here’s the code after fixing the objectLiteral() function:

private function objectLiteral() : * {
	return {
		a : 123,
		b : "888"
	};
}		
			
private function mixedUp() : String {
	var lit : * = objectLiteral();
	return lit.a + lit.b;
}

This amended code now outputs the following:
123888

If you’re wondering why it doesn’t output 1011, the + operator uses string concatenation if either argument is a String.

Puzzle 8: Unicoder

Clued up readers will recognize this example from the Java Puzzlers book (Puzzle 14). The reason I included it, is that it’s one of the examples that produces quite different results in ActionScript.

\u0022 is unicode for " (double quote). In Java, Unicode characters are given first class treatment, with the compiler seeing it as no different from an actual double quote. In this case the statement becomes the equivalent of:
return "a".length + "b".length; // returns 2

Here’s the output of a call to escaped() in ActionScript:
14

ActionScript recognizes the characters as Unicode, but it doesn’t translate them into symbols until after the parser has translated the source into tokens. It treats the Unicode as if it were escaped characters. The following code produces identical results.

private function escaped() : int {
	return "a\".length + \"b".length;
}

Puzzle 9: Constantly confused

Create the following two classes:

package
{
	public class A
	{
		protected const FOO : String = "foo";
	}
}
package
{
	public class B extends A
	{
		public var result : String;
		
		public function B()
		{
			result = 'before: ' + FOO;
			super();
			result += ' after: ' + FOO;
		}
	}
}

Then try running the following:

private function superDuper() : String {
	return new B().result;
}

Calling superDuper() will return the following result:
before: null after: foo

Unlike Java, ActionScript lets you position a call to super() anywhere you like in your constructor. If you don’t include a call to super() yourself, the compiler will add one at the start of your constructor. This flexibility comes with extra responsibility. Constructor code before super() must not rely on member variables or constants in any superclass. In this case, changing the FOO constant to be static will result in it always being properly initialized.

Puzzle 10: Extended Array

If you attempted to run arrayMax() any number of things could happen.

Under Flex 3.4 I get the following result:
len=3 max=NaN

Under Flex 4.0, I generally get the same as Flex 3.4, but if I chose to display the results in a Spark TextArea, I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert Function-9 to flashx.textLayout.container.ContainerController.

What is going wrong here? Our fancy new max() function is now being returned when iterating through any Array! Even though the length of numbers is correctly reported as 3, if you were to iterate through the items using for each, you would find there are in fact 4 values.

To prevent this from happening, you need to indicate that this function should not be included in iterators, using the setPropertyIsEnumerable method.

Here’s an updated version of arrayMax() that now produces the expected result: len=3 max=10

private function arrayMax() : String {
	Array.prototype.max = function() : Number {
		var array : Array = this;
		var result : Number = Number.MIN_VALUE;
		for each ( var item : Number in array ) {
			result = Math.max( result, item );
		}
		return result;
	};
	Array.prototype.setPropertyIsEnumerable("max", false );
	
	var numbers : Array = [ 1, 2, 10];
	return StringUtil.substitute('len={0} max={1}',
		numbers.length, numbers.max());
}

ActionScript Puzzlers

Joshua Bloch is well known for his work on several Java APIs and the excellent book Effective Java. He is also co-author of Java Puzzlers, a catalogue of the less well traveled quirks and corner cases of the language. This is invaluable reading for enthusiastic Java specification pedants, and sadistic job interviewers. If you are not caught out by at least a handful of these puzzlers, you no doubt design JVMs in your spare time.

In homage to this book, I’ve collected a small collection of oddities specific to Actionscript 3 (AS3). ActionScript has its roots in ECMAScript, but moved away from prototypal inheritance with AS3 favouring strong typing and class-based inheritance. I have the good fortune of being able to draw on three sources for my collection of puzzlers:

  1. JavaScript – There is no shortage of verbiage on the interwebs on the shortcomings of JavaScript. With its shared roots, ActionScript inherits both the good and also some, but not all, of the bad parts.
  2. Java – A number of the Java Puzzlers are directly applicable to AS3. Rather than repeating these here, I’ve listed the puzzlers that also apply to AS3 and suggest you get the book and try them out yourself, if you’re interested (see footnote).
  3. ActionScript – A few puzzlers exist due to features unique to ActionScript.

Without further ado, let’s get to the puzzlers.

Puzzle 1: Looping

What does a call to loopy() return?

private function loopy() : String {
  var names : Array = [ null, "bee", "bop", null, "boo" ];
  var result : Array = [];

  for each (var name : String in names) {
    var caps : String;

    if (name != null) {
      caps = name.toUpperCase();
    }
    if (caps != null) {
      result.push(caps);
    }
  }
  return result.join(',');
}

Puzzle 2: Sorted

What does a call to sorted() return?

private function sorted() : String {
	var values : Array = [ 22, 33, 71, 1, 4, -5, -22, 1 ];
	var sorted : Array = values.sort();
	return arrayToString( sorted );
}

public static function arrayToString( a : Array ) : String {
	return StringUtil.substitute(
		"[ {0} ] (length={1})",
		a.join(', '), a.length);
}

Puzzle 3: Delimiters

What does a call to delimiters() return?

private function delimiters() : String {
	var result : String = "";
	result += arrayToString( [ 1, 2, 3 ] ) + '\n';
	result += arrayToString( [ 1, 2, 3, ] ) + '\n';
	result += arrayToString( [ 1, 2, 3, , ] ) + '\n';
	result += arrayToString( [ , 1, 2, 3, ] ) + '\n';
	result += arrayToString( [ , ,1, 2, 3, , ] ) + '\n';
	return result;
}

public static function arrayToString( a : Array ) : String {
	return StringUtil.substitute(
		"[ {0} ] (length={1})",
		a.join(', '), a.length);
}

Puzzle 4: Indecisive

What does a call to responseOrError(indecisive) return?

private function responseOrError( myFunction : Function ) : String {
	var response : String;
	try {
		response = myFunction();
	} catch ( e : Error ) {
		response = e.message;
	}
	return response;
}

private function indecisive() : String {
	try {
		throw new Error("try wins");
	} catch (e : Error ) {
		throw new Error("catch wins");
	} finally {
		return "finally wins";
	}
}

Puzzle 5: Illusive dates

What does a call to dates() return?

private function dates() : String {
	var formatter : DateFormatter = new DateFormatter();
	formatter.formatString = "YYYY MM DD";

	var date : Date = new Date(2009, 7, 31);
	var result : String = "start=" + formatter.format(date);

	date.fullYear = 2010;
	date.month = 1;
	date.date = 15;
	result += " finish=" + formatter.format(date);

	return result;
}

Puzzle 6: Sparsity

What does a call to sparse() return?

private function sparse() : String {
	var values : Array = [ 1, 2 ];
	var valuesAC : ArrayCollection;
	var valuesVector : Vector.<int>;
	var count : int = 0;
	var value : int;
	var result : String = '';
	values[ 100 ] = 3;
	for each (value in values) {
		count++;
	}
	result = StringUtil.substitute("Array: length={0} count={1} ",
		values.length, count );

	valuesVector = Vector.<int>(values);
	count = 0;
	for each (value in valuesVector) {
		count++;
	}
	result += StringUtil.substitute("Vector: length={0} count={1} ",
		valuesVector.length, count );

	valuesAC = new ArrayCollection(values);
	count = 0;
	for each (value in valuesAC) {
		count++;
	}
	result += StringUtil.substitute("ArrayCollection: length={0} count={1}",
		valuesAC.length, count );

	return result;
}

Puzzle 7: Mixed up

What does a call to mixedUp() return?

private function objectLiteral() : * {
	return
	{
		a : 123
		b : "888"
	};
}

private function mixedUp() : String {
	var lit : * = objectLiteral();
	return lit.a + lit.b;
}

Puzzle 8: Unicoder

What does a call to escaped() return?

private function escaped() : int {
	return "a\u0022.length + \u0022b".length;
}

Puzzle 9: Constantly confused

Demonstrate a const having two different values within the same function call.

Puzzle 10: Extended Array

The Array class includes a number of methods that encourage functional style programming. For instance, the following line takes a list of tweets, filters down to those created today, extracts the body them adds these to a blotter.

allTweets.filter(createdToday).map(body).forEach(appendToBlotter);

This syntax is very readable, avoids out-by-one errors and encourages you to break up your code into short well defined methods. Since Array is a dynamic class, you can extend it to add your own methods, however this would limit its usefulness since it would only work on instances of your subclass, and not regular Arrays. Although prototypal inheritance is generally discouraged, it comes in very handy in this circumstance. By adding methods to the Array prototype, they will become available to all Array instances.

Here’s an example of using prototype inheritance on the Array class. What does a call to arrayMax()return?

private function arrayMax() : String {
	Array.prototype.max = function() : Number {
		var array : Array = this;
		var result : Number = Number.MIN_VALUE;
		for each ( var item : Number in array ) {
			result = Math.max( result, item );
		}
		return result;
	};

	var numbers : Array = [1, 2, 10];
	return StringUtil.substitute('len={0} max={1}',
		numbers.length, numbers.max());
}

Answers are now available here.

Continue Reading »

Flex data binding provides a powerful mechanism for reacting to data changes, but it can be exceptionally tricky to debug when it goes wrong. This post is an accumulation of advice gleaned from colleagues, blogs and painful personal experience into how to avoid some of the nastiest of binding woes.

The best place to start is to read Chapter 40 (Data Binding) of the Adobe Flex 3 developer guide; a great introduction to the topic. I’m taking this as assumed reading, so won’t be covering the many tips contained therein.

Let’s jump straight into the gory details…

1) Don’t use constants as Bindable event names.

public static const ASK_PRICE_CHANGE : String =
   "ASK_PRICE_CHANGE";

// DON'T DO THIS!!!!!

[Bindable(event=ASK_PRICE_CHANGE)]
public function get askPrice():Number
{
	return _askPrice;
}
public function set askPrice(value : Number) : void
{
	if (value != _askPrice) {
		_askPrice = value;
		dispatchEvent(new Event(ASK_PRICE_CHANGE));
	}
}

It’s a great idea in theory, but it doesn’t work. It looks like you’re using the constant here, but it’s just a literal string. If you change the constant value, the binding will not fire.

2) Don’t rely on the execution order of bindings.
You can’t rely on the order staying the same, even if it works for you now. A refactoring of your source file, or changes to the behaviour of future Flex compilers could cause your assumption to fail in future. Here’s a good example:

<mx:Panel id="foo"
   visible="{model.canViewFoo}"
   includeInLayout="{foo.visible}">
...
</mx:Panel>

This worked most of the time in Flex 3.0, but fails very frequently in Flex 3.3. The right way to do it is:

<mx:Panel id="foo"
   visible="{model.canViewFoo}"
   includeInLayout="{model.canViewFoo}">
...
</mx:Panel>

3) Be wary of swallowed exceptions
Exceptions thrown in binding expressions, or in functions called within binding expressions are silently captured by the binding framework.

4) [Bindable("propertyChange")] is not the same as [Bindable]
Even though behind the scenes, the default event name is “propertyChange”, if you explicitly refer to it in your [Bindable] tag, the compiler assumes you will be generating the event yourself. If other properties in the same class are bindable, their updates will not trigger a binding update on your property too. Worse yet, if you try and use dispatchEvent(new Event("propertyChange")) in your setter, you will end up with type coercion errors as the binding framework expects events with the name “propertyChange” to actually be of type PropertyChangeEvent. If you actually dispatch a PropertyChangeEvent event, all will be well, however you might as well of just labelled the property [Bindable] in the first place and let the code generator handle the donkey work.

5) Take extra care when using interfaces in binding expression
If you refer to your bindable class via an interface in a binding expression, you need to be aware of the following limitations:

  1. If the [Bindable] event name in your interface is different from the [Bindable] event name in your class, binding events will not fire.
  2. If you have both a getter and setter for a property and no [Bindable] tag in your interface, binding events will not fire.
  3. If you have only a getter for your property and no [Bindable] tag in your interface, binding events will fire. This is just a weird artifact and shouldn’t be relied upon in future.
  4. Be extra extra careful with nested interfaces.

If you wish to refer to your domain objects using interfaces in classes that use bindings, make sure the bindings metadata exactly matches. If you have multiple implements of an interface with different [Bindable] tags, don’t use the interface for binding expressions, as chances are, it won’t work. Instead, use Object and all your different binding implementations will work as expected.

Debugging bindings
When bindings do go wrong, what’s the easiest way to debug them? You can view the generated source, but don’t expect it to be especially enlightening. About the only useful thing you will glean from perusing the source, is to marvel at just how much of it there is.
A more constructive way to debug is to use the undocumented BindingManager class. This shows you when each of your bindings fire. Add the following line to your Application…

BindingDebugger.debugComponent(this);

…and include the following class…

package
{
  import mx.binding.BindingManager;
  import mx.binding.IBindingClient;
  import mx.core.UIComponent;
  import mx.core.mx_internal;

  use namespace mx_internal;
  
  // Kudos to Cathal Golden for coming up with this technique
  public class BindingDebugger
  {
    
    private static const BINDINGS_PROPERTY : String = 
      "_bindingsByDestination";

    public static function debugComponent (
      displayObject : Object, recursive : Boolean = true) : void
    {
      if (displayObject is IBindingClient)
      {
        var bindings : Object = displayObject[ BINDINGS_PROPERTY ];
        for ( var binding : String in bindings )
        {
          BindingManager.debugBinding(binding);
        }
      }
        
      if (recursive && displayObject is UIComponent)
      {
        var component : UIComponent = UIComponent( displayObject );
        for (var i : int = 0; i < component.numChildren; i++)
        {
          debugComponent(component.getChildAt(i), true);
        }
      }
    } 
  }
}

…and you should see all your binding expressions traced out to the console with output like this:

Binding: destString = bid.text, srcFunc result = Bid $15.31
Binding: destString = calcSpread.text, srcFunc result = Calc Spread $2.79
Binding: destString = ask.text, srcFunc result = Ask $18.12

Next post, I’ll look at ways to optimize data binding when dealing with low latency streaming data.

It’s been a long time coming, but I finally managed to get Shuffle out the door and into users hands. I started work in 2007 as an entry in the Android Developer Challenge mostly for the draw of the prize pool, but also as a chance to learn what looked like a great mobile OS. After not winning I didn’t touch the code for several months. I did feel validated in my design decisions however when a month after posting the original screenshots OmniGroup published it’s first glimpse of OmniFocus on iPhone. Since they both follow the GTD mantra fairly closely, this isn’t too surprising.

I started working on Shuffle again in earnest a couple of months ago when I secured an Android Dev phone. Using the app on a real device was a huge leap forward. I made a ton of changes based on my experience using it every day.

I plan on stepping back a little now, but I don’t plan on abandoning the project entirely. One current idea is to setup a Shuffle GWT website running on the promising Java Google App Engine. I plan on kicking the App Engine tires soon, so should have a better idea then.

Follow

Get every new post delivered to your Inbox.