May 16, 2015 #Development#Haxe#OpenFL#JavaScript
ECMAScript programmers (JavaScript, ActionScript, etc) are familiar with writing object literals in JSON notion. This appears to work in Haxe, but you'll quickly run into pitfalls and type issues. A bit of background about the Haxe type systm will help you avoid a lot of frustration.
Foreword: This is the first in hopefully a series of posts reviewing the nicities and pitfalls of the Haxe language from the perspective of an ECMAScript (aka JavaScript and ActionScript) hacker.
In languages like JS and AS3, you can quickly prototype code with Objects that look like this:
var config = {
name:"foo test",
values:[1.2, 3.5]
}
In ECMAScript, we think of config simply as an Object with String keys and values of any type. And it's dynamic - we can always add or delete more keys and values (in Haxe, Dynamic means something entirely different, but we'll get to that later.)
TLDR; Be careful with this notion, it's like a distant cousin, twice removed.
If you're just learning Haxe, you may have heard that it's very similar to JS or AS3, and you might write JSON literal objects like the above. It would compile and appear to work, as you can see here:
var config = {
name:"foo test",
values:[1.2, 3.5]
}
trace(config.name);
trace(config.values);
Run this code try.haxe.org/CF27C
But beware -- the object you've created with the same JSON notation you'd use in JS/AS3 is actually quite different from the one you'd get in those languages, and if you don't understand why it can be quite frustrating. How so? Read on...
The first thing you might notice is that config is not a dynamic object. You try to add a key/value pair to it, and you get a compiler error:
var config = {
name:"foo test",
values:[1.2, 3.5]
}
config.timestamp = 1431797453;
Build failure
Test.hx:7: characters 2-18 : { values : Array, name : String } has no field timestamp
Run this code try.haxe.org/47240
Wow, that's a confusing error message for a newcomer... until you understand how Haxe interpreted your config object. More on that later; you just wanna get something working so you impatiently plow forward...
TLDR; Don't do this until you understand the type system.
So being an ECMA-minded hacker, you may search for a dynamic JSON-like object and find the Dynamic class. It seems to do what you want, supporting dot notation and adding more key/value pairs:
var config:Dynamic = {
name:"foo test",
values:[1.2, 3.5]
}
config.timestamp = 1431797453;
trace(config.name);
trace(config.values);
trace(config.timestamp);
Run this code try.haxe.org/6C7d1
But you will soon realize the drawbacks of this approach when you try to iterate over your object, or access with array notation (e.g. config[key]). You try a bunch of different things you'd do in ECMA-land, until you eventually find the Reflect API, and 4 hours later you have code that doesn't perform well and is an ugly mess:
var config:Dynamic = {
var config:Dynamic = {
name:"foo test",
values:[1.2, 3.5]
}
config.timestamp = 1431797453;
// Failed attempts to iterate:
// for (key in config) { }
// Err: You can't iterate on a Dynamic value...
// var keys = config.keys();
// Err: Uncaught TypeError: undefined is not a function
// Arg, this is ugly and verbose!
for (key in Reflect.fields(config)) {
// trace("config."+key+" = "+config[key]);
// Err: String should be Int
trace("config."+key+" = "+Reflect.field(config, key));
}
Run this code try.haxe.org/37A3f
"If it was this hard to instantiate, iterate, and access an object," you think to yourself, "I can't imagine doing anything serious in Haxe." You give up on Haxe and curse the name of Nicolas Cannasse.
Side note: Actually, there are two Haxe compiler improvements that vould make this much more tenable -- I'll make sure to voice these ideas:
Then you'd just be able to access Dynamics easily like in ECMAScript, and Haxe already provides lots of ease of use features.
Ok, slow down, don't give up yet. First understand that Haxe isn't ECMAScript, and its type system is actually much more nuanced and powerful. This is good as it allows for better performing code, and sometimes it helpfully infers this automatically... But as with all things powerful and complex, there are quite a few pitfalls to avoid.
If you're wiser and less lazy than I am, you'll read the Haxe documentation on Types and the Type System before jumping in -- maybe read it twice, and you'll discover some really useful and time-saving stuff:
For starters - you thought you specified a ECMA-ish dynamic Object with JSON:
var config = {
name:"foo test",
values:[1.2, 3.5]
}
But in fact, due to the sluggish performance of dynamic objects, Haxe infers an anonymous structure with two keys, name and values. So that compiler error above starts to make more sense:
Build failure
Test.hx:7: characters 2-18 : { values : Array, name : String } has no field timestamp
You can't dynamically add keys to such a structure. (This is a trafe-off so the Haxe compiler can optimize access to that object.)
You can also initialize your object as a Map. This is an optimized hash object that has specific types
for its key and value, but to most closely match your JSON Object, you could use Map
static function main() {
var config:Map<String, Dynamic> = [
"name"=>"foo test",
"values"=>[1.2, 3.5]
];
config.set("timestamp", 1431797453);
trace(config);
for (key in config.keys()) {
trace("config."+key+" = "+config.get(key));
}
}
Run this code try.haxe.org/4d150
A few things to note:
AS3 programmers: Note that iterating over the Map itself ala for (key in config) iterates over the values -- which is the behavior of for each in AS3. See try.haxe.org/#6B474
So Map<String, Dynamic> is perhaps most like Objects in JS/AS3.Indeed. Maps are great, but if you start nesting objects, without the dot or array access notion, things start to get more hairy. You start to see why EMCA dot accessors and array accessors make working with objects so quick and easy.
The good news is, Haxe has some incredibly power language features -- such as macros, abstract classes, and the ability to define array access -- that can help. I found a great post (can't locate it just now) about an Abstract Object class that gets pretty close to EMCA Objects, and I'm playing with the notion here: try.haxe.org/#F1bA1
Let me know in the comments if you know of related work.
I plan to add more posts about Haxe type checking and type inference from the perspective of a coder familiar with JavaScript or AS3. Check back or watch my twitter feed for that!
comments powered by Disqus