Skip to the content.

Объектно ориентированное программирование

NewLang поддерживает следующую концепцию объектно-ориентированного программирования:

Каждый объект представляет собой отдельный тип данных, который наследуется от другого типа или от одного из его наследников (что очень похоже на концепцию объектов в языке Java), а экземпляр конкретного класса создается путем вызова его типа.

Наследование поддерживается для типов словарь (:Dictionary) и класс (:Class) и всех их потомков.

Словарь

Словарь (:Dictionary) — набор данных произвольного типа с доступом к отдельным элементам по целочисленному индексу или по имени элемента при его наличии (это похоже и на tuple и на структуру одновременно). Словари отличаются от тензоров тем, что всегда имеют только одно измерение, но каждый элемент может содержать произвольное количество элементов любого типа, в том числе и другие словари.

Доступ к элементам словаря происходит по имени элемента, которое записывается через точку от имени переменной, либо по целочисленному индексу. Индекс начинается с 0 и как у тензоров, тоже может быть отрицательным (индекс элемента от “конца”).

# Новый тип (класс) :NewClass
:Dict := :Dictionary() {
    _ := 1; # У поля данных имя отсутствует
    two := 2;
    name := 3; 
};
dict := :Dict(); # Экземпляр класса (1, two=2, name=3,):Dict
dict2 := :Dict(two=42); # Экземпляр класса (1, two=42, name=100,):Dict
dict3 := dict2(99, name=0); # Копия объекта dict2 (99, two=42, name=0,):Dict

Словарь как литерал

Литерал с типом «словарь» в тексте программы записывается в круглых скобках с обязательной завершающей запятой, т. е. (,) — пустой словарь, (1, 2= «2», name=3,). Для указания конкретного типа объекта у литерала, его необходимо указать после закрывающей скобки, т.е. (1, two= «2», name=3,):Dict.

Важный момент! Хоть такой объект и будет иметь указанный тип, но он будет “неполноценным” и содержать только те данные, которые были явно указаны в скобках, что не гарантирует корректного создания реального объекта, т.к. для создания “правильного” объекта требуется вызвать его тип, т.е. :Dict();

Такой способ создания литералов классов используется в основном в различных операциях сравнения типов и “утиной типизации”, т.к. не требует доступа к рантайму, да и сам класс может быть не определен.

Перечисление, структура и объединение

Существуют три отдельных типа данных, :Enum, :Struct и :Union — которые так же являются словарями, но на их элементы накладываются определённые ограничения. Каждый элемент должен иметь уникальное имя, а его тип данных должен быть простым, т.е. числом или строкой фиксированного размера. Эти типы данных одновременно относятся к группе :Plain и могут быть представлены в двоичном виде в одной области машинной памяти.

Классы

Тип данных :Class аналогичен словарю, но все свойства обязаны иметь имена (хотя доступ к свойствам класса по индексу так же возможен). При создании экземпляра класса создается новая переменная, для которой копируются свойства свойства и методы всех родителей.

Синтаксис определения класса использует выглядит следующим образом:

# Новый тип (класс) :NewClass
:NewClass := :Class() { # Родительские класс или несколько классов через запятую
    field := 1;
    method() := {};
};
obj := :NewClass(); # Экземпляр класса

Так как NewLang реализует полный набор вариантов проверок при создании объектов (::= - создать новый объект, := - создать новый или присвоить значение существующему, = - только присвоить значение, а если объект не существует будет ошибка), то переопределения наследуемых функций не требует вообще никаких ключевых слов:

:NewClass2 := :NewClass() { # Новый класс на базе существующего
    field ::= 2;    # Будет ошибка, т. к. поле field уже есть в базовом классе
    method() = {};  # Аналог override, т.к. method должен существовать в базовом классе
};

Интерфейсы, именование методов классов и пространства имен

Для создания уникальных идентификаторов у методов классов NewLang использует подход, примерно как в языке Python. При создании метода класса создается глобальная функция с именем класса и именем метода, объединенные через разделитель области имен. Например, в классе :NewClass2 при создании метода method будет создана функция с именем NewClass2::method.

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

:NewClass3 := :NewClass() { # Новый класс на базе существующего
    virtual() := _; # Виртуальный метод
};

obj := :NewClass3(); # объект создать нельзя, будет ошибка

:NewClass3::virtual() := {}; # функция для виртуального метода

obj := :NewClass3(); # ОК