2.1.2 Переполнение

Ссылка на manual

Ссылка на код демонстрации

По причинам производительности компилятор Haxe не осуществляет переполнения. Задача проверки на переполнения лежит на конечной платформе. Вот несколько примеров поведения при переполнении на разных платформах:

  • С++, Java, C#, Neko, Flash: 32-битное целое число со знаком с обычным поведением при переполнении
  • PHP, JS, Flash 8: нет типа int, потеря точности случится в случае достижения лимита float($2^{52}$)

В качестве альтернативы можно использовать классы Int32 и Int64 для уверенности, что будет правильное поведение при переполнении независимо от платформы, но ценой дополнительных вычислений в зависимости от платформы.

Пример поведения при переполнении продемонстрирован в коде ниже. Переменная i, выходя за пределы Int, приобретает отрицательное значение, а затем при декременте на единицу — крайнее положительное значение

Заметка: данный код следует выполнять в среде HaxeDevelop, так как на Try Haxe! он (код) приобретает другое поведение

6.7. Итераторы

С Haxe очень легко определить пользовательские итераторы и итерируемые типы данных. Эти понятия представлены типами  Iterator и   Iterable <T> соответственно:

Любой класс, который структурно согласуется с одним из этих типов, может быть проитерирован с помощью цикла for-loop. То есть, если класс определяет методы   hasNext и   next с соответствующими возвращаемыми типами, он считается итератором, а если он определяет итератор метода, возвращающий   Iterator <T>, он считается итерируемым типом.

Выполнение кода можно посмотреть здесь

Тип   BoundsArrayIterator<T> в данном примере является итератором. Он реализует методы hasNext, проверяющий достигнут ли конец диапазона, и   next, возвращающий объект из массива. Данный итератор используется в классе   BoundsArrayWrap<T> в методе   iterator, который и определяет данный класс как итерируемый.

 

Ссылка на источник

6.6 Array Comprehension

Array Comprehension – это выражение, которое позволяет быстро создавать новый массив из существующего с помощью for и while, или без массива с инициализацией переменных во время итераций цикла.

Простые array comprehensions:

Array Comprehension с использованием if:

Array Comprehension c использованием двух массивов:

Выражение цикла могут быть любыми, включая условия и вложенные циклы, поэтому следующие действия будут работать так, как ожидалось:

Также можно инициализировать массив после его объявления:

Ссылка на официальную документацию

2.3.2 Наследование

Source: 2.3.2 Inheritance
Как во многих объектно-ориентированных языках, классы в языке Haxe являются основной структурой данных для большинства программ. Класс объявляется с помощью ключевого слова class . В классе может быть ноль или более полей класса. Пример класса Ogr, который представляет собой создание экземпляра класса Ogr, с полем life типа Int:

Экземпляр класса создается с помощью ключевого слова new:

Здесь метод toString() отображает представление экземпляра класса Ogr.

В Haxe классы могут наследовать от других классов, это осуществляется с помощью использования ключевого слова extends:

Так любой экземпляр класса Warrior также является экземпляром класса Ogr. В этом случае Ogr называется родительским классом для класса Warrior, а Warrior является дочерним классом для класса Ogr. У одного родительского класса может быть много дочерних классов, но у дочернего есть только один родительский класс. Определение «родительский класс класса X» обычно относится к его прямому родительскому классу, родительскому классу его родительского класса и т. д.

Вышеприведенный код класса Warrior очень похож на исходный код родительского класса Ogr, но в нем появляются две новые конструкции:

  • extends Ogr обозначает, что этот класс наследует класс Ogr
  • super (x, y) — вызывает конструктор родительского класса, в данном случае Ogr.new
  • override переопределяет метод toString()  родительского класса   Ogr

Для дочерних классов не обязательно определять собственные конструкторы, но если они есть, вызов super () является обязательным. В отличие от некоторых других объектно-ориентированных языков, этот вызов может появляться в любом месте кода конструктора и не обязательно должен быть первым выражением.

Создается экземпляр класса Warrior таким образом:

Где видно, что когда класс наследует другой класс он становится подтипом родительского класса, т.е. каждый экземпляр Warrior также является и экземпляром типа Ogr.

Подробнее про эффекты и ограничения переопределяющих методов можно прочитать в Overriding Methodsе.

5.17. Выражение переключатель (switch)

Ссылка на оригинальную статью
switch является оператором ветвления в языках программирования, в том числе и в Haxe.
Конструкция switch открывается выражением «switch» и тело функции ограничивается фигурными скобками {}. Ветки switch начинаются или с ключевого слова «case» с условием выполнения данной ветки, или же с ключевого слова «default» для состояния по умолчанию. В обоих случаях после условия идет двоеточие : и блок операторов, которой надо выполнить.

Так как в Haxe ветки case выполняются независимо, break не нужен.
Конструкции switch могут быть использованы как значение; в таком случае типы всех case и default должны унифицироваться.
Пример. Программа, которая выводит текущий день недели.

Haxe позволяет использовать выражения, например, мы можем в предыдущем примере написать не b, а b+1, если мы захотим узнать следующий день.
Также Haxe дает нам большие возможности в использовании switch. Например, мы можем проверять соответствия значений различных типов с помощью регулярных выражений (шаблонов).
Рассмотрим пример с массивом, взятый из данной статьи. В качестве входного параметра для оператора switch дается массив фиксированной длины. В зависимости от заполнения и длины массива, оператор будет выдавать нужный результат. Данный пример показывает нам, что мы можем указывать как и нужный нам размер массива, так и заполнение определенной ячейки массива. Например, программа выведет 0 только при массиве длинной в два элемента, где в первой ячейке будет число 2. Если же размер массива будет другой или первой будет стоять не 2, то оператор продолжит сравнения с остальными условиями. В данном примере роль состояния по умолчанию выполняет последнее условие, которое «пропускает» любой массив. Стоит заметить, что если выполняются сразу два условия, например, массив будет состоять из [2,6], то программа все равно выведет 0, так как первое условие идет раньше.

7. Использование Компилятора

Ссылка на оригинальную статью.

Основное использование
Компилятор Haxe обычно вызывается из командной строки с несколькими аргументами, которые должны отвечать на два вопроса:

  • Что должно быть скомпилировано?
  • Каким должен быть результат?

Чтобы ответить на первый вопрос, обычно достаточно указать путь к классу через аргумент -cp path вместе с основным классом, который должен быть скомпилирован с помощью аргумента -main dot_path. Компилятор Haxe затем выбирает основной файл класса и начинает компиляцию.

Второй вопрос обычно сводится к предоставлению аргумента, указывающего желаемый результат. Haxe имеет выделенный переключатель командной строки, такой как -js имя_файла для JavaScript и -php для PHP. В зависимости от характера цели значение аргумента представляет собой либо имя файла (для -js, -swf и neko), либо путь к каталогу.

Общие аргументы
Ввод:

  • -cp path — добавляет путь к классу, в котором могут быть найдены .hx исходные файлы или пакеты (подкаталоги).
  • -lib library_name — добавляет библиотеку Haxelib. По умолчанию используется самая последняя версия в локальном репозитории Haxelib. Чтобы использовать определенную версию, можно использовать -lib library_name: version.
  • -main dot_path — устанавливает основной класс.

Вывод:

  • -js имя_файла — генерирует исходный код JavaScript в указанном файле.
  • -as3 directory — генерирует исходный код ActionScript 3 в указанной папке.
  • -swf file_name — создает указанный файл как Flash.swf.
  • -neko file_name — генерирует двоичный файл Neko в качестве указанного файла.
  • -php directory — генерирует исходный код PHP в указанной директории.
  • -cpp directory — создает исходный код C++ в указанном каталоге и компилирует его с использованием собственного компилятора C++.
  • -cs directory — создает исходный код C# в указанной директории.
  • -java directory — создает исходный код Java в указанном каталоге и компилирует его с помощью компилятора Java.
  • -python имя_файла — генерирует исходный код Python в указанном файле.

5.18. Исключительные ситуации (try-catch)

Ссылка на оригинал статьи тут
Haxe позволяет обрабатывать исключительные ситуации  используя синтаксис try/catch.

Если во время работы   try-выражения выполняется  throw , то генерируется исключительная ситуация, которая  может быть обработана любым последующим catch блоком. Эти блоки  состоят из

  • имени переменной которая содержится во вброшенном значении,
  • подробной  аннотации типа которая определяет для каких типов значений генерировать исключительные ситуации,
  • выражение которое будет исполняться в этом случае

Haxe позволяет сгенерировать исключительную ситуацию (throw) и обработать (catch) любой тип значения. Он не ограничен типами, наследованными от класса exception или  класса error. Блоки catch проверяются сверху вниз, с первого чей тип поддерживается или чье вброшенное значение будет обработано.

Этот процесс имеет много общего с поведением унификации во время компиляции. ( ссылка).
Однако, поскольку проверка должна быть выполнена во время выполнения, существует несколько ограничений:

Тип Dynamic может обрабатывать любое исключение.

Пример моего кода

Try Haxe!

4.1. Переменные

Ссылка на оригинальную статью.

Мы уже сталкивались с переменными в нескольких примерах из предыдущих статей. В переменных, как и в свойствах (но не во всех), содержатся значения:

Из примера видно, что переменная:

  1. имеет имя (в данном случае: member),
  2. имеет тип (в данном случае: String),
  3. может быть инициализирована (в данном случае значением: "bar") и
  4. может иметь  модификаторы доступа (в данном случае: static)

Метод main выведет значение переменной member присвоенное ей при инициализации, затем изменит его на «foo», и снова выведет содержимое. Значение модификаторов доступа в данном примере, объясняется в отдельной статье.

Важно заметить, что явное указание типов не обязательно, если присутствует начальная инициализация.  В этом случае, компилятор определяет тип переменной сам (см. Определение Типов).

Схема 1: Инициализация переменной.

Рассмотрим следующий пример. Воспользовавшись Схемой 1, объявим константу:

Метод main выведет: 1. (Ссылка на выполнение кода из примера)

Так как в Haxe не существует квалификатора const, в качестве альтернативы, в данном примере мы используем ключевое слово inline для статических переменных. (Подробнее про inline)

В случае, если мы попытаемся присвоить какое-либо значение константе myconst, при компиляции кода, мы получим ошибку «Cannot access field or identifier myconst for writing» (в переводе «Невозможно получить доступ для записи в поле либо идентификатор myconst«).

2.2 Обнуляемые типы

Определение: Nullable (обнуляемый)
Тип в Haxe считается обнуляемым, если null является допустимым значением для него.

Обычно языки программирования имеют единое и весьма конкретное определение допустимости нулевых значений. Однако, Haxe вынужден искать в этом вопросе компромисс в связи с различной природой языков на которые будет транслироваться программ (будем называть такие языки целевыми). В то время, как некоторые из целевых языков допускают (и фактически используют как значения по умолчанию) null-значения, все остальные вообще не допускают null-значений для некоторых типов. Это требует разделения целевых языков на 2 группы по типизации:

  • Определение: Статические целевые языки.
    Статическими будем называть целевые языки, использующие статическую типизацию. Это группа языков, в которых переменные связываются с определенным типом данных в момент объявления. Таковыми являются, например, Flash, C++, Java, C#.
  • Определение: Динамические целевые языки.
    Динамическими назовем те целевые языки для которых характерна динамическая типизация. Это группа языков, в которых переменные связываются с типом данных в момент присваивания им значения. Это, например, JavaScript, PHP, Neko, Flash 6-8.

Не о чем беспокоиться при работе с null для языков с динамической типизацией, так как в них переменные обнуляемы, в то время, как языки со статической типизацией требуют внимания.

При работе с языками со статической типизацией объекты базовых типов при создании могут быть инициализированы значениями по умолчанию.

Определение: Значения по умолчанию.
В языках со статической типизацией базовые типы имеют следующие значения по умолчанию:

  • Int: 0
  • Float: NaN на Flash, 0.0 на остальных статических целях
  • Bool: false

Как следствие, компилятор Haxe не допускает присваивания null базовым типам в языках со статической типизацией. Чтобы сделать допустимым такое присваивание необходимо обернуть базовый тип следующим образом Null<T>:

Таким же образом, базовые типы не могут быть сравнимы с null, если они не обёрнуты должным образом:

Это ограничение распространяется на все ситуации, где работает унификация.

На языках со статической типизацией  Null<T>, Null<Bool>, Null<Float> могут быть использованы чтобы сделать допустимым null-значения. На языках с динамической типизацией это не будет иметь эффекта. Null<T> также может быть использовано с другими типами данных для указания того, что null-значение допустимо.

Если null-значение будет «скрыто» Null<T> или динамической типизацией, при присваивании базовому типу будет использовано значение по умолчанию:

Подготовлено по материалам раздела Nullability официального учебника языка Haxe.

2.7.1 Динамический тип данных с типовым параметром

Ссылка на статью

Динамический тип — это специальный тип данных, потому как он допускает объявление переменной с и без строгой типизации. Если переменная строго типизирована, то семантика, описанная в динамическом типе, накладывает ограничения по всем свойствам, чтобы они были совместимы с заданным типом. Например, на следующем коде видно поведение Dynamic при строгой типизации String: