« Colin Moock's Charges Against ActionScript 3.0 | Main | What I'm working on these days »

moockblog String Concatenation Error Explained

Colin Moock blogged today about an interesting little ActionScript 3.0 string concatentation gotcha. I'll try to summarize what he said, but I encourage you to read his post and come back for my explanation.

The issue is that the following code:

trace("Hello" + + " world");

generates the following error message:

1067: Implicit coercion of a value of type String to an unrelated type Number.

Colin calls the error message misleading, and I can see why it seems so, but I'm going to argue that the message is accurate, even if it is obscure.

To understand this issue, we need to see the expression from the compiler's point of view. To the compiler, the expression:

"Hello" + + " world"

is actually the following:

"Hello" + ( + " world")

So the short answer for those of you familiar with operator precedence rules is that the compiler first evaluates the expression (+ " world"), which yields NaN. The compiler then evaluates the expression "Hello" + NaN, which yields "HelloNaN". The reason for the compiler error is that in Strict Mode, ActionScript 3.0 does not allow the application of the the unary "+" operator to a value of type String. You'll get "HelloNaN" as the result if you turn off Strict Mode. If this makes little or no sense to you, read on for a more detailed explanation.

Now for the long explanation. Note that the compiler interprets the expression as if there were added parentheses around the expression (+ " world"). The reason for this is something called "operator precedence". When the compiler encounters an expression, it must apply a set of rules to make heads or tails of the expression. When it encounters multiple operators in one expression, it needs to figure how and when to apply those operators. Which operator should apply first, and to which operands should each operator apply?

In the case of the "Hello" + + " world" expression, the compiler must decide which of the two "+" operators to apply first. It's a little complicated in this case for two reasons. First, there is no operand between the two "+" operators. Second, the two "+" operators appear to be the same operator, but are not. The twist here is that the "+" operator has more than one meaning. Not only can it represent the binary "additive" operator that takes two operands (binary in this case refers to the presence of two operands rather than binary digits), but it can also represent the unary "+" operator that takes only one operand (and converts it to a numeric value).

So which meaning does the compiler apply in this case? Well, the unary "+" operator has higher "precedence", which means that the compiler will always choose to evaluate the unary "+" operator before the additive "+" operator in situations where both can be applied successively. So the compiler acts as if the parentheses were around the expression (+ " world"). The compiler must now try to make sense of the application of the unary "+" operator to a string. Because the sole purpose of the unary "+" operator is to convert values to numbers, the compiler tries to convert the string to a numeric value. In this case, the compiler won't be able to because what number would you choose for the string " world"? In cases where it can't figure out a reasonable number, the compiler will convert the string to a special value, NaN, that represents the theoretical concept "Not a Number".

Now that the compiler has evaluated the subexpression + " world", it turns its attention to the remainder of the expression, which is now "Hello" + NaN. In this case, one of the operands is a string, and when the binary additive version of the "+" operator is used with an operand of type String, the "+" operator tries to concatenate the strings rather than add the values. The compiler will even try to convert the value of the other operand to a string. In this case the compiler succeeds, and the result is the string "HelloNaN".

So what about that weird error message that Colin talked about?

1067: Implicit coercion of a value of type String to an unrelated type Number.

That error occurs only in Strict Mode, where the compiler assumes that you want to catch implicit data type conversions at compile time. This error stems from the compiler's attempt to apply the unary "+" operator to a string, hence the message about converting a string to a number. If you want it to work as expected in Strict Mode, you have to be explicit about the data type conversion, like so:

trace("Hello" + + Number(" world"));

and the result would be the same as if Strict Mode were not selected, namely the string "HelloNaN"

One final note about this little gotcha. You will get numbers instead of NaN if you use a string that resembles a number. What I mean is that if the second string "looks" like a number, you'll get that number instead of NaN. For example, the following code outputs the string "Hello123"

trace("Hello" + + "123");

Or if you prefer Strict Mode:

trace("Hello" + + Number("123"));

As always, I welcome comments and corrections, but I should warn you that I won't be able to approve comments until August 11, so any comments you add may not show up until then.

Comments

But this should never be a problem, since one is not supposed to create messages by concatenating strings.
:-)

This is a bad internationalization practice, since in some languages a number might come before the string (and in general the order of the various message elements is not fixed).
So one should do something like:
var msg:String = loadStringFromResources("stringID");
// the result is something like "Hello {nrValue} world!"
msg = msg.replace( "{nrValue}", "123" );

And even better (of course) to wrap it all in a nice function.
Something more here: http://www.mihai-nita.net/article.php?artID=20060430a (even if not about ActionScript, the problems are the same).

fantastic explanation francis, thanks!

i still contend that the error is "misleading" because it's too general...it doesn't address the specific problem the programmer is having. in an ideal world, the compiler would "coach" the programmer by detecting the pattern "a" + + "b", and explaining the situation (with a link to your blog? ; ). i'm a big fan of "coaching" warnings. for example, i think this code:

public class Test extends Sprite {
public function test () {
}
}

should yield a warning explaining that "test()" won't be considered the constructor for Test because its case doesn't match the class name.

Very nice article francis!

I have to admit thought I cant see a problem with the error. It isnt ideal but it gives you a little insight as to how the innerworkings of the flash compiler works.

I do however also agree with colin with the fact that errors and warnings should not just say the problem but possibly hint towards the cause. The only problem with this is you can send programmers down the wrong path looking for the wrong error.

That said 95% of my debugging time is fixing logical errors rather than syntaxical errors. So it does seem somewhat of a moot point.

Something about this little quirk reminds me of the parseInt(); octal issue in javascript. I can't imagine an issue like this would come up too horribly often though.

Nice explained. There are so many possible sources of error, that an 80% programming like in HTML e.g. just isn't possible. I am almost spending more time on reading blogs in order to annul my failures than to program:/

Are you going to be posting new entries to your blog again soon?

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)