Module System

In Haxe, each .hx file is considered a module. A module consists in an (optional) package declaration followed by import statements followed by several type declarations :

// module foo/Test.hx
package foo;

import haxe.Md5;

class Test {
    // ...
}

class Sub {
    // ...
}

private class Priv {
    // ...
}

A type declaration can be :

  • a class or interface
  • an enum
  • a typedef

All types declared in a module belong to the package of this module, except if the type is declared as private : in that case, the type is only accessible from within the module, and its name will not conflit with other types in the same package.

So for instance, in the previous example, we have declared the types foo.Test, foo.Sub and foo._Test.Priv (private class).

An explicit error message will be displayed in case two different modules in the same package are declaring the same not-private type.

Types Reference

In order to access a type from within a module, you can use the fully qualified path foo.Test to access the Test class declared in the module foo/Test.hx.

var t : foo.Test;
// ....
t = new foo.Test();

But you can also use foo.Test.Sub to access the Sub class declared in the module :

var s : foo.Test.Sub;
// ...
s = new foo.Test.Sub();

Import

When importing a module, you are actually importing all its not-private types inside the current global namespace :

import foo.Test;

class Main {
    var t : Test; // reference the type Test inside module Test
    var s : Sub; // imported from module Test as well
}

The package path must be absolute, you cannot use a package path relative to the current package.

When importing an enum, all its constructors will also be accessible as global variables.

If you want to import a single type from a module, you can use the following notation :

import foo.Test.Sub;

This will only import the Sub type declared in the given module, and will then prevent cluttering the module scope with several definitions.

Following the principles of shadowing, the latest types imported always have priority over the previous ones.

Import Whole-package

Instead of importing a single module, you can add a given package to the list of wildcards packages :

import haxe.*;

Everytime a type is not found (following the type resolution algorithm below), we will look at the wildcard packages for a match before displaying an error.

Following the principles of shadowing, the latest wildcard package have priority over the previous ones.

Import Globals

Starting from Haxe 2.11, you can also import classes statics and enum constructors into the global scope, by using :

import js.Lib.document;

This will make the static variable document of the js.Lib type a global variable accessible in the whole module directly.

You can also import all statics by using the following notation :

import js.Lib.*;

This will add all statics of the js.Lib type to the global scope.

Please note that we always use the principal module type (the one named the same as the module) for statics lookup. If you want to import some statics of a module sub type, you can use the following notation :

import foo.Test.Sub.*;

Following the principles of shadowing, the latest globals imported always have priority over the previous ones.

Import Alias

When importing a single type or a single global, you can define an alias name that will be used as a shortcut for this type or global :

import foo.Test in T;
import js.Lib.document in doc;

Shortcuts

One commonly-used pattern of modules is to declare all the data structures that you need in a single module, so you can have them accessible with a single import.

Another pattern is to a create a module that only declares typedefs to make shortcuts for otherwise long-to-write types, for instance in Flash :

// module FastFlash.hx
typedef MC = flash.display.MovieClip;
typedef SPR = flash.display.Sprite;
typedef BMP = flash.display.BitmapData;
typedef EV = flash.display.Event;
// etc.

Then you can simply do :

import FastFlash;
class Button extends SPR {
    var animate : MC;
    //...
}

Type Resolution Algorithm

Inside an expression, you can sometimes have very ambiguous accesses paths such as a.b.C.D.e which could mean many different things, for instance (this.a).b.C.D.e or (a.b.C.D).e (a.b.C.D being a module subtype).

Let's explain how such path is resolved :

  • first, we do a variable lookup for the first element of the path ( a in our example ) . We are looking for (in that order) :
    • declared local variables
    • member variables (current class and superclasses)
    • static variables (only the current class)
    • imported enums constructors
    • imported globals
  • if we couldn't find a match, then we are looking for the most qualified module type (in that order) :
    • type D defined in module a.b.C
    • static D in type C defined in module a.b.C
  • in the particular case where we have no package prefix (C.D.e for example), we instead make a package lookup
    • using the imported wildcard packages
    • using the current module package. If the current package is a.b, we will then look for :
      • type D in a.b.C
      • static D in type C in a.b.C
      • type D in a.C
      • static D in type C in a.C
      • type D in C
      • type C in current module types
      • type C in imported types
      • type C in module C

version #15707, modified 2012-11-18 12:03:17 by ncannasse