Узнайте о спецификаторах и атрибутах, а также о том, как применить дополнительную семантику и поведение к коду Verse.
Спецификаторы в Verse описывают поведение, связанное с семантикой, и могут добавляться к идентификаторам и некоторым ключевым словам. Для спецификаторов используется следующий синтаксис: ключевое слово между < и >, например IsPuzzleSolved()<decides><transacts> : void.
Атрибуты в Verse описывают поведение, определяемое за рамками самого языка Verse (в отличие от спецификаторов, которые описывают семантику Verse). В их синтаксисе используется знак @, за которым следует ключевое слово; к примеру, вот так: @editable.
Далее описаны все спецификаторы и атрибуты в Verse, а также способы их использования.
Спецификаторы эффектов
Эффекты в Verse указывают категории поведения, допустимые для функции. Спецификаторы эффектов можно применить:
к круглым скобкам () после имени в определении функции: name()<specifier> : type = codeblock;
к ключевому слову class: name := class<specifier>():.
Спецификаторы эффектов делятся на две категории:
исключительные: к функции или к ключевому слову class можно добавить только один исключительный спецификатор. Без исключительного спецификатора эффект по умолчанию устанавливается на no_rollback;
аддитивные: к функции или ключевому слову class можно добавить любое количество аддитивных спецификаторов эффектов или обойтись без них.
Нажмите на изображение, чтобы увеличить.
Пример
Эффект
name() : type = codeblock
no_rollback: эффект по умолчанию, если не указывается исключительный эффект. Он указывает на то, что любые действия, выполняемые функцией, не могут быть отменены, и поэтому функция не может быть использована в контексте, допускающем неоднозначность. Этот эффект нельзя указать вручную.
Исключительные эффекты
name()<transacts> : type = codeblock
transacts: указывает на то, что для любых действий, выполняемых функцией, можно выполнить откат. Эффект transacts требуется при каждой записи изменяемой переменной (var). Если эффект transacts добавлен к функции, для которой нельзя выполнить откат, то при компиляции кода вы получите соответствующее сообщение. Учтите, что эта проверка не выполняется для функций со спецификатором native.
name()<varies> : type = codeblock
varies: указывает на то, что функция при одних и тех же вводных данных не всегда возвращает один и тот же результат. Эффект varies также указывает на то, что поведение функции может измениться в новых версиях содержащего её пакета.
name()<computes> : type = codeblock
computes: требует, чтобы функция не имела побочных эффектов, а её завершение не гарантировалось. На эффект распространяется непроверяемое требование, чтобы функция при передаче ей одних и тех же аргументов возвращала один и тот же результат. Хорошим примером использования эффекта computes является любая функция без спецификатора native, которая в противном случае имела бы эффект converges.
name()<converges> : type = codeblock
converges: гарантирует отсутствие побочных эффектов и обязательное завершение (без бесконечной рекурсии) при выполнении соответствующей функции. Этот эффект может применяться только к функциям со спецификатором native, однако проверка компилятором на это не выполняется. Этот эффект необходимо использовать для кода, который предоставляет значения по умолчанию для класса или значения для глобальных переменных.
Аддитивные эффекты
name()<transacts><decides> : type = codeblock
decides: указывает на то, что функция может завершиться неуспешно, а также что вызов этой функции является выражением с неоднозначным результатом. Определения функций с эффектом decides должны также иметь эффект transacts. Таким образом, для действий, выполняемых этой функцией, можно выполнить откат (до состояния, когда эти действия ещё не выполнялись), если какая-либо часть функции не вернёт результат.
name()<suspends> : type = codeblock
suspends: указывает на то, что функция является асинхронной. Создаёт асинхронный контекст в теле функции.
В любом случае вызов функции, которая имеет определённый эффект, потребует от вызывающей функции наличия аналогичного эффекта.
Спецификаторы доступа
Спецификаторы доступа определяют, что и как может взаимодействовать с составляющей. Спецификаторы доступа могут применяться:
к идентификатору составляющей: name<specifier> : type = value;
к ключевому слову var составляющей: var<specifier> name : type = value.
name<public> : type = value
public: идентификатор доступен из всех частей кода. Спецификатор можно использовать для следующих элементов:
модуль;
класс;
интерфейс;
структура;
перечисление;
метод;
данные.
name<protected> : type = value
protected: идентификатор доступен только для текущего класса и любых подтипов. Спецификатор можно использовать для следующих элементов:
класс;
интерфейс;
структура;
перечисление;
немодульный метод;
данные.
name<private> : type = value
private: идентификатор доступен только внутри текущей области видимости (будь то модуль, класс, структура и т. д.). Спецификатор можно использовать для следующих элементов:
класс;
интерфейс;
структура;
перечисление;
немодульный метод;
данные.
name<internal> : type = value
internal: идентификатор доступен только внутри текущего модуля. Это уровень доступа по умолчанию. Спецификатор можно использовать для следующих элементов:
модуль;
класс;
интерфейс;
структура;
перечисление;
метод;
данные.
Спецификаторы классов
Спецификаторы классов определяют характеристики классов или их составляющих, например возможность создавать подкласс класса.
abstract: если класс или метод класса имеет спецификатор abstract, для него нельзя создать экземпляр. Абстрактные классы предназначены для использования в качестве суперкласса с частичной реализацией или в качестве общего интерфейса. Это полезно в тех случаях, когда экземпляры суперкласса не нужны, но необходимо дублировать свойства и поведение аналогичных классов.
cat := class<concrete>():
# Поле должно быть инициализировано, поскольку класс имеет спецификатор concrete
Name : string = "Кошка"
concrete: если класс имеет спецификатор concrete, его можно создать с пустым архетипом. Следовательно, каждое поле класса должно иметь значение по умолчанию. Каждый его подкласс также неявно относится к категории concrete. Класс со спецификатором concrete может наследовать непосредственно от абстрактного класса только в том случае, если оба класса определены в одном и том же модуле.
unique_class := class<unique>:
Field : int
Main()<decides> : void =
X := unique_class{Field := 1}
X = X # X тождественен себе
Y := unique_class{Field := 1}
X <> Y # X и Y отличаются и поэтому не равны
unique: можно применить к классу, чтобы сделать его уникальным. В языке Verse экземпляру уникального класса присваивается уникальный идентификатор. Это позволяет сравнивать экземпляры уникальных классов на равенство по их идентификаторы. Классы без спецификатора unique не имеют таких идентификаторов, поэтому их можно сравнивать на равенство только по значению их полей. Таким образом, уникальные классы можно сравнивать с помощью операторов = и <>, и они являются подтипами comparable.
cat := class<final>():
final: можно использовать только для классов и их составляющих:
Если у класса есть спецификатор final, невозможно создать подкласс этого класса.
Если поле имеет спецификатор final, невозможно переопределить это поле в подклассе.
Если метод имеет спецификатор final, невозможно переопределить этот метод в подклассе.
Спецификаторы реализации
Использовать спецификаторы реализации при написании кода невозможно, однако их можно увидеть при просмотре различных API UEFN.
native: указывает на то, что детали определения элемента реализованы на языке C++. Определения в Verse со спецификатором native автоматически генерируют определения на C++, в которые разработчик на Verse может позднее добавить собственную реализацию. Этот спецификатор используется для следующих типов:
native_callable: указывает на то, что метод экземпляра относится к категории native (т. е. реализован на языке C++) и что его вызов может быть реализован другим кодом на C++. Этот спецификатор используется в методе экземпляра. Он не распространяется на подклассы, поэтому его не нужно добавлять к определению при переопределении метода, уже имеющего этот спецификатор.
Атрибуты
Атрибуты в Verse описывают поведение, определяемое за рамками самого языка Verse (в отличие от спецификаторов, которые описывают семантику Verse). Их можно добавлять в строку, предшествующую коду с дальнейшим определением.
Их синтаксис подразумевает использование символа @, за которым идёт ключевое слово.
editable: указывает на то, что это поле является открытым свойством, значение которого можно изменить непосредственно в UEFN, поэтому нет необходимости вносить изменения в код Verse. Более подробно это описано в разделе «Настройка свойств устройства».
@doc("Принимает расстояние по длине сплайна и возвращает соответствующие значения данных в заданной точке")
QueryAtDistanceAlongSpline<native><public>(DistanceAlongSpline : float, bIsWorldSpace : logic)<transacts><decides> : spline_query_result
doc: поле для типового примечания у классов и функций (но не параметров функций). Оно должно содержать полезную информацию о назначении описываемого элемента или способе его применения. Оно также используется для заполнения страницы этого элемента в справочнике по API в Verse.