Haxe

Last updated
Haxe
Haxe logo.svg
Paradigm Multi-paradigm: object-oriented, functional, generic
Family ECMAScript
Developer Haxe Foundation
First appearedNovember 14, 2005;19 years ago (2005-11-14)
Stable release
4.3.6 [1]   OOjs UI icon edit-ltr-progressive.svg / 7 August 2024;3 months ago (7 August 2024)
Typing discipline static, dynamic via annotations, nominal
Scope lexical
Implementation language OCaml
Platform IA-32, x86-64, AArch64, armel, armhf, MIPS, MIPS64el, MIPSel, ppc64el, RISC-V, s390x
OS Android, iOS; Linux, macOS, Windows
License GPL 2.0, library: MIT
Filename extensions .hx, .hxml
Website haxe.org OOjs UI icon edit-ltr-progressive.svg
Influenced by
ECMAScript, JavaScript, ActionScript, OCaml, Java, C++, PHP, C#, Python, Lua, NekoVM

Haxe is a high-level cross-platform programming language and compiler that can produce applications and source code for many different computing platforms from one code-base. It is free and open-source software, released under an MIT License. [2] The compiler, written in OCaml, is released under the GNU General Public License (GPL) version 2.

Contents

Haxe includes a set of features and a standard library [3] supported across all platforms, including numeric data types, strings, arrays, maps, binary, reflective programming, maths, Hypertext Transfer Protocol (HTTP), file system and common file formats. Haxe also includes platform-specific application programming interfaces (APIs) for each compiler target. [4] Kha, OpenFL , and Heaps.io are popular Haxe frameworks that enable creating multi-platform content from one codebase. [5]

Haxe originated with the idea of supporting client-side and server-side programming in one language, and simplifying the communication logic between them. [6] Code written in Haxe can be compiled into JavaScript, C++, Java, JVM, PHP, C#, Python, Lua [7] and Node.js. [8] Haxe can also directly compile SWF, HashLink, and NekoVM bytecode and also runs in interpreted mode. [9]

Haxe supports externs (definition files) that can contain data type information of extant libraries to describe target-specific interaction in a type-safe manner, like C++ header files can describe the structure of existing object files. This enables to use the values defined in the files as if they were statically typed Haxe entities. Beside externs, other solutions exist to access each platform's native capabilities.

Many popular IDEs and source code editors have support available for Haxe development. [10] No particular development environment or tool set is officially recommended by the Haxe Foundation, although VS Code, IntelliJ IDEA and HaxeDevelop have the most support for Haxe development. The core functionalities of syntax highlighting, code completion, refactoring, debugging, etc. are available to various degrees.

History

Development of Haxe began in October 2005. The first alpha version was released on November 14, 2005. [11] [12] Haxe 1.0 was released in April 2006, with support for Adobe Flash, JavaScript, and NekoVM programs. Support for PHP was added in 2008, and C++ was added in 2009. More platforms such as C# and Java were added with a compiler overhaul in 2012.

Haxe was developed by Nicolas Cannasse and other contributors, and was originally named haXe [13] because it was short, simple, and "has an X inside", which the author asserts humorously is needed to make any new technology a success. [14]

Haxe is the successor to the open-source ActionScript 2 compiler MTASC, [15] also built by Nicolas Cannasse and is released under the GNU General Public License version 2 or later.

Compiler

The Haxe language can compile into bytecode that can be executed directly by the virtual machines it targets. It can compile to source code in C++, JavaScript, PHP, C#, Java, Python, and Lua. Haxe also has an interpreter called eval. [16] This same interpreter is also used compile-time to run macros, which allow modification of the abstract syntax tree (AST).

This strategy of compiling to multiple source code languages is inspired by the write once, run anywhere paradigm. It also allows the programmer to choose the best platform for the job. Typical Haxe programs run identically on all platforms, but developers can specify platform-specific code and use conditional compilation to prevent it from compiling on other platforms.

The Haxe compiler is an optimizing compiler, and uses field and function inlining, tail recursion elimination, constant folding, loop unrolling and dead code elimination (DCE) to optimize the run-time performance of compiled programs. [17] The Haxe compiler offers opt-in null-safety, it checks compile-time for nullable values.

Targets

In Haxe, supported platforms are known as "targets", which consist of the following modules:

The following table documents platform and language support in Haxe. The Haxe language allows developers to gain access to many platform features, but Haxe is not a full featured engine, they might need frameworks that enable create content for certain platforms.

Haxe compiler targets [9]
NameTierOutputPlatformUseSince Haxe version
Neko3byte code NekoVM Server, desktop, CLI2005 (alpha)
Flash/SWF3byte code Flash: AVM2, Flash Player 9+, AIR Desktop, browser, server2005 (alpha)
JavaScript1source JavaScript: HTML5, NodeJS, PhoneGap Server, desktop, browser, mobile2006 (beta)
ActionScriptsource ActionScript 3: AIR, Flex, Royale Server, desktop, browser, mobile2007 (1.12), removed since 2019 (4.0)
PHP1source PHP: Zend Engine Server2008 (2.0) PHP 5; PHP 7 since 2019 (4.0)
C++2source C++: Windows, Linux, macOS, Android, iOS, Palm, WebOS Server, desktop, Mobile, CLI, game consoles2009 (2.04); cppia added in 2014 (3.2)
C#3source C#: .NET framework, .NET Core, Mono Server, desktop, mobile2012 (2.10)
Java3source Java: Java OpenJDK Server, desktop2012 (2.10)
Python3source Python CLI, web, desktop2014 (3.2)
Lua2source Lua CLI, web, desktop, mobile2016 (3.3)
HashLink1byte codeHashLink VM or HL/C (compile to C file)Server, desktop, mobile, game consoles (C export)2016 (3.4)
JVM1bytecode JVM: HotSpot, OpenJ9 Server, desktop2019 (4.0)
Eval1interpreterHaxe interpreterPrototyping, scripting2019 (4.0)

Advantages

Language

Haxe is a general-purpose programming language supporting object-oriented programming, generic programming, and various functional programming constructs. Features such as iterations, exceptions, and reflective programming (code reflection) are also built-in functions of the language and libraries. Unusual among programming languages, Haxe contains a type system which is both strong and dynamic. The compiler will check types implicitly via type inference and give compile-time errors, but it also enables programs to bypass type-checking and rely on a target platform's dynamic type-handling. All of the native target APIs can be used.

Type system

Haxe has a sophisticated and flexible type system. The type kinds it offers are classes, interfaces, function-method types, anonymous types, algebraic data types (called enum in Haxe), and abstract types. Parametric polymorphism is possible with classes, algebraic types and function types, giving the language support for generic programming based on type erasure. This includes support for variance in polymorphic functions, although not in type constructors.

The type system is static unless annotations for dynamic typing are present, for use with targets that support them. Type checking follows nominal typing with the exception of anonymous types where structural typing is used instead. Finally, type inference is supported, allowing for variable declarations without type annotations.

Modules and namespaces

All Haxe code is organized in modules, which are addressed using paths. In essence, each .hx file represents a module which may contain several types. For example, to create the type A in the package my.pack as shown, the folder structure should be my\pack and the file could be A.hx in the folder pack.

// file my/pack/A.hxpackagemy.pack;classA{}

In other modules, other types can be imported by putting import statements below the package definition, e.g. import my.pack.A;

A module can contain multiple types, such as the following. It is possible to import one type at a time from that module, using import my.pack2.A;. A type may be private, in which case only its containing module can access it.

packagemy.pack2;typedefA={a:String}privatetypedefB={b:String}

Classes

Classes (keyword class) in Haxe are similar to those in Java or TypeScript. Their fields can be either methods, variables, or properties, each static or per instance respectively. Haxe supports the accessors public and private, and more advanced methods for access control that are denoted using annotations. Methods and static constant variables can be inlined using the keyword inline. Fields can be marked as final to declare a constant that must be initialized immediately or in the constructor and cannot be written to, in case of function final will mark as non-overridable in subclasses.

Interfaces in Haxe are very similar to those in, for example, Java.

interfaceICreature{publicvarbirth:Date;publicvarname:String;publicfunctionage():Int;}classFlyimplementsICreature{publicvarbirth:Date;publicvarname:String;publicfunctionage():IntreturnDate.now().getFullYear()-birth.getFullYear();}

Generics

Haxe supports generic programming. The following is an example of the identity function.

functionidentity<T>(arg:T):T{returnarg;}

Enumerated types

Enumerated types are an important feature of the language; they can have type parameters and be recursive. [20] They provide basic support for algebraic data types, allowing the inclusion of product types, in a fashion similar to Haskell and ML. A switch expression can apply pattern matching to an enum value, allowing for elegant solutions to complex programming problems:

enumColor{red;green;blue;rgb(r:Int,g:Int,b:Int);}classColors{staticfunctiontoInt(c:Color):Int{returnswitch(c){casered:0xFF0000;casegreen:0x00FF00;caseblue:0x0000FF;casergb(r,g,b):(r<<16)|(g<<8)|b;}}staticfunctionvalidCalls(){varredint=toInt(Color.red);varrgbint=toInt(Color.rgb(100,100,100));}}

Examples of parametric enum types are the Haxe standard library types Option [21] and Either: [22]

enumOption<T>{Some(v:T);None;}enumEither<L,R>{Left(v:L);Right(v:R);}

Haxe also supports generalized algebraic data types (GADTs). [23] [24]

Anonymous types

Anonymous types are defined by denoting their structure explicitly, using a syntax that follows the mathematical record-based representation of a type. They can be used to implement structural typing for function arguments (see below), and can be given an alias with the keyword typedef:

typedefAliasForAnon={a:Int,b:String,c:Float->Void};

Function types

Functions are first-class values in Haxe. Their type is denoted by using arrows between argument types, and between the argument type(s) and return type, as common in many functional languages. However, unlike in prominent examples like Haskell or the ML language family, not all functions are unary functions (functions with one argument only), and in Haxe, functions can't be partially applied per default. Thus, the following type signatures have different semantics than in the aforementioned languages. The type F1 is a function that takes a String as arguments and returns a value of type Float.

Types F1 and F2 denote the same type, except that F2 uses labelled parameter, which is useful for completion and documentation.

Types F4 and F5 denote the same type. Both are binary functions that return a binary function of type F3. For F5 the syntax to declare a function type within a function type is used.

typedefF1=String->Float;typedefF2=(text:String)->Float;typedefF3=(score:Int,text:String)->Float;typedefF4=(score:Int,text:String)->F3;typedefF5=(score:Int,text:String)->((score:Int,text:String)->Float);

Anonymous functions

In Haxe, anonymous functions are called lambda, and use the syntax function(argument-list) expression; .

varf=function(x)returnx*x;f(8);// 64(function(x,y)returnx+y)(5,6);// 11

Abstract types

A relatively new addition to the Haxe type system is a concept termed abstract types. As used in Haxe, this refers to something different from a conventional abstract type. They are used to make conversions between types implicit, allowing reuse of existing types for specific purposes, like implementing types for units of measurement. This greatly reduces the risk of mixing up values of the same underlying type, but with different meanings (e.g., miles vs. km).

The following example assumes that the metric system is the default, while a conversion to miles is needed for legacy data. Haxe can automatically convert miles to kilometers, but not the reverse.

abstractKilometer(Float){publicfunctionnew(v:Float)this=v;}abstractMile(Float){publicfunctionnew(v:Float)this=v;@:topublicinlinefunctiontoKilometer():Kilometerreturn(newKilometer(this/0.62137));}classTest{staticvarkm:Kilometer;staticfunctionmain(){varone100Miles=newMile(100);km=one100Miles;trace(km);// 160.935}}

As the example shows, no explicit conversion is needed for the assignment "km = one100Miles;" to do the right thing.

Abstract types are entirely a compile-time feature of Haxe and do not exist at all at program runtime. As an example, both variables using abstract types above Mile and Kilometer will be of the type Float at runtime. [25]

Structural typing

In many functional programming languages, structural typing plays a major role. Haxe employs it in the presence of anonymous types, using the nominative typing of object-oriented programming, when only named types are involved. Anonymous types in Haxe are analogous to the implicit interfaces of the language Go as to typing. In contrast with Go interfaces, it is possible to construct a value using an anonymous type.

classFooBar{publicvarfoo:Int;publicvarbar:String;publicfunctionnew(){foo=1;bar="2";}functionanyFooBar(v:{foo:Int,bar:String})trace(v.foo);staticfunctiontest(){varfb=newFooBar();fb.anyFooBar(fb);fb.anyFooBar({foo:123,bar:"456"});}}

Internal architecture

Compiler

The Haxe compiler is divided into one frontend and multiple backends. The frontend creates an abstract syntax tree (AST) from the source code, and performs type checking, macro expansion, and optimization on the AST. The various backends translate the processed AST into source code or generate bytecode, depending on their target.

The compiler is written in OCaml. It can be run in server-mode to provide code completion for integrated development environments (IDEs) and maintain a cache, to further speed compiling. [26]

See also

Related Research Articles

OCaml is a general-purpose, high-level, multi-paradigm programming language which extends the Caml dialect of ML with object-oriented features. OCaml was created in 1996 by Xavier Leroy, Jérôme Vouillon, Damien Doligez, Didier Rémy, Ascánder Suárez, and others.

In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

Generic programming is a style of computer programming in which algorithms are written in terms of data types to-be-specified-later that are then instantiated when needed for specific types provided as parameters. This approach, pioneered in the programming language ML in 1973, permits writing common functions or data types that differ only in the set of types on which they operate when used, thus reducing duplicate code.

<span class="mw-page-title-main">D (programming language)</span> Multi-paradigm system programming language

D, also known as dlang, is a multi-paradigm system programming language created by Walter Bright at Digital Mars and released in 2001. Andrei Alexandrescu joined the design and development effort in 2007. Though it originated as a re-engineering of C++, D is now a very different language. As it has developed, it has drawn inspiration from other high-level programming languages. Notably, it has been influenced by Java, Python, Ruby, C#, and Eiffel.

<span class="mw-page-title-main">C syntax</span> Set of rules defining correctly structured programs

The syntax of the C programming language is the set of rules governing writing of software in C. It is designed to allow for programs that are extremely terse, have a close relationship with the resulting object code, and yet provide relatively high-level data abstraction. C was the first widely successful high-level language for portable operating-system development.

This article compares two programming languages: C# with Java. While the focus of this article is mainly the languages and their features, such a comparison will necessarily also consider some features of platforms and libraries.

In compiler construction, name mangling is a technique used to solve various problems caused by the need to resolve unique names for programming entities in many modern programming languages.

<span class="mw-page-title-main">Java syntax</span> Set of rules defining correctly structured program

The syntax of Java is the set of rules defining how a Java program is written and interpreted.

The computer programming languages C and Pascal have similar times of origin, influences, and purposes. Both were used to design their own compilers early in their lifetimes. The original Pascal definition appeared in 1969 and a first compiler in 1970. The first version of C appeared in 1972.

typedef is a reserved keyword in the programming languages C, C++, and Objective-C. It is used to create an additional name (alias) for another data type, but does not create a new type, except in the obscure case of a qualified typedef of an array type where the typedef qualifiers are transferred to the array element type. As such, it is often used to simplify the syntax of declaring complex data structures consisting of struct and union types, although it is also commonly used to provide specific descriptive type names for integer data types of varying sizes.

The C and C++ programming languages are closely related but have many significant differences. C++ began as a fork of an early, pre-standardized C, and was designed to be mostly source-and-link compatible with C compilers of the time. Due to this, development tools for the two languages are often integrated into a single product, with the programmer able to specify C or C++ as their source language.

In computer programming, an enumerated type is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type. The enumerator names are usually identifiers that behave as constants in the language. An enumerated type can be seen as a degenerate tagged union of unit type. A variable that has been declared as having an enumerated type can be assigned any of the enumerators as a value. In other words, an enumerated type has values that are different from each other, and that can be compared and assigned, but are not specified by the programmer as having any particular concrete representation in the computer's memory; compilers and interpreters can represent them arbitrarily.

C++11 is a version of a joint technical standard, ISO/IEC 14882, by the International Organization for Standardization (ISO) and International Electrotechnical Commission (IEC), for the C++ programming language. C++11 replaced the prior version of the C++ standard, named C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.

TypeScript is a free and open-source high-level programming language developed by Microsoft that adds static typing with optional type annotations to JavaScript. It is designed for the development of large applications and transpiles to JavaScript..

<span class="mw-page-title-main">Vala (programming language)</span> Programming language

Vala is an object-oriented programming language with a self-hosting compiler that generates C code and uses the GObject system.

This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.

NekoVM is a virtual machine developed by Nicolas Cannasse as part of research and development (R&D) efforts at two independent video game developers in Bordeaux, France: first at Motion Twin and then at Shiro Games. NekoVM's native language is the bytecode for a high-level dynamically typed programming language called Neko. This pairing allows Neko to be used directly as an embedded scripting language, or to target NekoVM by compiling another language to NekoVM bytecode.

The syntax and semantics of PHP, a programming language, form a set of rules that define how a PHP program can be written and interpreted.

Nemerle is a general-purpose, high-level, statically typed programming language designed for platforms using the Common Language Infrastructure (.NET/Mono). It offers functional, object-oriented, aspect-oriented, reflective and imperative features. It has a simple C#-like syntax and a powerful metaprogramming system.

<span class="mw-page-title-main">Nim (programming language)</span> Programming language

Nim is a general-purpose, multi-paradigm, statically typed, compiled high-level system programming language, designed and developed by a team around Andreas Rumpf. Nim is designed to be "efficient, expressive, and elegant", supporting metaprogramming, functional, message passing, procedural, and object-oriented programming styles by providing several features such as compile time code generation, algebraic data types, a foreign function interface (FFI) with C, C++, Objective-C, and JavaScript, and supporting compiling to those same languages as intermediate representations.

References

  1. "Release 4.3.6". 7 August 2024. Retrieved 22 August 2024.
  2. "Open Source licence Haxe".
  3. Introduction to the Haxe Standard Library, Haxe Docs
  4. Target Specific APIs, Introduction to the Haxe Standard Library, Haxe Docs
  5. Doucet, Lars (2014-06-24). "Dear Adobe: Support Haxe, save your Tools". Gamasutra.
  6. "Haxe Interview". Io Programmo. 2009-04-01. pp. 1–6. Archived from the original on 2015-12-08. Retrieved 2015-08-06.
  7. "Hello Lua!". Haxe.org.
  8. "hxnodejs".
  9. 1 2 "Compiler Targets". Haxe. Retrieved 2021-11-05.
  10. List of IDEs supporting Haxe, Haxe Foundation
  11. "Release alpha-1". haxe. HaxeFoundation. 2005-11-14. Retrieved 2022-04-02 via GitHub.
  12. Cannasse, Nicolas (2005-11-17). "Haxe Alpha 1b". haXe (Mailing list). Archived from the original on 2007-10-26.
  13. "Nicolas' announcement of spelling change on Haxe official mail list".
  14. "Haxe mailing list post on naming". Archived from the original on 2007-03-28.
  15. MTASC Compiler, MTASC website
  16. "Eval - The new Haxe macro interpreter".
  17. "Compiler Features".
  18. "Macros in Haxe".
  19. Dyachenko, Vadim (2013-12-05). "On "You can't make good HTML5 games in Haxe"". Yellow After Life.
  20. "Haxe reference detailing the use of enum". Archived from the original on 2012-05-11.
  21. "haxe/Option.hx at development · HaxeFoundation/haxe". Github. 7 November 2021.
  22. "haxe/Either.hx at development · HaxeFoundation/haxe". Github. 7 November 2021.
  23. "Language Features". Haxe - The Cross-platform Toolkit. Haxe Foundation. Retrieved 30 April 2015.
  24. "haxe/TestGADT.hx at development · HaxeFoundation/haxe". Github. 7 November 2021.
  25. "Abstract". Haxe - The Cross-platform Toolkit. Retrieved 2023-04-24.
  26. Server mode command-line: haxe --wait [host:]port