I. langage universel Web A. Introduction à haXe

haXe se prononçant « hacks », a pour premier objectif d’être un langage universel pour le Web. En effet, il rassemble en une même syntaxe et un ensemble de librairies standard , tous les outils pour développer une riche application pour Internet. haXe est également un compilateur et cible 3 plateformes : • Interpréteur Javascript (client) • Machine virtuelle Flash Player (client) • Machine virtuelle NekoVM (serveur)

Javascript haXe génère un unique fichier javascript (.js) utilisé pour l’ensemble des actions nécessaires à notre page Web dynamique. On accède aux API traditionnelles du navigateur et de javascript. HTML + CSS + haXe/JS permettent de réaliser des applications AJAX sophistiquées et très simplement. haXe apporte, entre autres, au développement traditionnel en javascript, une organisation des données en classes et une vérification des types à la compilation.

Flash haXe génère du bytecode Flash 6 à Flash 8, exécuté par la machine virtuelle AVM1 du Flash Player ou du bytecode Flash 9, tournant sur la AVM2 du Flash Player 9. On accède à toute l’API Flash de base (selon les versions de Flash désirés). Le bytecode produit par haXe est plus performant que celui d’Adobe Flash ou Flex. Une option du compilateur permet de générer des fichiers sources AS3 à partir d’un projet haXe.

Neko haXe génère du bytecode pour la machine virtuelle NekoVM. On accède a l’API du system, de la base de données, etc. Le bytecode tourne sur la nekoVM en « stand alone » (utilisation directement dans le système d’exploitation en ligne de commande ou application fenêtrée) ou en tant que module Apache (mod_neko). Neko peut être comparé à PHP coté serveur et il est 20x plus rapide que ce dernier dans certains cas d’utilisation.

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 1

B. Installation haXe est un langage composé de librairies (classes sources) et d’un compilateur en ligne de commande. Dans la version « automatic installer », haXe est directement fourni avec la nekoVM qui permet d’exécuter du bytecode neko. Pour télécharger haXe, rendez-vous sur http://haxe.org/download Il existe la version « automatic installer » correspondant à chacun des 3 systèmes d’exploitations sur lesquels est disponible haXe. Il suffit de lancer l’installeur et il s’occupe de toute l’installation ainsi que de mettre les variables d’environnement nécessaires. Sinon, il faut télécharger et dezipper haXe et Neko dans des dossiers et mettre les variables d’environnement du système à jour (classpath de haxe, neko…) afin qu’il soit accessible de partout. haxelib et plug-in FlashDevelop

« haxelib » est une commande proche du « apt-get » sous et qui s’occupe d’organiser les librairies de projets communautaires de haXe. (Pour plus d’informations http://lib.haxe.org/ ) Parmi ces « libs », il y a « haxefd » qui est un plug-in de projet haXe pour l’éditeur FlashDevelop sous Windows. FlashDevelop est disponible sur http://www.flashdevelop.org/downloads/releases/FlashDevelop-2.0.2-Final.exe

Pour installer le plug-in pour FlashDevelop il suffit d’écrire en ligne de commande : haxelib install haxefd

Une fois l’installation terminée, on exécute la librairie comme suit : haxelib run haxefd

Nous pouvons désormais utiliser FlashDevelop avec un panneau d’affichage spécifique aux projets haXe et bénéficier de l’auto-completion.

C. Compilateur en ligne de commande compatible Windows / Mac OS / Linux

Le compilateur haXe est disponible sur chacun des grands systèmes d’exploitation et avec les mêmes options. Pour un projet haXe, il suffit de taper en ligne de commande : haxe option[, option, …] exemple : haxe –main Main –swf index.swf –swf-header 640 :480 :24 :FFFFFF

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 2

Ici on spécifie la classe contenant le point d’entrée du programme, on demande une compilation pour le Flash Player et on définit l’entête du SWF (la taille, la cadence et la couleur de fond)

1. Liste des paramètres Version haXe 1.18

• -js : compile pour JavaScript • -swf : compile pour le Flash Player • -neko : compile pour la machine virtuelle NekoVM

Paramètres communs à toutes les plateformes

• -cp : ajoute le chemin de classes à utiliser dans les sources • -lib utilise une bibliothèque de classes « haxelib » • -main : classe du point d’entrée du programme • -D : définie une constante de compilation o -D no-swf-compress : option spéciale pour ne pas compresser le fichier o -D network-sandbox : option spéciale pour rendre le fichier pour l’utilisation en communication sur le réseau • -resource : ajoute un fichier ressource • -exclude : ne génère pas de code pour les classes listés dans ce fichier • -v : active le mode verbose (plus d’infos à la compilation) • -xml : génère un XML de description des types • -debug : ajoute de informations de debug dans le code • -prompt : demande confirmation à chaque erreur • -cmd : exécute la commande lorsque la compilation a réussi • --override : s’assure de la présence du « override » dans les déclarations des méthodes • --next : sépare plusieurs compilations à la volée • --display : affiche un XML contenant l’auto-completion • --times : mesure le temps de compilation • --no-traces : omet le code « trace » • --no-output : compile mais ne génère aucun fichier • --altfmt : utilise un format d’erreurs alternatif • --auto-xml : créé un XML de description des types automatiquement pour chaque plateforme utilisée • --no-inline : refuse les méthodes et variables « inline » • -help Affiche la liste des options • --help Affiche la liste des options

Paramètres pour la compilation des SWF

• -swf-version : attribue ou change la version du SWF (6,7,8,9) • -swf-header : attribue ou change l’entête du SWF (largeur:hauteur:ips:couleur)

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 3

• -swf-lib : ajoute un SWF en tant que bibliothèque multimédia • -as3 : génère le code AS3 correspondant dans le dossier • --flash-strict : utilise une API Flash plus stricte • --flash-use-stage : affiche les objets présents sur la scène principale du SWF, bibliothèque multimedia • --gen-hx-classes : génère les classes externes haXe depuis un fichier SWF version 9

Paramètres pour Neko

• -x raccourcis pour compiler et exécuter un fichier neko • --neko-source : conserve le code source neko généré

2. Le fichier « hxml »

Pour compiler un projet haXe, on peut également utiliser un fichier hxml. Le fichier hxml est un fichier texte, de « build », contenant les options de(s) compilation(s) (une par ligne). Si l’installation de haXe s’est bien déroulée, ce fichier est automatiquement associé à haXe. Sinon, il suffit de taper : cheminDeHaXe/haxe monFichierHxml exemple : # commentaire # compilation pour le Flash Player -swf index.swf -main Main -debug -D network-sandbox # compilation pour la nekoVM --next -main Main -neko index.n

Ici on compile une première fois un SWF, avec les informations debug supplémentaires et la possibilité de lire des donnés sur le réseau suivi d’un fichier pour la nekoVM.

D. Référence du langage : 1. Les types de base

La syntaxe est proche de celle de JavaScript / ActionScript.

Un fichier source est composé d’un nom de paquet (déclaration package ) suivi de déclarations import et type . Les noms de paquet sont composés de plusieurs

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 4

identifiants commençant par une minuscule, alors que les types commencent toujours par une majuscule.

Il y a de nombreux types de base. Les deux plus importants sont les classes et les énumérations ( enums ). Voici quelques-uns des types de base comme déclarés dans la librairie standard:

enum Void { }

class Float { }

class Int extends Float { }

enum Bool { true; false; }

enum Dynamic { }

Étudions chacun de ces types un par un:

• Void est déclaré comme un enum . Une énumération liste un nombre de constructeurs valides. Une énumération vide comme Void n’a donc aucune instance. Cependant, cela reste un type valide qui peut être utilisé. • Float est une classe définissant un nombre à virgule flottante. Elle n’a aucune méthode, elle peut donc être grandement optimisée sur certaines plate- formes. • Int est un entier. Il ne dispose pas non plus de méthode, mais hérite de Float , ce qui signifie que partout où un Float est requis, vous pouvez utiliser un Int (le contraire n’étant pas vrai). • Bool est une énumération, comme Void , mais il dispose de deux instances, respectivement true et false . Comme vous pouvez le voir, même les types standards peuvent être facilement définis ou surchargés en utilisant le système de type haXe. Cela signifie que vous pouvez, de même, définir vos propres types. • Dynamic est une énumération avec un paramètre de type . Nous expliquerons comme utiliser les paramètres de type dans la suite de ce document.

Voyons maintenant comment vous pouvez utiliser les classes en pratique.

2. Les classes

Nous allons brièvement introduire la structure des classes, avec laquelle vous êtes peut-être familier si vous avez déjà fait de la programmation orientée objet:

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 5

package my.pack; /* ceci définit la classe my.pack.MyClass */ class MyClass { // .... }

Une classe peut avoir plusieurs variables ou méthodes .

package my.pack;

class MyClass {

var id : Int;

static var name : String = "MyString";

function foo() : Void { }

static function bar( s : String, v : Bool ) : Void { } }

Les variables et méthodes peuvent avoir les identifiants suivants (à placer avant leur définition) :

• static : le champ appartient à la classe et non à ses instances . Les propriétés statiques peuvent être utilisées directement dans la classe elle-même. A l’extérieur de la classe, il faut y accéder par le nom de classe (pour exemple : my.pack.MyClass.name ).

• public : le champ est accessible depuis toute autre classe. Par défaut, tous les champs sont privés .

• private : l’accès au champ est restreint à la classe et à toutes les classes qui l‘étendent . Il est conseillé de s’assurer que l’état interne (des variables ou fonctions essentielles mais dont l’utilité n’est qu’interne) de la classe ne soit pas accessible hors de cette dernière.

Toutes les variables de classe doivent être déclarées avec un type (vous pouvez utiliser le type Dynamic si vous ne savez pas quel type doit être utilisé). Les types des arguments de fonction et des valeurs de retour sont optionnels mais sont strictement vérifiés, comme cela sera précisé par la suite dans la partie sur l’ inférence de type ./.

Les variables statiques peuvent avoir une valeur initiale mais cela n’est pas obligatoire.

Constructeur

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 6

Une classe ne peut avoir qu’un seul constructeur : la fonction non statique nommée new .

class Point { public var x : Int; public var y : Int;

public function new() { this.x = 0; this.y = 0; }

}

Nous allons maintenant introduire les expressions qui peuvent être utilisées pour implémenter les fonctions ou initialiser les variables statiques.

3. Les expressions

Dans haXe , toutes les expressions ont le même niveau. Ce qui signifie que vous pouvez les imbriquer les unes dans les autres sans aucun problème. Pour exemple : foo(if (x == 3) 5 else 8

Comme cet exemple le montre, cela signifie également que chaque expression retourne une valeur d’un type donné.

Les constantes

Les valeurs constantes suivantes peuvent être utilisées :

0; // Int -134; // Int 0xFF00; // Int

123.0; // Float .14179; // Float 13e50; // Float -1e-99; // Float

"hello"; // String "hello \"world\" !"; // String 'hello "world" !'; // String true; // Bool false; // Bool null; // Unknown<0>

~/[a-z]+/i; // EReg : expression régulière

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 7

Vous remarquerez que null est une valeur spéciale qui peut être utilisée pour tout type mais qui a un comportement différent de celui de Dynamic . Cela sera expliqué en détails lorsque nous introduirons l’ inférence de type .

Opérations

Les opérations usuelles suivantes peuvent être utilisées, suivant cette ordre de priorité :

• v = e : assigner une valeur à une expression, retourne e. • += -= *= /= %= &= |= ^= <<= >>= >>>= : assigner après avoir exécuté l’opération correspondante. • e1 || e2 : si e1 vaut true alors true sinon évaluer e2 . e1 et e2 doivent être de type Bool . • e1 && e2 : si e1 vaut false alors false sinon évaluer e2 . e1 et e2 doivent être de type Bool . • e1...e2 : construire un itérateur d’entiers (voir la suite à propos des Itérateurs). • == != > < >= <= === !== : exécuter des comparaisons normales ou physiques entre deux expressions de même type. Retourne un type Bool . • | & ^ : exécute une opération au niveau des bits entre deux expressions de type Int . Retourne un type Int . • << >> >>> : exécuter un décalage de bits entre deux expressions de type Int . Retourne un type Int . • e1 + e2 : exécute une addition. Si les deux expressions sont un entier, retourne un entier. Sinon, si les deux expressions sont de type Int ou Float alors cela retourne un Float . En dernier cas, cela retourne une expression de type String . • e1 - e2 : exécute une soustraction entre deux expressions de type Int ou Float . Retourne un type Int si les deux sont des types Int , un type Float si les expressions sont de type Float et Int . • e1 % e2 : modulo entre deux nombres (reste de division euclidienne), même type de retour qu’une soustraction. • e1 * e2 : multiplie deux nombre, même type de retour qu’une soustraction. • e1 / e2 : divise deux nombres, retourne une expression de type Float .

Opérations unaires

Les opérations unaires suivantes sont disponibles:

• ! : non booléen. Inverse la valeur d’une expression de type Bool . • - : nombre négatif, change le signe d’une expression de type Int ou Float . • ++ et -- peuvent être utilisés avant ou après une expression. Quand ils sont placés avant, ils incrémentent d’abord la variable correspondante puis retournent cette nouvelle valeur. S’ils sont placés après, ils incrémentent la variable mais retournent la valeur qu’elle avait avant. Cet opérateur peut uniquement être employé avec des valeurs de type Int ou Float . • ~ : complémente une valeur de type Int à 1.

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 8

Les parenthèses

Les exepressions peuvent être délimitées par des parenthéses pour donner une priorité spécifique à l’exécution. Le type de ( e ) est le même que celui de e et tous deux retournent la même valeur.

Les blocs

Les blocs peuvent exécuter plusieurs expressions. La syntaxe d’un bloc est la suivante :

{ e1; e2; // ... eX; }

Un bloc retourne le type et la valeur de la dernière expression de celui-ci. Pour exemple:

{ f(); x = 124; true; }

Ce bloc est de type Bool et retournera true .

Cependant, le bloc vide { } retournera Void .

Les variables locales

Les variables locales peuvent être déclaré dans des bloc en utilisant var, en suivant les exemples :

{ var x; var y = 3; var z : String; var w : String = ""; var a, b : Bool, : Int = 0; }

Une variable peut être déclarée avec un type facultatif et une valeur initiale facultative. Si aucune valeur n’est indiquée alors la variable est null par défaut. Si aucun type n’est donné, alors le type variable est Unknown mais sera toujours strictement typpé. Ceci sera expliqué dans les détails lors de la présentation du typpage par inférence .

Plusieurs variables locales peuvent être déclarées dans la même expression var .

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 9

Des variables locales sont seulement accessibles jusqu’à la fermeture du bloc dans lequel elles sont déclaré. Elles ne sont plus disponibles après.

Identificateurs

Quand un identificateur de variable est trouvé, il est résolu en utilisant l’ordre suivant :

enum Axis { x; y; z; }

class C { static var x : Int; var x : Int;

function new() { // x est la variable membre this.x { var x : String; // x est une variable locale } }

function f(x : String) { // x est un paramètre de la fonction }

static function f() { // x est une variable de la fonction statique } }

class D { function new() { // x est équivalent à la valeur de x de Axis } }

Les types des identificateurs sont résolus en fonction des paquets importés, comme nous le verrons plus tard.

Accès à un champ

L’accès à un objet est fait de manière traditionnelle par l’accès “par point” :

o.field

Appels

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 10

Vous pouvez appeler des fonctions en utilisant des parenthèses et des virgules respectivement dans l’ordre pour définir les arguments. Vous pouvez appeler les méthodes en utilisant l’accès “par point” sur les objets :

f(1,2,3); object.method(1,2,3);

Le mot-clé « new »

Le mot-clé new est utilisé pour créer une instance de classe. Il nécessite le nom d’une classe et peut prendre des paramètres :

a = new Array(); s = new String("hello");

Tableaux

Vous pouvez créer des tableaux directement depuis une liste de valeur en utilisant la syntaxe suivante :

var a : Array = [1,2,3,4];

Veuillez noter qu’il ne peut y avoir qu’un seul type pour un tableau : c’est le type des éléments présents dans le tableau. De cette manière, toutes les opérations faites sur le tableau sont sécurisées. Du coup, tous les éléments dans un tableau donné doivent être du même type.

Vous pouvez lire et écrire dans un tableau en utilisant la méthode traditionnelle par crochets :

first = a[0]; a[1] = value;

L’index du tableau doit être du type Int (entier).

Mot-clé « if »

Voici quelques exemples d’utilisation de if :

if (life == 0) destroy(); if (flag) 1 else 2;

Voici la syntaxe générique des expressions if :

if( expr-cond ) expr-1 [else expr-2]

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 11

D’abord, expr-cond est exécuté. Le résultat peut être de type Bool (booléen). Si celui-ci est true alors expr-1 est exécuté, sinon, si expr-2 est précisé, alors cette expression est exécutée.

S’il n’y a pas de mot-clé else , l’expression a le type Void . Dans le cas contraire, expr-1 et expr-2 doivent être du même type et seront le type de l’expression if :

var x : Void = if( flag ) destroy(); var y : Int = if( flag ) 1 else 2;

Dans haXe , les conditions if sont actuellement similaires à la syntaxe ternaire de C : a ? b : c

Si le bloc if n’est pas supposé retourner une quelconque valeur, alors les deux expressions expr-1 et expr-2 peuvent avoir des types différents et le bloc if sera de type Void .

Les Boucles « while »

Il est possible de faire des boucles avec soit une condition d’entrée soit une condition de sortie.

while( expr-cond ) expr-loop; do expr-loop while( expr-cond );

Exemple de boucle avec une condition d’entrée :

var i = 0; while( i < 10 ) { // ... i++; }

Autre exemple avec une condition de sortie :

var i = 0; do { // ... i++; } while( i < 10 );

Comme avec if , expr-cond doit être, dans les boucles while , du type Bool (booléen).

Voici un autre exemple classique d’une boucle de décompte de 10 à 1 :

var i = 10; while( i > 0 ) { ...... i--; }

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 12

Les Boucles « for »

Les boucles for sont ici un peu différentes de leur homologues en C. Elles sont utilisées pour les itérations (voir plus bas). Voici un exemple de boucle utilisant for :

for( i in 0...a.length ) { foo(a[i]); }

Mot-clé « return »

Le mot-clé return est utilisé pour sortir prématurément d’une fonction et/ou pour que la fonction renvoie une valeur :

function odd( x : Int ) : Bool { if( x % 2 != 0 ) return true; return false; } return peut être utilisé sans argument si la fonction ne requiert pas valeur de renvoi.

function foo() : Void { // ... if( abort ) return; // .... }

Mots-clé « break » et « continue » break permet de casser une boucle for ou while avant sa fin. Le mot-clé continue permet, quant à lui, de passer à l’itération suivante en omettant les instructions qui suivent.

Démonstration :

var i = 0; while( i < 10 ) { if( i == 7 ) continue; // le cas où i=7 n'est pas traité // ... if( flag ) break; // la boucle est cassée }

Les exceptions

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 13

Les exceptions peuvent être vues comme un moyen de faire des sauts externes. Il est possible de lancer une exception - throw - et de l’attraper - catch - depuis l’importe quel appel de fonction :

function foo() { // ... throw new Error("invalid foo"); }

// ...

try { foo(); } catch( e : Error ) { // traitement de l'exception }

Il est possible de surveiller plusieurs exceptions en implémentant plusieurs catch à la suite du try . Les tests sont effectués dans l’ordre de leur déclaration. Attrapper l’exception Dynamic permet de traiter tous les types d’exceptions en même temps :

try { foo(); } catch( e : String ) { // traite un type d'exception } catch( e : Error ) { // traite un autre type d'exception } catch( e : Dynamic ) { // traite toutes les exceptions }

Les tous les blocs d’instruction try et catch doivent retourner le même type ou alors ne rien retourner dutout (comme pour if ) si ce n’est pas nécessaire.

Mot-clé « switch » switch permet de remplacer un bloc conditionnel du type if...else if...else if...else testant les différentes valeurs d’une même vairables :

if( v == 0 ) e1 else if( v == foo(1) ) e2 else if( v == 65 ) e3 else e4;

L’exemple précédent sera traité de préférence comme suit :

switch( v ) { case 0:

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 14

e1; case foo(1): e2; case 65: e3; default: e4; }

Le switch de haxe est différent des autres car chacun des cas est isolé des autres. Seules les instructions du cas vérifié sont exécutés et le bloc switch est terminé. Par conséquent, break est inutile dans le bloc switch et la position du cas par défaut (default ) n’est pas important.

Sur certaines plateformes, les switch peuvent être optimisés et plus rapides pour des veleurs constantes (particulièrement pour des valeurs constantes entières).

Nous verrons plustard que switch peut aussi être utilisé dans les enum avec cependant quelques règles particulières.

Les fonctions locales

Les fonctions locales sont déclarées avec le mot-clé function mais il n’est pas possible de leur attribuer de nom. Elles sont considérées comme des valeurs litérales d’entiers ou de chaines de caractères.

var f = function() { /* ... */ }; f(); // appel de la fonction

Les fonctions locales peuvent accéder à leur arguments, aux variables statiques de la classe courante ainsi qu’aux variables locales qui ont été déclarées avant elles. Exemple :

var x = 10; var ajouterAx = function(n) { x += n; }; ajouterAx(2); ajouterAx(3); // ici x = 15

Cependant, les fonction déclarées dans des méthodes ne peuvent pas avoir accès à this . Pour leur y donner accès, il faut d’abord associer this à une varible locale comme dans l’exemble suivant :

class C {

var x : Int;

function f() { // créera un erreur de compilation var ajouterAx = function(n) { this.x += n }; } Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 15

function f2() {

// compilera var cela = this; var ajouterAx = function(n) { cela.x += n }; } }

Les objets anonymes

Les objets anonymes sont déclarés en utilisant la syntaxe suivante :

var o = { age : 26, name : "Tom" };

Veuillez noter que les objets anonymes sont strictement typés par le système d’inférence des types.

4. Inférence de type

L’inférence de type permet de définir le typage des variables utilisées. Elle a lieu aussi bien lors de la compilation que pendant la rédaction du programme. C’est pour cela qu’elle n’a pas besoin d’être résolue immédiatement. Par exemple, une variable locale peut très bien être déclarée sans type particulier (en réalité elle sera du type unknown ). Son type sera déterminé lors de sa première assignation en concordance avec le contenu qui lui est passé.

Afficher un type

Il est possible d’utiliser n’importe où dans votre programme l’instruction type pour connaître le type d’une expression. L’avantage, c’est qu’à la compilation, type est omit et seul l’expression est évaluée :

var x : Int = type(0);

Le code précédant va afficher Int à la compilation. Le programme compilé sera le même que si type n’avait pas été utilisé. C’est une instruction donnée au compilateur qui n’est pas transcrite dans le programme lui-même. type peut être pratique pour connaitre rapidement un type sans avoir à chercher cette information dans la déclaration d’une classe ou dans une documentation.

Inférence des variables locales

L’inférence de type permet au programme tout entier d’être strictement typé sans avoir à s’en occuper soi-même. Ceci est valable en particulier pour les variables locales qui n’ont pas besoin d’être explicitement typées durant la phase de rédaction.

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 16

Elles le seront automatiquement lors de leur premier appel en écriture ou en lecture. Exemple :

var loc; type(loc); // affiche Unknown<0> loc = "hello"; type(loc); // affiche String

Inférence des fonctions

La déclaration du type des arguments d’une méthode ou d’une fonction n’est pas obligatoire. Leur typage se fera lors de la première utilisation de la fonction. Le type de chaque argument sera fixé en fonction de celui passé la premiere fois (à la manière des variables locales).

Il faut utiliser judicieusement cette particularité puisque le résultat dépend de l’ordre d’exécution du programme. Voici un exemple qui explicite cette notion :

function f( posx ) { // .... } // ...

f(134); f(12.2); // erreur de compilation "Error : Float should be Int"

Le premier appel de la fonction f fixe le type de posx à Int . Le second appel cause une erreur de compilation car f attend maintenant un Int , et non un Float . En inversant les deux appels, l’erreur de compilation est évitée car l’argument posx est typé en Float . L’appel suivant, avec un Int , ne cause aucune erreur puisque Int est un sous ensemble de Float .

function f( posx ) { // .... } // ...

f(12.2); // définie le type de l'argument à Float f(134); // compilation sans problème

Dans cette exemple, les deux appels sont proches l’un de l’autre. Il y est donc facile de déceler et corriger une erreur éventuelle. Mais dans le cas d’un code plus volumineux et plus complexe, il faudra certainement faire preuve de finesse pour trouver de genre d’erreur sementique. Le mieux reste d’expliciter le typage des arguments des fonctions. De cette manière, l’appel à l’origine de l’erreur de compilation est clairement identifiée et affichée en sortie du compilateur.

En comparaison avec le premier exemple :

function f( posx : Int ) {

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 17

// .... } // ...

f(134); f(12.2); // l'erreur de compilation pointera cette ligne

Liberté du programmeur

Utiliser l’inférence de type est un choix. Il suffit simplement de ne pas préciser le type des variables et de laisser au compilateur cette charge. Il est tout aussi possible de définir tous les types pour un contrôle plus étroit du programme. La meilleure approche reste sans doute le compromis entre le typage nécessaire à la bonne compréhension du code et la rapidité de rédaction en omettant les autres.

Dans presque tous cas - le typage dynamic est particulier et sera abordé plus loin - un programme est au final strictement typé. Toute utilisation frauduleuse sera immédiatement détectée à la compilation.

5. Héritage

A la déclaration, il est possible de préciser si une classe étends une autre ou si elle implémente la déclaration d’une ou plusieurs classes et/ou interfaces. Cela signifie que la classe hérite de plusieurs type en même temps et peut être traitée en tant que tels :

class D extends A, implements B, implements C {

}

Toutes les instances de D ont non seulement le type D mais elles peuvent aussi être utilisées partout ou les types A, B et C sont demandés. Ce qui veut dire que les instance de D sont aussi typés A, B et C.

Étendre

Etendre une classe mère par une classe fille permet à cette dernière d’ hériter de toutes les données publiques et privées non statiques de sa classe mère . Il est alors possible de les utiliser dans la classe fille comme si elles y étaient directement déclarées. Il est aussi possible de surcharger certaines méthodes héritées (voire toutes) en les redéclarant avec les mêmes nombres et types d’arguments que dans la classe mère . La classe fille ne peut, par contre, pas hériter des données statiques .

Il est possible d’accéder, grâce au mot-clé super , à la déclaration comme héritée de la classe mère d’une méthode pourtant surchargée :

class B extends A { function foo() : Int { return super.foo() + 1; // appel foo comme définie dans la classe A

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 18

} }

Il est aussi possible d’appeler le constructeur de la classe mère en utilisant super :

class B extends A { function new() { super(36,""); // appelle le contructeur de A. } }

Implémenter

Implémenter une classe et/ou une interface demande de définir toutes les données déclarées et/ou de redéfinir toutes celles héritées. Ceci sans exception.

Les interfaces

Les interfaces sont des prototypes de classes. Elles sont déclarées avec le mot-clé interface . Par défaut, toutes les données d’une interface sont publiques . Il n’est pas possible d’instancier une interface .

interface PointProto { var x : Int; var y : Int;

function length() : Int; function add( p : PointProto ) : Void; } 6. Les paramètres de classe

Une classe peut avoir plusieurs paramètres types qui peuvent être utilisés pour avoir des comportement variés. Par exemple la classe Array a un paramètre type :

class Array {

function new() { // ... }

function get( pos : Int ) : T { // ... }

function set( pos : Int, val : T ) : Void { // ... } }

A l’intérieur de la classe Array , le type T est abstrait et ses données ne sont pas accessibles. A cause de cela, l’instanciation d’un objet de la classe Array doit aussi spécifier son type : Array ou Array par exemple. Cela a le même effet que Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 19

de remplacer tout les typages en T de la déclaration de la classe Array par le type spécifié lors de l’instanciation.

Le paramètre type est très pratique pour un typage strict de conteneurs comme Array , List ou Tree . Grâce à cela, il est possible de définir différents types de contenus sans avoir a réécrire la classe.

Paramètres de contraintes

S’il est vrai qu’il peut être pratique d’utiliser des paramètres abstraits , il est aussi possible de leur spécifier des contraintes pour pouvoir les utiliser dans l’implémentation de la classe. Par exemple :

class EvtQueue { var evt : T; // ... }

Dans le cas précédent, le compilateur sait que la donnée evt , même si elle est de type abstrait, peut avoir les deux types Event et EventDispatcher . Il peut alors accéder aux données correspondantes comme si la classe les implémentait toutes les deux. Par la suite lorsque qu’un objet EvtQueue est instancié, le compilateur vérifie si le paramètre type passé implémente ou étend les deux types Event et EventDispatcher . Quand plusieurs paramètres de contraintes sont définis pour un seul paramètre de classe, comme dans l’exemple précédant, ils doivent être placés entre parenthèses pour lever toute ambiguïté dans les cas ou il y aurait plusieurs paramètres de classe à la suite.

Les paramètres de contraintes sur le type sont un outil puissant qui peut être très utile pour écrire des codes génériques réutilisables dans différentes applications.

7. La puissance des énumérations

Les énumérations sont un type particulier, différent de celui des classes. Elles sont déclarées avec un nombre fini de constructeurs . En voici un petit exemple :

enum Couleur { rouge; vert; bleu; }

class Couleurs { static function toInt( c : Couleur ) : Int { return switch( c ) { case rouge: 0xFF000; case vert: 0x00FF00; case bleu: 0x0000FF; } }

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 20

}

Les énumérations sont utilisées pour définir un groupe de valeurs particulières. Elles donnent l’assurance que seules ces valeurs seront construites.

Les paramètres des constructeurs

L’énumération Couleur de l’exemple précédent comporte trois constructeurs constants . Il est toutefois possible de spécifier des paramètres aux constructeurs :

enum Couleur2 { rouge; vert; bleu; gris( v : Int ); rvb( r : Int, v : Int, b : Int ); }

De cette manière, il peut y avoir une infinité de Couleur2 possibles, mais il n’existe que cinq façons de les créer. Les valeurs suivantes sont toutes des Couleur2 :

rouge; vert; bleu; gris(0); gris(128); rvb( 0x00, 0x12, 0x23 ); rvb( 0xFF, 0xAA, 0xBB );

Il est aussi possible d’utiliser un type récursif. Par exemple, pour ajouter un canal alpha :

enum Couleur3 { rouge; vert; bleu; gris( v : Int ); rvb( r : Int, g : Int, b : Int ); alpha( a : Int, coul : Couleur3 ); }

Les valeurs suivantes sont des Couleur3 valides :

alpha( 127, red ); alpha( 255, rgb(0,0,0) );

Le test « switch » sur une énumération

L’utilisation de switch sur une énumération est particulière. Le compilateur attend que tous les constructeurs de l’enumération soient implémentés dans le bloc switch . Dans le cas contraire - et si le cas default , qui peut remplacer toutes les implémentations Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 21

manquantes, n’est pas présent - une erreur de compilation interviendra. L’exemple suivant fait référence à l’énumération Couleur précédente :

switch( c ) { case rouge 0xFF000; case vert 0x00FF00; }

Cette exemple provoque une erreur de compilation car le constructeur bleu n’est pas implémenté. Pour corriger cette erreur, il faut soit implémenter tous les constructeurs manquants (ici, juste bleu ) ou ajouter le cas default qui traitera de la même manière tous les constructeurs non explicitement implémentés. Grâce à cette syntaxe, l’ajout d’un constructeur dans une énumération sans son traitement provoquera un erreur de compilation, et le compilateur indiquera où le traitement doit être effectué.

Si un constructeur d’une énumération contient des arguments, ils doivent être listés comme des noms de variables dans un cas du switch . De cette manière, toutes les variables seront accessibles localement dans l’expression du cas et correspondront aux types des paramètres du constructeur. L’exemple suivant utilise l’énumération Couleur3 précédente :

class Couleurs { static function toInt( c : Couleur3 ) : Int { return switch( c ) { case rouge: 0xFF000; case vert: 0x00FF00; case bleu: 0x0000FF; case gris(v): (v << 16) | (v << 8) | v; case rvb(r,v,b): (r << 16) | (v << 8) | b; case alpha(a,c): (a << 24) | (toInt(c) & 0xFFFFFF); } } }

La seule façon d’accéder aux paramètres des constructeurs d’une énumération est le switch .

Les paramètres de typage d'une énumération

Tout comme les classes, les énumérations peuvent avoir des paramètres de type. La syntaxe est identique. Voici un petit échantillon d’une liste chaînée paramétrée utilisant une énumération pour stocker les cellules :

enum Cellule { vide; cons( valeur : T, suivant : Cellule ); }

class ListeChainee { var premiere : Cellule;

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 22

public function new() { premiere = vide; }

public function add( valeur : T ) { premiere = cons(valeur,premiere); }

public function length() : Int { return compter_cellules(premiere); }

private function compter_cellules( c : Cellule ) : Int { return switch( c ) { case vide : 0; case cons(val,suiv): 1 + compter_cellules(suiv); } }

}

La combinaison d’une énumération et d’une classe peut être très efficace dans certains cas.

8. Importations et Paquets

Chaque fichier haxe peut contenir plusieurs classes , énumérations et importations . Elles font toutes partie du paquet déclaré au début du fichier. Si aucun paquet n’est spécifié alors le paquet par défaut vide est utilisé. Chaque type déclaré dans le fichier a alors une adresse correspondant au nom du paquet suivi de la dénomination du type .

// fichier mon/paquet/C.hx package mon.paquet;

enum E { }

class C { }

Ce fichier comporte la déclaration de deux type : mon.paquet.E et mon.paquet.C . Il est possible d’avoir plusieurs types déclarés dans un même fichier. Cependant, l’ adresse des types dans toute l’application doivent être uniques et des conflits peuvent apparaître si vous n’utilisez pas les paquets (ce qui ne veut pas dire que vous devez utiliser de noms de paquets à rallonge partout).

Quand vous utilisez des paquets, les fichiers doivent être placés suivant une arborescence correspondant à leur nom de paquet. En général, le nom du fichier est le nom de la classe principale définie à l’intérieur.

L’extension de fichier haxe est .hx .

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 23

Importation

L’importation peut être utilisée pour accéder à tous les types contenus dans le fichier sans avoir à spécifier le nom du paquet.

package mon.paquet2; class C2 extends mon.paquet.C { }

L’exemple suivant équivaut à celui qui suit :

package mon.paquet2; import mon.paquet.C;

class C2 extends C { }

La seule différence c’est qu’il est possible d’utiliser les constructeurs de l’énumération implémentée dans le fichier mon/paquet/C.hx .

Recherche de type

Quand un nom de type est rencontré, la résolution est effectuée dans l’ordre suivant :

• recherche parmi les paramètres types de la classe courante • recherche parmi les types standards • recherche parmi types déclarés dans le fichier courant • recherche parmi types déclarés dans les fichiers importés (si le paquet recherché est vide) • chargement du fichier correspondant et recherche du type à l’intérieur.

Accès au constructeurs des énumérations

Pour pouvoir appeler les constructeurs d’une énumération, le fichier contenant la déclaration de l’énumération doit avant tout être importé. Il faut sinon utiliser la dénomination complète des constructeurs comme si ils étaient des données statiques du type énumération correspondant.

var c : mon.paquet.Couleur = mon.paquet.Couleur.rouge;

Comme exception, dans un switch : si le type de l’énumération est connue au moment de la compilation, alors il est possible d’utiliser les constructeurs directement sans avoir besoin d’importation.

9. Le type « dynamic »

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 24

Lorsqu’une variable à besoin de ne pas être figée dans un type , elle doit alors être associé au type dynamic . Le type dynamic permet de libérer la variable du système de typage.

var x : Dynamic = ""; x = true; x = 1.744; x = new Array();

De plus, une variable dynamique peut avoir un nombre infini de champs , tous héritant du type dynamic . Le type dynamic peut être implémenté simplement à la manière d’un tableau mais en utilisant des parenthèses.

Si cela peut parfois être utile, il faut très attention à ne pas rendre votre programme instable en utilisant trop de variables dynamiques .

NOTE : une variable non typée est en réalité du type unknown (inconnu), et non dynamic . Une variable non typée le sera par l’inférence de type à sa première utilisation. Les variables dynamiques ont bien un type, un type quelconque !

Le paramètre de type du typage dynamique

Comme il a été dit lors du détail des types de la librairie standard, dynamic peut aussi avoir un paramètre de type. En utilisant dynamic le comportement est modifié. dynamic ne peut pas être employé à la place d’un autre type. Cependant, il a une infinité de champs qui sont tous du type String . Cette utilisation est pratique pour construire des tables de hachage où les éléments sont accessible grâce au point :

var x : Dynamic = xml.attributes; x.nom = "Nicolas"; x.age = "26"; // ...

Implémentation dynamique

Toutes les classes peuvent être implémentées dynamiquement avec ou sans paramètre de type. Dans le premier cas, les éléments de la classe sont typé au moment de leur création, sinon les éléments sont eux aussi dynamiques.

class C implements Dynamic { public var nom : String; public var adresse : String; } // ... var c = new C(); var n : String = c.nom; // valide : comme défini dans le classe var a : String = c.adresse; // valide : comme défini dans le classe var i : Int = c.telephone; // valide : le type dynamique à un nombre non fini de champs. var co : String = c.ville // erreur: c.ville est typé en Int à cause de Dynamic

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 25

Le comportement dynamique est hérité par les classes-filles. Quand plusieurs classe implémentent des types dynamic avec des paramètres de type différents dans la hiérarchie des classes, c’est la dernière définition qui est transmise.

Transtypage

Il est possible de transformer une valeur d’un type à un autre grâce au mot-clé cast (mouler).

var a : A = .... var b : B = cast(a,B);

Le code précédent retourne la valeur a avec le typage B si le type A est une implémentation du type B, sinon une exception “Class cast error” est envoyée.

Mot-clé « untyped »

Le mot-clé untyped permet aussi de faire du typage dynamique. Quand une expression est déclarée untyped , cette dernière n’est pas passée à la vérification de type. Il est alors possible d’effectuer tout un tas d’opérations atypiques :

untyped { a["hello"] = 0; }

Il est préférable d’éviter au maximum les expressions dynamiques. Elles ne sont à utiliser qu’avec beaucoup de maîtrise et seulement si elles sont strictement nécessaires.

Le transtypage libre untyped peut être très pratique mais il laisse aussi passer toute sorte de syntaxes invalides, tant qu’elles sont placées à la droite du mot-clé. Il existe une autre méthode qui consiste à faire un transtypage libre , similaire au transtypage classique mais où aucun type n’est spécifié. cast ne sera alors soumis à aucune vérification mais il fera ‘perdre’ le type.

var y : B = cast 0; cast revient d’une certaine manière à sauvegarder une valeur dans une variable dynamique temporaire.

var tmp : Dynamic = 0; var y : B = tmp; 10. Les autres Types

À ce niveau du document, les types suivant ont été présentés :

• les instances de classe • les instances d’énumération Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 26

• les valeurs dynamiques

Cependant, il existe bien d’autres types importants.

Anonyme

Le type anonymous correspond à un objet déclaré de façon anonyme . C’est aussi le type d’un identificateur de classe (contenant toutes les valeurs statiques) ou celui d’une énumération (contenant tous les constructeurs). Voici un exemple explicatif :

enum Etat { on; off; disable; }

class C { static var x : Int; static var y : String;

function f() { // affiche : { id : Int, ville : String } type({ id : 125, ville : "Kyoto" }); // { id : Int, ville : String } <=> anonyme }

function g() { // affiche : { on : State, off : State, disable : State } type(State); // State <=> anonyme }

function h() { // affiche : { x : Int, y : String } type(C); // C <=> anonyme } }

Les types Anonymous sont structurels. Il est donc possible d’avoir plus de champs dans la valeur que dans le type :

var p : { x : Int, y : Int } = { x : 0, y : 33, z : -45 };

Typedef

Il est possible de déclarer des définitions de types qui sont une sorte de raccourcis permettant de donner un nom a un type anonyme ou d’éviter de ré-écrire des noms de type à rallonge : typedef Perso = { var age : Int; var nom : String; } // ....

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 27

var x : Perso = { age : 26, nom : "Tom" }; // PointCube est un tableau contenant un système de coordonnées 3-D typedef PointCube = Array>>

Les typedef ne sont pas des classes. Ce sont de simples commodités de rédaction.

Function

Pour réutiliser la définition d’une fonction à la manière d’une variable, il faut lister les types de tous les arguments et finir par le type de retour en les séparant par des flèches. Par exemple Int -> Void est le type function d’une fonction attendant un Int en argument et retourne un Void . Couleur -> Couleur -> Int défini une fonction qui attend deux arguments Couleur et retourne un Int .

class C { function f(x : String) : Int { // ... }

function g() { type(f); // affiche String -> Int var ftype : String -> String = f; // erreur : f est incompatible String -> Int } }

Unknown

Quand un type n’est pas déclaré, il est défini en type Unknown . Lorsqu’il est utilisé pour la première fois avec un type différent, unknown est transtypé dans le type en question (voir la section inference de type ). Un numéro d’ identifiant est affiché avec le type Unknown pour différencier tous les types Unknown .

function f() { var x; type(x); // affiche Unknown<0> x = 0; type(x); // affiche Int }

La diversité des types exprimables avec haXe permet de créer des modèles de programmation plus puissants en fournissant de haut niveaux d’abstractions qui évite les entrelacements complexes de classes pour être employés.

Extensions

Les extensions peuvent être employées pour étendre un typedef représentant un type anonyme , ou, pour prolonger une classe à la volée. Voici un exemple d’une extension d’un typedef anonyme :

typedef Polaire = {

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 28

var r : Int; var theta : Int; }

// définir un point 'p' en coordonnées polaire // et rajouter une coordonnée z pour obtenir des coordonnées cylindriques var p : {> Point, z : Int } p = { r : 0, theta : 0, z : 0 }; // compile p = { r : 0, theta : 0 }; // ne compile pas

Il faut utiliser un transtypage libre lors de l’assignement pour faire correspondre les données. Cependant, il faut faire très attention car il n’y a pas de garde fou dans ce cas :

var p : {> flash.MovieClip, tf : flash.TextField }; p = flash.Lib._root; // cause une erreur de compilaton p = cast flash.Lib._root; // compile mais sans vérifications

Il est aussi possible d’utiliser les extensions pour créer des typedef en cascade :

typedef Point = { var x : Int; var y : Int; } typedef Point3D = {> Point, var z : Int; }

Dans le dernier cas, tous les Point3D sont aussi bien des Point .

11. Les Itérations

Un itérateur est un objet qui a le type Iterator (T est le type de l’itéré) :

typedef Iterator = { function hasNext() : Bool; function next() : T; }

La syntaxe for permet de mettre en oeuvre l’ itération . L’itération la plus simple est le IntIter dont la construction est facilitée grâce à l’opérateur ... (points de suspension). L’exemple suivant liste tous les entiers entre 0 et 9 :

for( i in 0...10 ) { // ... }

Un autre exemple usuel d’itération :

for( i in 0...arr.length ) { foo(arr[i]);

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 29

}

La variable i est automatiquement déclarée dans for et n’est accessible qu’à l’intérieure de la boucle. Il n’est donc pas nécessaire de déclarer i auparavant.

Implémenter une itération

Il est tout à fait possible de customiser les itérateurs en implémentant dans votre classe les méthodes hasNext et next pour les utiliser dans un typedef Iterator . L’exemple suivant montre comment est implémenté dans la librairie standard la classe IntIter :

class IntIter { var min : Int; var max : Int; public function new( min : Int, max : Int ) { this.min = min; this.max = max; } public function hasNext() { return( min < max ); } public function next() { return min++; } }

Une fois la classe implémentée, il suffit simplement de l’utiliser dans une boucle for...in comme suit :

var iter = new IntIter(0,10); for( i in iter ) { // ... }

La variable de bouclage est automatiquement déclarée et sont type est adapté en fonction de l’itération. Elle n’est accessible uniquement que dans la boucle.

Les objets itérables

Un objet est itérable s’il contient une méthode iterator() n’acceptent aucun argument et retournant un itérateur. Elle n’a pas besoin d’implémenter un type en particulier. L’objet itérable est alors utilisable directement dans l’expression for...in sans avoir à appeler la méthode iterator() :

var a : Array = ["hello","world","J'","adore","haXe","!"]; for( txt in a ) { tf.text += txt + " "; }

La classe Array de la librairie standard contient une telle méthode. Le code précédent construit une chaîne de caractères en listant tous les éléments d’un Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 30

tableau grâce à une itération. Haxe tente automatiquement de lancer la méthode a.iterator() . Il est donc tout à fait possible de créer une méthode portant un autre nom et respectant les mêmes règles que celles de iterator() . Il faudra dans ce cas l’appeler explicitement dans la boucle for .

12. La Compilation conditionnelle

Il est parfois préférable d’avoir une unique librairie utilisant une API spécifique à la plateforme de compilation. Il peut également être plus pratique de n’avoir qu’un drapeau à spécifier pour activer certaines optimisations. Pour ce faire, il faut utiliser des conditions (AKA preprocessor macros) qui indiqueront au compilateur comment procéder pour produire le programme :

L’exemple suivant permet de spécifier la plateforme de destination :

#if flash8 // code spécifique à flashplayer 8. #else flash // code spécifique à la plateforme flash mais commune à toutes les version. #else js // code spécifique à une application javascript #else neko // code spécifique à la plateforme neko #else error // message d'erreur affiché si une autre plateforme est requise #end

Un autre exemple contenant du code spécifique au débogage. Le mode débogage est activable avec le drapeau debog est utiliser à la compilation :

#if debog trace("informations de débogage..."); #end

L’instruction #else doit aussi être suivie d’un nom de variable pour ne pas avoir d’erreur de syntaxe comme dans l’exemple suivant :

#if NomDeVariable // code conséquent... #else var i = 15; // autre code alors... #end

Le compilateur interprétera cette exemple comme #else var et cherchera la valeur de la variable var . Il lancera alors une erreur de syntaxe.

Il est possible de définir des variables grâce aux options en ligne de commande de haxe .

13. Les Propriétés Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 31

Les propriétés sont une manière spécifique de déclarer des champs de classe. Elles peuvent être utiliser pour implémenter différents types de dispositifs tels que des champs de lecture seule/écriture seule ou des champs d’accès par des méthodes d’assignation et/ou de d’interrogation.

Exemple d’implémentation par avec des propriétés :

class C { public var x(interroger,assigner) : Int; }

Les valeurs pour interroger et/ou assigner peuvent être comme suit :

• un nom de méthode permettant d’assigner ou d’interroger • null pour restrindre l’accès • default pour définir un accès classique au champ • dynamic pour permettre l’accès à travers une méthode créée en cours d’exécution

Exemple

Voici un exemple complet : class C { public var ro(default,null) : Int; public var wo(null,default) : Int; public var x(interrogerX,assignerX) : Int;

private var _x : Int;

private function interrogerX() { return _x; }

private function assignerX( v : Int ) { if( v >= 0 ) _x = v; return _x; }

}

En utilisant l’implémentation avec des propriétés, la classe précédente contient trois champs publics :

• de l’extérieur de la classe, le champ ro est en lecture seule • de l’extérieur de la classe, le champ wo est en écriture seule • le champ x est accessible à travers une paire de fonction d’assignement et d’interrogation

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 32

Le champ x est un champ virtuel . Il permet de faciliter l’utilisation de la classe et d’abstraire les traitements effectués en amont et en aval de l’utilisation du champ. En théorie, les fonctions f1 et f2 sont identiques. Cependant, les fonctions assignerX et interrogerX sont inaccessibles car privées . En pratique seule f1 est valable.

var c : C; function f1() { c.x *= 2; } function f2() { c.assignerX(c.interrogerX()*2); }

Remarques importantes

Il est important de garder à l’esprit que ces dispositifs ne fonctionnent que si le type de la classe est connu. Il n’existe pas des méthodes de manipulation des propriétés pendant l’exécution . Donc, l’exemple suivant tracera toujours null tant que la méthode interrogerX ne sera jamais appelée :

var c : Dynamic = new C(); trace(c.x);

Ceci est aussi valable pour les propriétés en lecture et écriture seule. Elles peuvent toujours être modifiée si le type de la classe est unknown .

Il faut aussi noter qu’il est nécessaire que la méthode d’assignement retourne une valeur, dans le cas contraire, le compilateur s’en plaindra.

L'accès dynamique

L’ accès dynamique peut être utilisé pour renseigner les méthodes d’assignement et d’interrogation au cours de l’exécution. C’est un dispositif spécifique qui doit être employé avec soin :

class C { public var age(dynamic,dynamic) : Int; public function new() { } } class Test { static function main() { var c = new C(); var mon_age = 26; Reflect.setField(c,"interroger_age",function() { return mon_age; }); Reflect.setField(c,"assigner_age",function(a) { mon_age = a; return mon_age; }); trace(c.age); // 26 c.age++; // exécute en réalité c.interroger_age(c.interroger_age()+1) trace(c.age); // 27 } }

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 33

14. Les arguments optionnels

Certains arguments de fonction peuvent être définis comme optionnels en utilisant un point d’interrogation « ? » devant son nom :

class Test { static function foo( x : Int, ?y : Int ) { trace(x+","+y); } static function main() { foo(1,2); // trace 1,2 foo(3); // trace 3,null } }

S’il est préférable de positionner les arguments optionnels en dernières positions, ce n’est pas obligatoire. Il est tout à fait possible d’en mettre au début ou même au milieu.

En outre, les arguments optionnels sont indépendants dans haxe . Cela signifie qu’un argument optionnel peut très bien être renseigné sans que l’autre le soit :

function foo( ?x : A, ?y : B ) { }

foo(new A()); // identique à foo(new A(),null); foo(new B()); // identique à foo(null, new B()); foo(); // identique à foo(null,null); foo(new C()); // erreur à la compilation foo(new B(),new A()); // erreur : l'ordre doit par contre être respecté

Cependant les arguments optionnels peuvent sembler faire partie des options avancées d’ haxe .

15. Inline

Depuis haxe 1.17, un nouveau mot clé est apparu : « inline ». Il peut être utilisé pour des variables « static » ou n’importe quelle methode.

Variable statique

Partout ou la variable est utilisée, elle va être remplacée par sa valeur : class Test { static inline var WIDTH = 500; static function main() { trace(WIDTH); } }

Le « inline » ajoute quelques restrictions : Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 34

• La variable doit être initialisée à la déclaration. • La valeur de la variable ne peut être modifiée.

On peut ainsi utilisé autant de variables que l’on veut sans ralentir l’exécution du code, puisque la valeur de la variable est directement injectée dans le code généré

Méthode

L’appel à une méthode peut être coûteux en ressources processeur à l’exécution, c’est pourquoi il est parfois utile, pour de petites fonctions appelées fréquemment de les « inliner » : class Point { public var x : Float; public var y : Float; public function new(x,y) { this.x = x; this.y = y; } public inline function add(x2,y2) { return new Point(x+x2,y+y2); } } class Main { static function main() { var p = new Point(1,2); var p2 = p.add(2,3); // is the same as writing : var p2 = new Point(p.x+2,p.y+3); } }

Les restrictions suivantes surviennent lors de l’utilisation du mot clé « inline » sur des fonctions : • La fonction ne peut être redéfinie au run-time • La fonction ne peut être « overridée » dans une classe fille. • Une fonction contenant un accès à une fonction de sa super classe ou une déclaration d’une autre fonction ne peut être « inline »

L’utilisation du mot clé « inline » augmente la taille du fichier mais également les performances du programme. 16. Les expressions régulières haxe autorise les expressions régulières. Elles peuvent être littérales ou créées avec des objets. Une expression régulière littérale débute par ~/ et se termine par / suivie par des options comme g ou i :

var r = ~/([0-9]+)/g; var str = "coucou 48 cool 87!"; trace( r.match(str) ); // true trace( r.matched(1) ); // 48

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 35

trace( r. split(str ) ); // ["hello "," cool ","!"]

Support de cours « haxe et les outils open source de développement web » ère 1 partie : haXe langage universel Web 2007 / 2008 - M.Romecki 36