haXe 2: The Dynamic Type and Properties

Exclusive offer: get 50% off this eBook here
haXe 2 Beginner's Guide

haXe 2 Beginner's Guide — Save 50%

Develop exciting applications with this multi-platform programming language

$26.99    $13.50
by Benjamin Dasnois | July 2011 | Open Source Web Development

haXe is the universal programming language which is completely cross-platform and provides a standard library that remains the same—regardless of platform. The Dynamic type is a type with a special behavior. Through this article by Benjamin Dasnois, author of haXe 2 Beginner's Guide, you will learn why, how you can use it, and also what you should pay attention to. Understanding the Dynamic type is a key point in mastering haXe.

Specifically, we will:

  • Use Dynamic variables
  • Learn about Dynamic's pitfalls
  • Learn about the parameterized Dynamic class
  • Implement Dynamic in a class
  • Learn about the use of properties in classes
  • Implement properties in classes

Let's get started.

 

haXe 2 Beginner's Guide

haXe 2 Beginner's Guide

Develop exciting applications with this multi-platform programming language

        Read more about this book      

(For more resources on this subject, see here.)

Freeing yourself from the typing system

The goal of the Dynamic type is to allow one to free oneself from the typing system. In fact, when you define a variable as being a Dynamic type, this means that the compiler won't make any kind of type checking on this variable.

Time for action – Assigning to Dynamic variables

When you declare a variable as being Dynamic, you will be able to assign any value to it at compile time. So you can actually compile this code:

class DynamicTest
{
public static function main()
{
var dynamicVar : Dynamic;
dynamicVar = "Hello";
dynamicVar = 123;
dynamicVar = {name:"John", lastName : "Doe"};
dynamicVar = new Array<String>();
}
}

The compiler won't mind even though you are assigning values with different types to the same variable!

Time for action – Assigning from Dynamic variables

You can assign the content of any Dynamic variable to a variable of any type. Indeed, we generally say that the Dynamic type can be used in place of any type and that a variable of type Dynamic is indeed of any type.

So, with that in mind, you can now see that you can write and compile this code:

class DynamicTest
{
public static function main()
{
var dynamicVar : Dynamic;
var year : Int;
dynamicVar = "Hello";
year = dynamicVar;
}
}

So, here, even though we will indeed assign a String to a variable typed as Int, the compiler won't complain. But you should keep in mind that this is only at compile time! If you abuse this possibility, you may get some strange behavior!

Field access

A Dynamic variable has an infinite number of fields all of Dynamic type. That means you can write the following:

class DynamicTest
{
public static function main()
{
var dynamicVar : Dynamic;
dynamicVar = {};
dynamicVar.age = 12; //age is Dynamic
dynamicVar.name = "Benjamin"; //name is Dynamic
}
}

Note that whether this code will work or not at runtime is highly dependent on the runtime you're targeting.

Functions in Dynamic variables

It is also possible to store functions in Dynamic variables and to call them:

class DynamicTest
{
public static function main()
{
var dynamicVar : Dynamic;
dynamicVar = function (name : String) { trace("Hello" + name); };
dynamicVar();

var dynamicVar2 : Dynamic = {};
dynamicVar2.sayBye = function (name : String) { trace("Bye" +
name ); };
dynamicVar2.sayBye();
}
}

As you can see, it is possible to assign functions to a Dynamic variable or even to one of its fields. It's then possible to call them as you would do with any function.

Again, even though this code will compile, its success at running will depend on your target.

Parameterized Dynamic class

You can parameterize the Dynamic class to slightly modify its behavior. When parameterized, every field of a Dynamic variable will be of the given type.

Let's see an example:

class DynamicTest
{
public static function main()
{
var dynamicVar : Dynamic<String>;
dynamicVar = {};
dynamicVar.name = "Benjamin"; //name is a String
dynamicVar.age = 12; //Won't compile since age is a String
}
}

In this example, dynamicVar.name and dynamicVar.age are of type String, therefore, this example will fail to compile on line 7 because we are trying to assign an Int to a String.

Classes implementing Dynamic

A class can implement a Dynamic, parameterized or not.

Time for action – Implementing a non-parameterized Dynamic

When one implements a non-parameterized Dynamic in a class, one will be able to access an infinite number of fields in an instance. All fields that are not declared in the class will be of type Dynamic.

So, for example:

class User implements Dynamic
{
public var name : String;
public var age : Int;
//...
}
//...
var u = new User(); //u is of type User
u.name = "Benjamin"; //String
u.age = 22; //Int
u.functionrole = "Author"; //Dynamic

What just happened?

As you can see, the functionrole field is not declared in the User class, so it is of type Dynamic.

In fact, when you try to access a field that's not declared in the class, a function named resolve will be called and it will get the name of the property accessed. You can then return the value you want. This can be very useful to implement some magic things.

Time for action – Implementing a parameterized Dynamic

When implementing a parameterized Dynamic, you will get the same behavior as with a non-parameterized Dynamic except that the fields that are not declared in the class will be of the type given as a parameter.

Let's take almost the same example but with a parameterized Dynamic:

class User implements Dynamic<String>
{
public var name : String;
public var age : Int;
//...
}
//...
var u = new User(); //u is of type User
u.name = "Benjamin"; //String
u.age = 22; //Int
u.functionrole = "Author"; //String because of the type parameter

What just happened?

As you can see here, fields that are not declared in the class are of type String because we gave String as a type parameter.

Using a resolve function when implementing Dynamic

Now we are going to use what we've just learned. We are going to implement a Component class that will be instantiated from a configuration file.

A component will have properties and metadata. Such properties and metadata are not pre-determined, which means that the properties' names and values will be read from the configuration file.

Each line of the configuration file will hold the name of the property or metadata, its value, and a 0 if it's a property (or otherwise it will be a metadata). Each of these fields will be separated by a space.

The last constraint is that we should be able to read the value of a property or metadata by using the dot-notation.

Time for action – Writing our Component class

As you may have guessed, we will begin with a very simple Component class — all it has to do at first is to have two Hashes: one for metadata, the other one for properties.

class Component
{
public var properties : Hash<String>;
public var metadata : Hash<String>;

public function new()
{
properties = new Hash<String>();
metadata = new Hash<String>();
}
}

It is that simple at the moment.

As you can see, we do not implement access via the dot-notation at the moment. We will do it later, but the class won't be very complicated even with the support for this notation.

Time for action – Parsing the configuration file

We are now going to parse our configuration file to create a new instance of the Component class.

In order to do that, we are going to create a ComponentParser class. It will contain two functions:

  • parseConfigurationFile to parse a configuration file and return an instance of Component.
  • writeConfigurationFile that will take an instance of Component and write data to a file.

Let's see how our class should look at the moment (this example will only work on neko):

class ComponentParser
{
/**
* This function takes a path to a configuration file and returns
an instance of ComponentParser
*/
public static function parseConfigurationFile(path : String)
{
var stream = neko.io.File.read(path, false); //Open our file for
reading in character mode
var comp = new Component(); //Create a new instance of Component
while(!stream.eof()) //While we're not at the end of the file
{
var str = stream.readLine(); //Read one line from file
var fields = str.split(" "); //Split the string using space
as delimiter
if(fields[2] == "0")
{
comp.properties.set(fields[0], fields[1]); //Set the
key<->value in the properties Hash
} else
{
comp.metadata.set(fields[0], fields[1]); //Set the
key<->value in the metadata Hash
}
}
stream.close();
return comp;
}
}

It's not that complicated, and you would actually use the same kind of method if you were going to use a XML file.

Time for action – Testing our parser

Before continuing any further, we should test our parser in order to be sure that it works as expected.

To do this, we can use the following configuration file:

nameMyComponent 1
textHelloWorld 0

If everything works as expected, we should have a name metadata with the value MyComponent and a property named text with the value HelloWorld.

Let's write a simple test class:

class ComponentImpl
{
public static function main(): Void
{
var comp = ComponentParser.parseConfigurationFile("conf.txt");
trace(comp.properties.get("text"));
trace(comp.metadata.get("name"));
}
}

Now, if everything went well, while running this program, you should get the following output:

ComponentImpl.hx:6: HelloWorld
ComponentImpl.hx:7: MyComponent

haXe 2 Beginner's Guide Develop exciting applications with this multi-platform programming language
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on this subject, see here.)

Time for action – Writing the configuration file

Now, let's write a function in ComponentParser to write a configuration file from an instance of Component. So here is our class completed:

class ComponentParser
{
/**
* This function takes a path to a configuration file and returns
an instance of ComponentParser
*/
public static function parseConfigurationFile(path : String)
{
var stream = neko.io.File.read(path, false); //Open our file for
reading in character mode
var comp = new Component(); //Create a new instance of Component
while(!stream.eof()) //While we're not at the end of the file
{

var str = stream.readLine(); //Read one line from file
var fields = str.split(" "); //Split the string using space
as delimiter
if(fields[2] == "0")
{

comp.properties.set(fields[0], fields[1]); //Set the
key<->value in the properties Hash
} else
{
comp.metadata.set(fields[0], fields[1]); //Set the
key<->value in the metadata Hash
}
}
stream.close();
return comp;
}
/**
* This function takes a path to a configuration file.
* It also takes an instance of Component to save.
*/
public static function writeConfigurationFile
(path : String, comp : Component)
{
var stream = neko.io.File.write(path, false); //Open our file
for writing in character mode
//Let's iterate over metadata
for(meta in comp.metadata.keys())
{
//Write the meta's name, a space, its value, and a space
followed by 1 and new-line
stream.writeString(meta + " " + comp.metadata.get
(meta) + " 1\n");
}
//Let's iterate over properties
for(prop in comp.properties.keys())
{
//Write the properties name, a space, its value, and a space
followed by 0 and new-line
stream.writeString(prop + " " + comp.properties.get
(prop) + " 0\n");
}
stream.close();
}
}

As you can see, the new function is pretty simple too.

Time for action – Testing the writer

A good way to test our newly created function is to continue after our first test and just save the result of parseConfigurationFile.

So, here is the new test class:

class ComponentImpl
{
public static function main(): Void
{
var comp = ComponentParser.parseConfigurationFile
("bin/conf.txt");
trace(comp.properties.get("text"));
trace(comp.metadata.get("name"));

//Test writing
ComponentParser.writeConfigurationFile("bin/out.txt", comp);
}
}

After running this test, you should get a file with the following content:

nameMyComponent 1
textHelloWorld 0

As you saw, that was really simple.

The dot-notation

If you remember, one constraint was to be able to read values of metadata and properties using the dot-notation.

In fact, everything we've done until now has been done only for the sake of this exercise and for you to write some haXe code.

Although this is important, let's move on to implementing access via the dot-notation.

To do that, we will make our class implement Dynamic<String> and implement a resolve method.

Before doing that, there's just one question that arises: how are we going to handle the situation if a key exists in the metadata Hash and in the properties Hash? In our example, we will handle this situation quite easily by simply returning the value that is in the metadata.

So, here is our Component class modified:

class Component implements Dynamic<String>
{

public var properties : Hash<String>;
public var metadata : Hash<String>;

public function new()
{

properties = new Hash<String>();
metadata = new Hash<String>();
}

public function resolve(name : String) : String
{
//If this key exists in the metadata
if(metadata.exists(name))
returnmetadata.get(name);
//If this key exists as a property
if(properties.exists(name))
returnproperties.get(name);
//This key doesn't exist, return null.
return null;
}
}

Note how we added a "implements Dynamic<String>" and the signature of the resolve function.

When we write comp.someName it is as if we had written comp. resolve ("someName") as the compiler makes the transformation automatically.

Thoughts on writing our parser

As you can see, although the proposed subject of this exercise was pretty long, it was pretty easy to implement what we were asked to implement.

Also, the longest part of it indeed was to implement the ComponentParser class and not to implement the dot-notation in order to access metadata and properties.

The dynamic keyword

In haXe code you will sometimes meet the dynamic keyword (all in lowercase). It doesn't have a lot of things to do with the Dynamic type and only appears when declaring a function. It does mean that a method can be rebound. That is, it is possible to change the body of the function.

This keyword is necessary because on some platforms, those methods have to be generated in a special way that can induce some cost on performance.

Here is an example of how to declare such a function:

public dynamic function myFunction()
{
//Do things
}

Warning

In this article we've talked about the Dynamic type. We've been discussing this all through the article but it is important to say it again: the Dynamic type is there to tell the compiler not to do any type checking.

Although the compiler won't complain, it doesn't mean that your code will run correctly. It will depend on the runtime. So, if you're targeting JavaScript which is a dynamic language it is likely that it will work without any problem, but if you're targeting a more static runtime there are chances that you will get into troubles for using Dynamic too much.

Most of the time, Dynamic is used when trying to use some native features of the runtime you're targeting or for interoperability purposes. It can also be used sometimes just to ease development.

It is highly advised to limit the usage of Dynamic as much as possible to avoid problems.

Properties in classes

We have talked a lot about the Dynamic type, but now, we are going to talk about something else: properties.

Use

Sometimes, you would like to be able to react to the access to an object's property. Whether this is when reading from or assigning to the property, the only way to do that is through the use of functions.

It would then be possible to use it with some code looking like this:

myObject.getValue();
//or
myObject.setValue("This is my new value");

This is a non-natural way of accessing properties, and it can make the code really difficult to read, particularly if you are going to chain calls.

With properties, we will be able to define our functions setValue and getValue so that we can react to accesses to our properties. But what is so interesting with properties, is that we won't have to directly call our functions, we will just be able to access properties as if they were simple fields:

myObject.value;
//or
myObject.value = "This is my new value";

This syntax is really more natural and easier to read.

Implementation

Implementing properties is not something very complicated. As you may guess, you will have to write your two accessors (the functions to get and set the value). After writing them, you will have to bind them to the property. All of this will be done easily in haXe.

Writing the getter

The getter of a property is the function that will get called when the user wants to read from your property. This function should take no arguments and return a value of the same type as your property.

Here is a simple example:

public function getWidth() : Int
{
return width;
}

For sure, simply doing that does not have any interest over accessing a simple field, but it's up to you to use properties whenever needed.

Writing the setter

If you need to write a setter for your property, you will need to keep two important things in mind:

  • Your setter will take one argument that will be of the type of your property
  • Your setter will return a value of the type of your property

Because the first point is easy to remember, as this argument will be used to give you the value the user wants to assign to your property, you may really wonder why the second point is needed. Well, the reason is simple; here is some simple code to demonstrate it:

a.value = b.value = c.value

In this example, the b.value gets assigned the value of the c.value and then the a.value gets assigned the new value of the b.value. This code would get transformed to the following if value is a property with the setValue as the setter:

a.setValue( b.setValue( c.getValue ) );

When seeing this, it becomes obvious that the setter needs to return the new value of the property.

Here is a sample code to demonstrate how this could be implemented:

public function setWidth(value : Int) : Int
{
if(value >= 0)
{
width = value;
//Maybe you will want to do some other things to react to the
assignment
}
return width;}

This code actually implements a setter for our width property that will verify that the new value is superior or equal to 0. This actually makes sense for a width since a negative width doesn't make any sense.

Defining the property

So we've seen how to implement the getter and the setter for a property but we now need to define this property.

The syntax to do this is pretty close to the one used to declare a field. The only difference is that we will provide a reference to our getter and to our setter:

public var width(getWidth, setWidth) : Int;

This one was easy, but we will now see how we can do some more advanced things such as forbidding getting or setting a property's value.

Accessibility

Until now, we have created a property that had a getter and a setter that we wrote.

But it is possible to write a property that has no getter or no setter. In order to do this, we will simply write "null" in place of the getter or setter reference.

So, here is an example of a property that would be accessible only to read:

public var readOnly(getProp, null) : String;

Here is an example of a property that would only be writeable:

public var writeOnly(null, setProp) : String;

The default getter

If your getter is simply going to return the value of your field, you may want to use the default setter. This way, you won't have to write such a simple function.

So, for example:

var prop(default, setX) : Int;

This will tell the compiler to directly access the prop field when reading the property's value.

The default setter

As you can use a default getter, you can also use a default setter. The default setter will simply assign the new value to the field and return it.

So, for example:

var prop(getX, default) : Int;

This will tell the compiler to directly access the prop field when writing the property's value.

The dynamic getter and setter

When defining your property, you can also set your getter or setter to dynamic.

If you do so, you will be able to redefine the functions used as setter and getter by using Reflect.setField. To do so, you will have to set the fields "set_" and "get_" followed by the name of your property.

The never getter and setter

When using null as a setter or getter, the class the property is defined in can indeed read or write the value of this property. The operation is only forbidden out of the class.

It is possible to completely forbid an access to a getter or setter by using the never getter or setter.

A compile-time feature

Properties are a compile-time feature; what this means is that when you access a property, the compiler knows that it indeed is a property and knows what setter and getter it refers to. This way, when compiling, the compiler will automatically create a call to the functions.

The only drawback to this approach is that if the compiler doesn't know the type of your variable it won't be able to generate those calls.

So, to sum-up things, in an untyped block and with a variable typed as Dynamic, the compiler won't have a clue that you are accessing a property and not a simple field and, therefore, won't be able to generate function calls.

Have a go hero – Writing a person class

Imagine that you have to write a class that represents a person. A person can be given a name when he/she comes to life but it is impossible to change it afterwards. Try to write a class to represent that (but it should still be possible to read the name).

Summary

In this article we learned about the Dynamic type and the dynamic keyword.

Specifically, we covered what the Dynamic type is, what the parameterized Dynamic type is, and how you can implement both in your own classes. We also covered how you can use properties.

Dynamics can be useful but remember to avoid overuse them as they can break your code at runtime.

On the same note, remember that properties are a compile-time feature only.


Further resources on this subject:


haXe 2 Beginner's Guide Develop exciting applications with this multi-platform programming language
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Benjamin Dasnois

Benjamin Dasnois has always been fascinated by open source software and as such, has been giving courses about Linux in an IT school in France. In the meantime, Benjamin has been following and working with haXe since its beginning and uses it in his professional life. He now works on an open source project, started the integration of haXe into it, and made it work with the technologies that were already in use.

Books From Packt

Agile Web Application Development with Yii1.1 and PHP5
Agile Web Application Development with Yii1.1 and PHP5

Python 3 Web Development Beginner's Guide
Python 3 Web Development Beginner's Guide

Grok 1.0 Web Development
Grok 1.0 Web Development

Flash Development for Android Cookbook
Flash Development for Android Cookbook

Mastering phpMyAdmin 3.1 for Effective MySQL Management
Mastering phpMyAdmin 3.1 for Effective MySQL Management

jQuery UI 1.7: The User Interface Library for jQuery
jQuery UI 1.7: The User Interface Library for jQuery

Learning Ext JS 3.2
Learning Ext JS 3.2

MODx Web Development - Second Edition
MODx Web Development - Second Edition

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
7
C
h
T
4
C
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software