Typed lua + OOP

Не секрет, что язык программирования lua в десятки раз быстрее python. Это обосновывает, почему microsoft использует lua в vscode, а также почему этот язык используется в neovim.

Teal - это очень хорошая попытка сделать lua типизированным. Идея языка - применять типизацию во время компиляции кода, превращая его в обычный lua (похоже на typescript). Да, в runtime типизация может нарушиться (если есть ввод данных), но при правильной архитектуре можно этого избежать.

Есть игровые движки, где используется lua (и следовательно teal): LÖVE (игра Balatro), Solar2D (игра Coromon). Поэтому типизированный lua становится интересен.

В языке есть структура данных record - это как struct в других языках, но без методов. Через танцы с метатаблицами можно прикрутить методы к этим record.

Но я оформил pull request, который создает в этом языке struct - это продолжение record, но с методами и одиночным наследованием: https://github.com/teal-language/tl/pull/1161

struct - это намеренно примитивная реализация OOP. Пользователь регистрирует поля и методы:

local struct Person name: string end

Экземпляр создается через зарезервированный метод new, куда передаются аргументы для полей:

local a = Person.new {name = “Andrey”}

Наследование только одиночное:

local struct Employee:Person is_lead: boolean = false end

Если требуется вычислить поле посредством других имеющихся полей, то это делается через init без параметров:

function Employee:init() self.payroll_x2: boolean = self.is_lead == true end

init выполняется автоматически от родительского класса к текущему. Методы и поля экземпляра мапятся в метатаблицу текущего экземпляра (в том числе и родительские), это покрывает 95% случаев, но при этом не требует накладных расходов на диспетчеризацию в runtime. При желании, всегда можно сделать метод на текущем экземпляре, который явно позовет другой метод родительского экземпляра или обратится к полю родителя (а это покрывает оставшиеся 5%, но пользователь платит за это намеренно).

Почему бы не передавать параметры в init? А я отвечу: зачем? Ведь если требуется предварительная калькуляция с данными, которые не относятся к структуре, то напишите функцию-хэлпер, а далее передайте в экземпляр struct только то, что относится к структуре (и ничего лишнего).

Также можно создать статические поля:

local Employee:Person static q_of_employees: number = 0 end end

Статические поля также не имеют диспетчеризации и мапятся напрямую к текущему типу struct. При желании опять же можно вручную сделать диспетчеризацию к родительскому статическому полю, если потребуется.

И статические методы:

function Employee.print_q_of_employees() print(Employee.q_of_employees) end

Если PR не примут, то буду развивать свой форк, но мне не хочется постоянно поддерживать обновления teal, поэтому PR предпочтителен.