Правила: действия и присваивание
Правила действий
-
Ассистент ОБЯЗАН избегать
FOR, когда тот же результат можно выразить множественной (set-based) конструкцией.FORитерирует строку за строкой, и к нему СЛЕДУЕТ прибегать в последнюю очередь, когда нет декларативной альтернативы. Единственное измеренное исключение работает в обратную сторону: когда присваиваемое значение — агрегатGROUP, границы которого коррелируют с обновляемой строкой, и оба множества велики, множественный вариант может скомпилироваться в запрос, материализующий всю корреляцию, и построчныйFOR ... NOINLINEможет оказаться быстрее — там, где индекс по агрегируемому классу позволяет отвечать на агрегат каждой строки индексным обращением.Предпочитайте множественные альтернативы, например:
- агрегация или материализация множества
->
GROUP SUM,GROUP CONCAT,GROUP MAX,GROUP LAST,GROUP AGGR - присваивание свойства по множеству
-> прямое присваивание свойства с параметрами
вместо цикла
FOR ... DO - экспорт табличных или иерархических данных
->
EXPORT FROM,EXPORT JSON FROM,EXPORT XML FROM,EXPORT CSV FROM - построение структурированных данных
->
JSON FROM,XML FROM - массовые интеграционные записи
->
NEW,DELETEили множественное изменение свойства вместо построчногоFOR
FORприемлем, когда тело имеет настоящий построчный поток управления, такой как условныйAPPLY,MESSAGE,throwExceptionили внешние вызовы, которые нельзя выразить как операцию над множеством. - агрегация или материализация множества
->
-
Параметры, вводимые в
NEW alias = ClassиFOR expr(p) [NEW alias = Class] DO { ... }, НЕ следуют обычным правилам лексической области видимости из мейнстримных языков программирования.Такие параметры видны ТОЛЬКО внутри тела блока
NEWили циклаFOR, которые их вводят.Ассистент НЕ ДОЛЖЕН ссылаться на эти параметры вне вводящего их блока.
Когда зависимое вычисление должно переиспользовать эти параметры, ассистенту СЛЕДУЕТ вкладывать дополнительные блоки
NEWилиFORвнутрь вводящего блока, где параметры ещё находятся в области видимости, вместо того чтобы выносить значения во вспомогательное хранилище.И наоборот, параметр, объявленный внутри агрегата
GROUP, принадлежит этому агрегату и НЕ виден снаружи; в частности, он не может служить переменной цикла объемлющегоFOR. Объявите переменную собственным параметромFOR, а агрегат используйте только как логическое условие по ней. -
Ассистенту СЛЕДУЕТ избегать введения свойств
LOCALбез конкретной необходимости.LOCALматериализует временную таблицу в PostgreSQL, только когда содержит больше одной строки, поэтому стоимость выполнения, заметно превышающая стоимость стековой переменной в обычном языке, относится кLOCALс параметрами (буферы, ключуемые номером строки, значения по объектам).LOCALбез параметров содержит не больше одной строки и всегда хранится в памяти, поэтому флаги и одиночные значения без параметров дёшевы; избегать их стоит не из-за стоимости, а чтобы не плодить сущности. -
LOCALобычно оправдан, когда выполняются ОБА условия:- его значение нетривиально вычислить (агрегация, join'ы, многошаговая логика, внешние вызовы или другая работа, которую стоит материализовать), И
- то же значение используется более одного раза, так что материализация позволяет избежать повторного вычисления.
-
По возможности ассистенту СЛЕДУЕТ предпочитать альтернативы новому
LOCAL:- встраивать выражение в каждое место использования, если оно дешёвое
- вкладывать блоки
NEW/FOR, чтобы промежуточные значения оставались в области видимости параметров - использовать обычное (не
LOCAL) вычисляемое свойство, когда значение переиспользуется в нескольких действиях
-
Это рекомендации, а не жёсткие запреты. Если ассистент не может найти рабочий синтаксис для конструкции без
LOCALили другой подход постоянно не получается и не выходит построить чистое действие, откат кLOCALприемлем как последнее средство.Устоявшиеся паттерны
LOCAL, предписанные другими правилами (например, промежуточное хранение при импорте, перенос между вложенными сессиями), остаются допустимыми; ассистенту СЛЕДУЕТ держать такиеLOCALминимальными по количеству и области. -
Параметры верхнеуровневых операторов тела действия разделяют один контекст параметров: одинаковые имена обозначают один и тот же параметр, а класс параметра объявляется только при первом использовании.
В генерируемых скриптах (
eval, наполнение данных) ассистенту СЛЕДУЕТ давать параметрам верхнеуровневых операторов уникальные имена, чтобы не зависеть от порядка операторов. -
Многие системные служебные действия возвращают результат через одноимённое локальное свойство без параметров (например, в
Utils: действиеfileExists[ISTRING[500]]пишет в свойствоfileExists[]). Такой элемент — ДЕЙСТВИЕ, а не логическое свойство: ассистент ОБЯЗАН сначала вызвать действие, а затем прочитать свойство без параметров (fileExists(path); IF fileExists() THEN ...), и НЕ ДОЛЖЕН использовать форму с параметрами внутри выражения (IF fileExists(path)— неверно).
Правила присваивания (<-)
-
Аргументы изменяемого свойства в левой части
<-могут быть выражениями от параметров оператора (sentFolder(account(f)) <- f), но новые локальные параметры вводятся только как типизированные параметры, а не внутри выражений. Запись «по вычисляемому ключу» по аналогии с императивнымmap[key] = valueлегко нарушает это.Поэтому, перенаправляя ссылки-самоссылки при глубоком копировании графа объектов, ассистенту СЛЕДУЕТ держать обратное отображение и итерироваться с ЦЕЛЕВЫМ объектом в роли параметра —
link(Copy n) <- newOf(link(srcOf(n))) WHERE spec(n);— а не писатьlink(newOf(x)) <- newOf(link(x)); -
<- выражение IF условиеприсваивает всё выражение ВСЕМ объектам: там, где условие не выполняется, свойство перезаписываетсяNULL. Фактически это сброс-плюс-запись.ДОБАВЛЯЯ присваивание к свойству, уже заполненному ранее в том же действии, ассистент ОБЯЗАН использовать форму
WHERE(prop(x) <- TRUE WHERE cond(x)), которая меняет только строки, подходящие под условие. Второе присваивание в форме IF тому же свойству ОБЯЗАНО рассматриваться как сигнал тревоги при ревью. -
Написанный прямо в теле действия или события
PREV(<выражение>)переносит в состояние начала сессии ВСЁ обёрнутое выражение, включая подвыражения-аргументы: аргумент, вычисленный в текущей сессии (LOCAL, свойство объекта, созданного в сессии), внутриPREVчитается какNULL, молча обнуляя результат.Чтобы прочитать предыдущие данные при текущих аргументах, ассистент ОБЯЗАН обернуть
PREVв отдельное свойство —prevF(x) = PREV(f(x));— и вызывать его, а не писатьPREV(f(<вычисленный в сессии аргумент>))в теле. -
Параметр, через который читается или изменяется свойство с именем, объявленным у нескольких классов, ОБЯЗАН быть аннотирован классом при первом использовании (
date(Interaction i) <- ...): перегруженное имя разрешается по классам параметров, и нетипизированный параметр даёт ошибку «ambiguous name». Особенно это касается событий: их оператор — отдельный контекст параметров, в котором класс больше ниоткуда не выводится, а условиеi IS Interactionкласс параметра не задаёт.
Правила циклов (FOR, WHILE)
-
FORфиксирует свой набор до первой итерации: условие вычисляется один раз, подходящие строки читаются, и тело выполняется по разу на строку этого набора. То, что меняет тело, — включая данные под условием — не добавляет и не убирает итераций.Перечитывает набор
WHILE, но делает это по ШАГАМ, а не по строкам: один шаг заново вычисляет условие, читает весь подходящий набор и выполняет тело для каждой его строки, и только потом набор читается снова; итерации прекращаются, когда он возвращается пустым. Поэтому строка, уже попавшая в текущий шаг, свою очередь получит, даже если более ранняя строка того же шага сделала условие для нее ложным. -
Без
ORDERFORобходит свой набор в произвольном порядке. Ассистент ОБЯЗАН задавать явныйORDERвсюду, где результат зависит от последовательности — нумерация, накопительные итоги, любое чтение записанного предыдущей итерацией, — и всюду, гдеTOPограничивает число взятых строк, и ОБЯЗАН завершать этотORDERключом, различающим любые две строки.
Правила потоков (NEWTHREAD, NEWEXECUTOR)
-
Серверное поточное действие разделяет сессию изменений вызывающего кода, а сессии изменений не потокобезопасны.
Поэтому ассистенту СЛЕДУЕТ оборачивать тело серверного
NEWTHREADвNEWSESSION, а когда ему нужна собственная транзакция базы — вNEWSESSION NEWSQL. Это размен: обычныйNEWSESSIONперестает видеть несохраненные изменения вызывающего, поэтому обертка опускается только там, где разделение сессии сделано намеренно И известно, что одновременно они не выполняются.Что дает обертка внутри транзакции
APPLY, зависит от того, КОГДА тело стартует на самом деле. Пока транзакция открыта,NEWSESSION, включаяNEWSQL, сессию не создает — действие откладывается в текущую, — а проверка происходит в момент выполнения тела, а не в момент его планирования.SCHEDULE DELAY— это число миллисекунд, а не барьер, ждущий применения, так что и он ничего не гарантирует. Ассистент НЕ ДОЛЖЕН рассчитывать, что поток, запущенный из глобального обработчика, окажется изолированным.Клиентский executor — обратный случай: действие доставляется в соединение пользователя и выполняется там в собственной свежей сессии, так что оборачивать его незачем.