Бизнес-правило, которое исполняется, тестируется и печатается в код.
Справа — работающий файл: двадцать строк без единой скобки, из которых состоит почти всё, что пишут на языке в первый день. Вот что делает каждая конструкция.
категория«Продажи»Предметная область и первая строка файла. Всё остальное лежит внутри неё отступом: скобок и точек с запятой в языке нет.
объект ПокупкаДанные и их типы. Встроенных типов пять — строка, число, дата, деньги, признак; иногда является делает поле необязательным.
утилита«Рассчитать скидку»Расчёт. Вход, тип результата и стартовое значение обязательны: пропустите начинает с — компилятор откажет с FTS_UTILITY_INITIAL, а не подставит ноль сам.
правило«Большая покупка»Условие и одно действие. Срабатывают все правила, чьи условия истинны, и складываются: это не цепочка if / else if.
свойство«Скидка ограничена»Постусловие: проверяется после всех правил. Нарушено — остановка с FTS_UTILITY_PROPERTY, а не тихая подрезка до «безопасного» числа.
пример«Двадцать тысяч»Тест внутри спецификации: те же две строки читает человек и выполняет fts test — отсюда подпись под кодом.
category"Sales"object Purchase
amount ismoneyutility"Calculate discount"accepts Purchase
returnsmoneystarts with0rule"Large purchase"if amount is at least10000thenadd10percentoffield amount
property"Discount is capped"resultis at most15000example"Twenty thousand purchase"given amount equals20000expectedresultequals2000
Как читать код на этой страницедевять видов лексем, один словарь цветов на все языки
утилитаобъявление
принимаетключевое слово
«Рассчитать скидку»предметное имя
деньгитип
не меньшесравнение
далитерал
"постоянный"строка
20000число
// пояснениекомментарий
Раскраска FTS снята настоящим лексером языка при сборке (npm run fts:highlight), напечатанный JavaScript, C, Go, Rust, Python и файлы проектов красит Chroma, встроенный в Hugo. Ни то, ни другое не ходит в сеть и не грузит библиотек в браузер: страница остаётся раскрашенной с выключенным JavaScript и в оффлайн-поставке.
От правила к модели
Язык прибавляется по одной конструкции, и каждая покупает компилятору новую проверку.
Четыре ступени на одном и том же примере. Первые три — куски файла из первого экрана, четвёртая — из модели «Отгрузка заказа», её можно открыть в песочнице на шаге 3.
ступень 1 · правило
Условие и одно действие
правило«Большая покупка»если сумма не меньше10000тодобавить10процентовотполя сумма
Сравнений в языке ровно шесть: равен, не равен, больше, меньше, не больше, не меньше. Седьмое даст FTS_UTILITY_COMPARISON с номером строки. Правил может быть сколько угодно, и работают они не как if / else if: выполняются все, чьи условия истинны, и складываются.
Три строки обязательны — вход, тип результата и стартовое значение. Ноль в начинает с написал автор модели, а не подставил рантайм: без строки компилятор откажет с FTS_UTILITY_INITIAL. С ней fts run считает утилиту на JSON, а flang emit печатает её в пять языков.
Свойство — постусловие: проверяется после всех правил, и нарушенное останавливает выполнение с FTS_UTILITY_PROPERTY, а не подрезает результат до «безопасного» числа втихую. Пример — тест, лежащий внутри спецификации: те же строки читает человек и выполняет fts test. Разойтись документации и коду здесь нечем — файл один.
ступень 4 · морфизм и теорема
Категорная часть, и она маленькая
морфизм«Готовый заказ можно отгрузить»если«Готов к отгрузке»то«Отгрузить заказ разрешено»теорема«Заказ ЗК-7781 можно отгрузить»дано Заказ имеет«готов к отгрузке»равноедав данных заказы найти где номер равен«ЗК-7781»по морфизму«Готовый заказ можно отгрузить»следовательно«Отгрузить заказ разрешено»
Объекты категории — типы: структура Заказ и именованные состояния. Морфизм — стрелка A → B с именем и объявленным законом; он не выполняет переход, а утверждает его допустимость. Композиция разрешена ровно тогда, когда кодомен предыдущей стрелки совпал с доменом следующей, а равенство типов номинальное — по имени. Строка в данных — самая важная: она превращает рассуждение в проверяемое утверждение, называя, где искать свидетельство.
fts check · проверено статически
Имена не дублируются, поля объектов существуют, домен и кодомен каждого морфизма — реальные типы, объявленный вывод совпал с вычисленным. Данных на этом уровне ещё нет.
fts test · выполнено
Примеры утилиты сошлись с текущей семантикой. Это тест, а не доказательство: сходятся ровно те входы, что написаны в файле, — отсюда три примера на одно правило вокруг границы.
flang check · доказано
У функции со словом тотальная компилятор принял доказательство структурного убывания: она не может зациклиться. Анализ консервативен и отвергает часть действительно завершающихся программ — FLANG_NOT_TOTAL.
чего не даёт ни один уровень
Истинности самого правила, полноты модели и актуальности снимка данных. «FTS доказал, что заказ можно отгружать» — неверно; верно: «проверил свидетельство и типизируемость вывода при объявленных законах». Где проходит эта граница.
Одна модель · пять языков
Спецификация не описывает код проекта — она в него печатается.
Печатает flang — полный язык, выросший из FTS. Любая модель FTS остаётся его валидной программой, а напечатанный модуль самодостаточен: рантайм едет в тот же каталог. Ниже одно правило и его свойство в пяти целевых языках.
order-discount.fts · тот же файл, что открыт в песочницеисходник
«Скидка ограничена» в каждом языке — проверка с кодом FTS_UTILITY_PROPERTY, а не комментарий рядом.
совпадает с интерпретатором
Собранный C, собранный Rust, напечатанные JavaScript и Python на входе «сумма 20000, постоянный клиент да» отвечают 3000 — столько же, сколько песочница на шаге 3.
ядро FTS написано на flang
Лексер, парсер, вычислитель и печать JSON — 300 функций, все тотальные; цепочка «из текста в JSON» сходится байт в байт с эталонным ядром на TypeScript.
собирается без Node
Ядро печатается в C и собирается cc -Werror -pedantic; в зависимостях только libc и libm.
Быстрый старт
Node.js 20, клон репозитория — и правило считается.
Пакета в npm пока нет, поэтому CLI собирается из исходников. У ядра нет рантайм-зависимостей, а flang запускается прямо из клона, без сборки. Чтобы просто посмотреть, ставить ничего не нужно: песочница ниже считает тем же компилятором прямо во вкладке.
1 · поставитьbash
git clone https://github.com/digitable-lol/flang.git
cd flang
npm ci
npm run build
2 · выполнить первое правилоbash
# типы, ссылки, композиция
node dist/src/cli.js check examples/utilities/discount.fts --pretty
# примеры, записанные внутри правила
node dist/src/cli.js test examples/utilities/discount.fts --pretty
# напечатать в код: c | go | rust | python | js
node flang/bin/flang.mjs emit examples/utilities/discount.fts \
--target go --out ./out-go
Каждая команда печатает JSON в stdout, диагностику в stderr и возвращает ненулевой код на провале — один контракт для CI, редактора и агента.
Живая песочница
Отредактируйте правило — результат пересчитается в вашей вкладке.
Тот же компилятор @digitable-lol/fts@0.4.0, что и в Node.js: всё считается локально, без единого запроса на сервер. Вкладки справа — это команды CLI: «Проверка» — fts check, «Примеры» — fts test, «Выполнить» — fts run, «Доказательство» — fts prove.
Что песочница не делает.prove — символьный вывод: он показывает, что цепочка морфизмов типизируется, но данные при этом не проверяет и даёт тот же ответ с контекстом и без него. Проверенный сертификат выпускает fts certify и пересчитывает fts verify — они считают дайджесты через node:crypto и живут на серверной стороне. Чем символьный вывод отличается от проверенного.
order-discount.fts
Компилятор загрузится, когда секция появится на экране.
Компилятор загружается…
На чём это уже написано
26 задач LeetCode на flang: у 20 завершение доказано компилятором.
Алгоритмическая задача не даёт предметной опоры — это проверка языка на чужой территории. Файлы читаются из репозитория языка при сборке страницы, числа сняты запуском flang check и flang test.
26
решений в репозитории языка
20
с доказанным завершением
62/77
тотальных функций
135/135
примеров сходится
Пришли учиться? Каждая строчка карточки объясняется главой курса.Ниже в каждом решении: техника, две гарантии компилятора, список функций с отметкой тотальности и полный исходник. Вот где про это написано — по одной главе на каждый элемент.
Отступы, слова, примерыЧем запись решения отличается от псевдокода и почему пример — часть исходника.
Записи, варианты, спискиОткуда в решениях деревьев берутся запись и вариант и почему их не приходится объявлять дважды.
ТотальностьЧто стоит за значком «завершение доказано» и у скольких решений его нет: 6 из 26.
Свёртки вместо цикловТа самая «свёртка», с которой начинается техника почти каждого решения ниже.
flang check и flang testДве команды, которыми сняты все числа на карточках; те же коды выхода в CI.
Границы языкаНевыразимых задач в наборе — 12: почему они не пишутся вовсе и почему это записано, а не спрятано.
1Две суммы
Two Sum
завершение доказаносписки6/6 примеров3/3 функций тотальны65 строк
Вместо словаря «значение → номер» — вложенный проход: для каждой головы ищем дополнение в хвосте. O(n²) как прямая цена отсутствия хеш-таблиц.
flang check
модуль «Две суммы» проходит проверку типов. Главная функция «Две суммы» тотальна: компилятор принял доказательство завершения.
flang test
6 примеров в файле, сошлось 6, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль«Две суммы»// LeetCode 1. Two Sum.// Дан список чисел и цель. Найти два разных элемента, дающих в сумме цель,// и вернуть их номера. Номера — с нуля, как в условии задачи; язык нумерует// строки с единицы, поэтому пересчёт делается один раз, в конце.//// Тотальная. Хеш-таблиц в языке нет, поэтому вместо словаря «значение → номер»// работает вложенный проход: для каждой головы ищем дополнение в хвосте.// Это O(n²) вместо O(n) — прямая цена отсутствия ассоциативного массива.тотальнаяфункция«Позиция значения»принимает элементы: списокчисла, нужное: числовозвращаетчислопример«Найдено вторым»дано элементы равно [5, 7]
дано нужное равно7ожидается2пример«Не найдено»дано элементы равно [5, 7]
дано нужное равно9ожидается0разбор элементов
случайпустото0случайголова и хвостеслиголоваравен нужное
то1иначепусть дальше равно«Позиция значения»отхвости нужное
если дальше равен0то0иначе дальше плюс1тотальнаяфункция«Поиск пары с позиции»принимает элементы: списокчисла, цель: число, позиция: числовозвращаетсписокчислапример«Пара в начале»дано элементы равно [2, 7, 11]
дано цель равно9дано позиция равно0ожидается [0, 1]
разбор элементов
случайпустотопустой списокслучайголова и хвостпусть смещение равно«Позиция значения»отхвости (цель минусголова)
если смещение больше0то [позиция, позиция плюс смещение]
иначе«Поиск пары с позиции»отхвости цель и (позиция плюс1)
тотальнаяфункция«Две суммы»принимает элементы: списокчисла, цель: числовозвращаетсписокчислапример«Пример 1 из условия»дано элементы равно [2, 7, 11, 15]
дано цель равно9ожидается [0, 1]
пример«Пример 2 из условия»дано элементы равно [3, 2, 4]
дано цель равно6ожидается [1, 2]
пример«Пример 3 из условия»дано элементы равно [3, 3]
дано цель равно6ожидается [0, 1]
«Поиск пары с позиции»от элементы и цель и0
104Глубина дерева
Maximum Depth of Binary Tree
завершение доказанодеревья3/3 примеров4/4 функций тотальны61 строк
Поле варианта — часть значения, поэтому рекурсия по поддеревьям доказывается сразу. Дерево в примерах строится из списка: вариант в «дано» не записать.
flang check
модуль «Глубина дерева» проходит проверку типов, объявлено типов: 1. Главная функция «Глубина из списка» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль«Глубина дерева»// LeetCode 104. Maximum Depth of Binary Tree.// Найти длину самого длинного пути от корня до листа.//// Тотальная. Дерево — сумма типов, а поле варианта анализ завершаемости// считает частью значения, поэтому рекурсия по поддеревьям доказывается// сразу и без ухищрений. Деревья язык принимает лучше, чем строки и числа.//// Дерево в примерах строится из списка: значение варианта нельзя записать// в «дано» (парсер теряет имя конструктора), поэтому вход задаётся списком,// а «Дерево из списка» превращает его в дерево поиска.тип«Дерево»вариант Лист
вариант Узел содержит значение: число, левое: «Дерево», правое: «Дерево»тотальнаяфункция«Вставить в дерево»принимает дерево: «Дерево», число: числовозвращает«Дерево»разбор дерева
случай Лист
то Узел с значение равнымчислои левое равнымвариант Лист и правое равнымвариант Лист
случайвариант Узел с значение как зн и левое как л и правое как п
есличисломеньше зн
топусть новое равно«Вставить в дерево»от л ичисло
Узел с значение равным зн и левое равным новое и правое равным п
иначепусть новое равно«Вставить в дерево»от п ичисло
Узел с значение равным зн и левое равным л и правое равным новое
тотальнаяфункция«Дерево из списка»принимает элементы: списокчиславозвращает«Дерево»свёртка элементы начиная свариант Лист как акк и эл →«Вставить в дерево»от акк и эл
тотальнаяфункция«Глубина»принимает дерево: «Дерево»возвращаетчислоразбор дерева
случай Лист
то0случайвариант Узел с левое как л и правое как п
пусть слева равно«Глубина»от л
пусть справа равно«Глубина»от п
1плюс (если слева больше справа то слева иначе справа)
тотальнаяфункция«Глубина из списка»принимает элементы: списокчиславозвращаетчислопример«Сбалансированное дерево»дано элементы равно [2, 1, 3]
ожидается2пример«Цепочка»дано элементы равно [1, 2, 3, 4]
ожидается4пример«Пустое дерево»дано элементы равнопустой списокожидается0«Глубина»от («Дерево из списка»от элементы)
35Место вставки
Search Insert Position
завершение доказанопоиск3/3 примеров1/1 функций тотальны27 строк
Одна свёртка: счёт элементов меньше цели. O(n) вместо требуемого O(log n) — двоичный поиск в тотальном классе требует приёма с топливом.
flang check
модуль «Место вставки» проходит проверку типов. Главная функция «Место вставки» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль«Место вставки»// LeetCode 35. Search Insert Position.// В отсортированном списке найти номер цели, а если её нет — номер, куда её// следовало бы вставить. Номера с нуля, как в условии.//// Тотальная. Условие требует O(log n), здесь же один линейный проход:// двоичный поиск в тотальном классе требует особого приёма (см. 704), а// линейная свёртка доказывается сама. Ответ тот же, сложность хуже —// и это честная цена, а не недосмотр.тотальнаяфункция«Место вставки»принимает элементы: списокчисла, цель: числовозвращаетчислопример«Пример 1 из условия»дано элементы равно [1, 3, 5, 6]
дано цель равно5ожидается2пример«Пример 2 из условия»дано элементы равно [1, 3, 5, 6]
дано цель равно2ожидается1пример«Пример 3 из условия»дано элементы равно [1, 3, 5, 6]
дано цель равно7ожидается4свёртка элементы начиная с0как акк и эл →если эл меньше цель то акк плюс1иначе акк
509Фибоначчи
Fibonacci Number
доказательства нетчисла6/6 примеров0/2 функций тотальны42 строк
Линейный вариант с двумя накопителями. Нетотальна ровно из-за счётчика: убывания по числу анализ не знает.
flang check
модуль «Фибоначчи» проходит проверку типов. Главная функция «Фибоначчи» не тотальна: убывание не доказано, конечность держится на входных данных.
flang test
6 примеров в файле, сошлось 6, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль«Фибоначчи»// LeetCode 509. Fibonacci Number.// F(0) = 0, F(1) = 1, дальше сумма двух предыдущих.//// Обычная. Прямое определение F(n) = F(n−1) + F(n−2) не проходит анализ// завершаемости по той же причине, что и всё остальное на числах: убывания// по числовому аргументу он не знает. Здесь взят линейный вариант с двумя// накопителями — он и работает быстрее, и делает нетотальность заметной:// единственное, что мешает доказать конец, — счётчик.функция«Фибоначчи шагом»принимает осталось: число, предыдущее: число, текущее: числовозвращаетчислопример«Ноль шагов»дано осталось равно0дано предыдущее равно7дано текущее равно9ожидается7если осталось не больше0то предыдущее
иначе«Фибоначчи шагом»от (осталось минус1) и текущее и (предыдущее плюс текущее)
функция«Фибоначчи»принимает н: числовозвращаетчислопример«Пример 1 из условия»дано н равно2ожидается1пример«Пример 2 из условия»дано н равно3ожидается2пример«Пример 3 из условия»дано н равно4ожидается3пример«Нулевое»дано н равно0ожидается0пример«Тридцатое»дано н равно30ожидается832040«Фибоначчи шагом»от н и0и1
Остальные решения по категориям: 22 из 26
Раскройте карточку — под ней полный файл из flang/examples/leetcode. Связывания модулей в языке нет, поэтому решение самодостаточно и запускается как есть. Раскраска этих 22 файлов лежит отдельным файлом и приезжает при раскрытии карточки: код в разметке есть всегда, цвета — по требованию.
26Удалить повторы
Remove Duplicates from Sorted Array
завершение доказаносписки4/4 примеров2/2 функций тотальны32 строк
Свёртка с проверкой «содержит». Изменения на месте нет: значения flang неизменяемы, возвращается новый список.
flang check
модуль «Удалить повторы» проходит проверку типов. Главная функция «Без повторов» тотальна: компилятор принял доказательство завершения.
flang test
4 примеров в файле, сошлось 4, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Удалить повторы»
// LeetCode 26. Remove Duplicates from Sorted Array.
// Из отсортированного списка убрать повторы. В условии просят изменить массив
// на месте и вернуть число уникальных; изменяемых массивов в языке нет и быть
// не может — значения flang неизменяемы, — поэтому возвращается новый список,
// а «число уникальных» вынесено отдельной функцией.
//
// Тотальная: свёртка по списку, рекурсии нет вовсе.
тотальная функция «Без повторов»
принимает элементы: список числа
возвращает список числа
пример «Пример 1 из условия»
дано элементы равно [1, 1, 2]
ожидается [1, 2]
пример «Пример 2 из условия»
дано элементы равно [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]
ожидается [0, 1, 2, 3, 4]
свёртка элементы начиная с пустой список как акк и эл
если акк содержит эл то акк иначе добавить эл к акк
тотальная функция «Сколько уникальных»
принимает элементы: список числа
возвращает число
пример «Пример 1 из условия»
дано элементы равно [1, 1, 2]
ожидается 2
пример «Пустой список»
дано элементы равно пустой список
ожидается 0
длина («Без повторов» от элементы)
66Прибавить единицу
Plus One
завершение доказаносписки6/6 примеров3/3 функций тотальны57 строк
Перенос идёт справа налево, свёртка — слева направо, поэтому список обращается дважды. Обращение выражено свёрткой: встроенного «в начало» нет.
flang check
модуль «Прибавить единицу» проходит проверку типов, объявлено типов: 1. Главная функция «Прибавить единицу» тотальна: компилятор принял доказательство завершения.
flang test
6 примеров в файле, сошлось 6, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Прибавить единицу»
// LeetCode 66. Plus One.
// Список цифр — большое число. Прибавить к нему единицу.
//
// Тотальная. Перенос идёт справа налево, а свёртка идёт слева направо,
// поэтому список сначала обращается, потом сворачивается, потом обращается
// обратно. Обращение выражается свёрткой, а не встроенной формой: приписать
// элемент в начало списка встроенным «добавить … к …» нельзя — оно
// дописывает в конец.
объект «Перенос»
перенос является числом
цифры является список числа
тотальная функция «Приписать в начало»
принимает первый: число, элементы: список числа
возвращает список числа
пример «В непустой»
дано первый равно 1
дано элементы равно [2]
ожидается [1, 2]
свёртка элементы начиная с [первый] как акк и эл → добавить эл к акк
тотальная функция «Обратить»
принимает элементы: список числа
возвращает список числа
пример «Три элемента»
дано элементы равно [1, 2, 3]
ожидается [3, 2, 1]
свёртка элементы начиная с пустой список как акк и эл → «Приписать в начало» от эл и акк
тотальная функция «Прибавить единицу»
принимает цифры: список числа
возвращает список числа
пример «Пример 1 из условия»
дано цифры равно [1, 2, 3]
ожидается [1, 2, 4]
пример «Пример 2 из условия»
дано цифры равно [4, 3, 2, 1]
ожидается [4, 3, 2, 2]
пример «Пример 3 из условия»
дано цифры равно [9]
ожидается [1, 0]
пример «Сплошные девятки»
дано цифры равно [9, 9, 9]
ожидается [1, 0, 0, 0]
пусть начальное равно запись «Перенос» с перенос равным 1 и цифры равным пустой список
пусть итог равно свёртка («Обратить» от цифры) начиная с начальное как акк и цифра
пусть сумма равно цифра плюс акк.перенос
пусть новая равно сумма остаток от 10
пусть дальше равно если сумма не меньше 10 то 1 иначе 0
запись «Перенос» с перенос равным дальше и цифры равным (добавить новая к акк.цифры)
пусть результат равно «Обратить» от итог.цифры
если итог.перенос равен 1
то «Приписать в начало» от 1 и результат
иначе результат
Не классическое слияние (оно убывает то по одному списку, то по другому и получает FLANG_NOT_TOTAL), а вставка в свёртке: O(n·m), зато доказуемо конечно.
flang check
модуль «Слияние отсортированных» проходит проверку типов. Главная функция «Слить отсортированные» тотальна: компилятор принял доказательство завершения.
flang test
6 примеров в файле, сошлось 6, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Слияние отсортированных»
// LeetCode 88. Merge Sorted Array.
// Слить два отсортированных списка в один отсортированный.
//
// Тотальная — но не тем алгоритмом, каким её пишут обычно. Классическое
// слияние рекурсирует то по первому списку, то по второму, и анализ
// завершаемости такой цикл отвергает: он требует ОДНУ позицию аргумента,
// убывающую на каждом рекурсивном вызове (totality.mjs, контрпример в шапке
// файла). Слияние даёт две разные позиции и получает FLANG_NOT_TOTAL.
//
// Поэтому здесь слияние вставками: «Вставить по порядку» рекурсирует ровно
// по хвосту второго аргумента, а внешний проход — свёртка. Результат тот же,
// сложность O(n·m) вместо O(n+m).
тотальная функция «Приписать в начало»
принимает первый: число, элементы: список числа
возвращает список числа
пример «В непустой»
дано первый равно 1
дано элементы равно [2]
ожидается [1, 2]
свёртка элементы начиная с [первый] как акк и эл → добавить эл к акк
тотальная функция «Вставить по порядку»
принимает значение: число, элементы: список числа
возвращает список числа
пример «В середину»
дано значение равно 2
дано элементы равно [1, 3]
ожидается [1, 2, 3]
пример «В пустой»
дано значение равно 2
дано элементы равно пустой список
ожидается [2]
разбор элементов
случай пусто
то [значение]
случай голова и хвост
если значение не больше голова
то «Приписать в начало» от значение и элементы
иначе «Приписать в начало» от голова и («Вставить по порядку» от значение и хвост)
тотальная функция «Слить отсортированные»
принимает первый: список числа, второй: список числа
возвращает список числа
пример «Пример 1 из условия»
дано первый равно [1, 2, 3]
дано второй равно [2, 5, 6]
ожидается [1, 2, 2, 3, 5, 6]
пример «Пример 2 из условия»
дано первый равно [1]
дано второй равно пустой список
ожидается [1]
пример «Пример 3 из условия»
дано первый равно пустой список
дано второй равно [1]
ожидается [1]
свёртка второй начиная с первый как акк и эл → «Вставить по порядку» от эл и акк
136Одиночное число
Single Number
завершение доказаносписки4/4 примеров2/2 функций тотальны36 строк
Побитового исключающего ИЛИ в языке нет вовсе, поэтому подсчёт вхождений: O(n²) вместо O(n).
flang check
модуль «Одиночное число» проходит проверку типов. Главная функция «Одиночное число» тотальна: компилятор принял доказательство завершения.
flang test
4 примеров в файле, сошлось 4, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Одиночное число»
// LeetCode 136. Single Number.
// В списке каждое число встречается дважды, кроме одного. Найти его.
//
// Тотальная. Канонического решения «сложить всё исключающим ИЛИ» здесь нет:
// побитовых операций в языке нет вообще (SPEC, раздел 4 перечисляет только
// арифметику и сравнения). Поэтому — подсчёт вхождений, O(n²) вместо O(n).
тотальная функция «Считать вхождения»
принимает элементы: список числа, значение: число
возвращает число
пример «Два вхождения»
дано элементы равно [4, 1, 4]
дано значение равно 4
ожидается 2
свёртка элементы начиная с 0 как акк и эл → если эл равен значение то акк плюс 1 иначе акк
тотальная функция «Одиночное число»
принимает элементы: список числа
возвращает число
пример «Пример 1 из условия»
дано элементы равно [2, 2, 1]
ожидается 1
пример «Пример 2 из условия»
дано элементы равно [4, 1, 2, 1, 2]
ожидается 4
пример «Пример 3 из условия»
дано элементы равно [1]
ожидается 1
пусть одиночные равно отфильтровать элементы где эл → («Считать вхождения» от элементы и эл) равен 1
разбор одиночные
случай пусто
то 0
случай голова и хвост
голова
169Мажоритарный элемент
Majority Element
завершение доказаносписки3/3 примеров2/2 функций тотальны34 строк
Прямой подсчёт вхождений и фильтр. Бойер — Мур без изменяемых счётчиков всё равно свёлся бы к свёртке с записью.
flang check
модуль «Мажоритарный элемент» проходит проверку типов. Главная функция «Мажоритарный элемент» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Мажоритарный элемент»
// LeetCode 169. Majority Element.
// Найти элемент, встречающийся более чем в половине позиций списка.
//
// Тотальная. Алгоритм Бойера — Мура здесь не нужен: без изменяемых счётчиков
// он всё равно свёлся бы к свёртке с записью-состоянием, а прямой подсчёт
// вхождений короче и очевидно верен. Цена — O(n²).
тотальная функция «Считать вхождения»
принимает элементы: список числа, значение: число
возвращает число
пример «Три тройки»
дано элементы равно [3, 3, 3, 1]
дано значение равно 3
ожидается 3
свёртка элементы начиная с 0 как акк и эл → если эл равен значение то акк плюс 1 иначе акк
тотальная функция «Мажоритарный элемент»
принимает элементы: список числа
возвращает число
пример «Пример 1 из условия»
дано элементы равно [3, 2, 3]
ожидается 3
пример «Пример 2 из условия»
дано элементы равно [2, 2, 1, 1, 1, 2, 2]
ожидается 2
пусть половина равно (длина элементы) делить на 2
пусть частые равно отфильтровать элементы где эл → («Считать вхождения» от элементы и эл) больше половина
разбор частые
случай пусто
то 0
случай голова и хвост
голова
217Есть повторы
Contains Duplicate
завершение доказаносписки3/3 примеров1/1 функций тотальны26 строк
Рекурсия по хвосту плюс «хвост содержит голову». Множеств нет, отсюда O(n²).
flang check
модуль «Есть повторы» проходит проверку типов. Главная функция «Есть повторы» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Есть повторы»
// LeetCode 217. Contains Duplicate.
// Есть ли в списке хотя бы одно значение, встречающееся дважды.
//
// Тотальная: рекурсия строго по хвосту списка. Множества в языке нет,
// поэтому проверка «голова встречается дальше» — линейный поиск,
// и всё решение получается O(n²).
тотальная функция «Есть повторы»
принимает элементы: список числа
возвращает признак
пример «Пример 1 из условия»
дано элементы равно [1, 2, 3, 1]
ожидается да
пример «Пример 2 из условия»
дано элементы равно [1, 2, 3, 4]
ожидается нет
пример «Пример 3 из условия»
дано элементы равно [1, 1, 1, 3, 3, 4, 3, 2, 4, 2]
ожидается да
разбор элементов
случай пусто
то нет
случай голова и хвост
если хвост содержит голова то да иначе «Есть повторы» от хвоста
283Сдвинуть нули
Move Zeroes
завершение доказаносписки3/3 примеров2/2 функций тотальны30 строк
Два фильтра и склейка, без рекурсии. «На месте» не выражается: значения неизменяемы.
flang check
модуль «Сдвинуть нули» проходит проверку типов. Главная функция «Сдвинуть нули» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Сдвинуть нули»
// LeetCode 283. Move Zeroes.
// Перенести все нули в конец, сохранив порядок остальных.
//
// Тотальная и без единой рекурсии: два фильтра и склейка. В условии просят
// работать «на месте», но изменяемых массивов в языке нет — возвращается
// новый список того же содержания.
тотальная функция «Соединить списки»
принимает первый: список числа, второй: список числа
возвращает список числа
пример «Склейка»
дано первый равно [1]
дано второй равно [0, 0]
ожидается [1, 0, 0]
свёртка второй начиная с первый как акк и эл → добавить эл к акк
тотальная функция «Сдвинуть нули»
принимает элементы: список числа
возвращает список числа
пример «Пример 1 из условия»
дано элементы равно [0, 1, 0, 3, 12]
ожидается [1, 3, 12, 0, 0]
пример «Пример 2 из условия»
дано элементы равно [0]
ожидается [0]
пусть ненулевые равно отфильтровать элементы где эл → эл не равен 0
пусть нули равно отфильтровать элементы где эл → эл равен 0
«Соединить списки» от ненулевые и нули
344Обратить строку
Reverse String
завершение доказаносписки5/5 примеров3/3 функций тотальны41 строк
Условие даёт массив символов — то есть список, а список разбирается структурно. Та же операция над строкой тотальной быть не может.
flang check
модуль «Обратить строку» проходит проверку типов. Главная функция «Обратить символы» тотальна: компилятор принял доказательство завершения.
flang test
5 примеров в файле, сошлось 5, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Обратить строку»
// LeetCode 344. Reverse String.
// Условие даёт массив символов и просит обратить его на месте. Массив
// символов — это список строки, и здесь это удача: список flang разбирается
// структурно, а строка нет. Поэтому задача остаётся тотальной, тогда как
// та же операция над строкой (модуль stdlib/strings.flang) тотальной быть
// не может — по строке нельзя рекурсировать с доказательством убывания.
//
// Изменения на месте нет и не будет: значения flang неизменяемы.
тотальная функция «Приписать строку в начало»
принимает первая: строка, элементы: список строки
возвращает список строки
пример «В непустой»
дано первая равно "а"
дано элементы равно ["б"]
ожидается ["а", "б"]
свёртка элементы начиная с [первая] как акк и эл → добавить эл к акк
тотальная функция «Обратить символы»
принимает символы: список строки
возвращает список строки
пример «Пример 1 из условия»
дано символы равно ["h", "e", "l", "l", "o"]
ожидается ["o", "l", "l", "e", "h"]
пример «Пример 2 из условия»
дано символы равно ["H", "a", "n", "n", "a", "h"]
ожидается ["h", "a", "n", "n", "a", "H"]
пример «Пустой список»
дано символы равно пустой список
ожидается пустой список
свёртка символы начиная с пустой список как акк и буква → «Приписать строку в начало» от буква и акк
тотальная функция «Строка из символов»
принимает символы: список строки
возвращает строка
пример «Склейка»
дано символы равно ["o", "l", "l", "e", "h"]
ожидается "olleh"
свёртка символы начиная с "" как акк и буква → соединить акк с буква
349Пересечение списков
Intersection of Two Arrays
завершение доказаносписки3/3 примеров2/2 функций тотальны29 строк
Уникальные из первого, фильтр по вхождению во второй. Множеств нет, уникальность строится вручную.
flang check
модуль «Пересечение списков» проходит проверку типов. Главная функция «Пересечение» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Пересечение списков»
// LeetCode 349. Intersection of Two Arrays.
// Вернуть уникальные значения, встречающиеся в обоих списках.
//
// Тотальная: два прохода свёрткой и фильтром, рекурсии нет.
// Множеств в языке нет, «уникальность» строится вручную через «содержит».
тотальная функция «Уникальные»
принимает элементы: список числа
возвращает список числа
пример «С повторами»
дано элементы равно [1, 1, 2]
ожидается [1, 2]
свёртка элементы начиная с пустой список как акк и эл
если акк содержит эл то акк иначе добавить эл к акк
тотальная функция «Пересечение»
принимает первый: список числа, второй: список числа
возвращает список числа
пример «Пример 1 из условия»
дано первый равно [1, 2, 2, 1]
дано второй равно [2, 2]
ожидается [2]
пример «Пример 2 из условия»
дано первый равно [4, 9, 5]
дано второй равно [9, 4, 9, 8, 4]
ожидается [4, 9]
отфильтровать («Уникальные» от первый) где эл → второй содержит эл
13Римское в число
Roman to Integer
доказательства нетстроки9/9 примеров2/4 функций тотальны94 строк
Свёртка с записью-состоянием (сумма и предыдущая цифра). Нетотальна только из-за разложения строки в список символов.
flang check
модуль «Римские числа» проходит проверку типов, объявлено типов: 1. Главная функция «Римское в число» не тотальна: убывание не доказано, конечность держится на входных данных.
flang test
9 примеров в файле, сошлось 9, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Римские числа»
// LeetCode 13. Roman to Integer.
// Перевести римскую запись в число.
//
// Обычная, не тотальная — и виновата только строка. Сам разбор («если
// предыдущая цифра меньше текущей, вычесть её дважды») выражается свёрткой
// с записью-состоянием и тотален; но чтобы получить символы строки, её надо
// обойти по одному, а убывание «строка стала короче на символ» анализ
// завершаемости не признаёт: часть значения — это хвост списка, голова,
// поле записи или поле варианта, и ничего больше.
//
// Была бы встроенная форма «символы строки», задача стала бы тотальной
// целиком.
объект «Разбор римского»
сумма является числом
предыдущее является числом
тотальная функция «Приписать строку в начало»
принимает первая: строка, элементы: список строки
возвращает список строки
пример «В непустой»
дано первая равно "I"
дано элементы равно ["V"]
ожидается ["I", "V"]
свёртка элементы начиная с [первая] как акк и эл → добавить эл к акк
тотальная функция «Значение цифры»
принимает буква: строка
возвращает число
пример «Единица»
дано буква равно "I"
ожидается 1
пример «Тысяча»
дано буква равно "M"
ожидается 1000
пример «Не римская цифра»
дано буква равно "щ"
ожидается 0
если буква равен "I"
то 1
иначе
если буква равен "V"
то 5
иначе
если буква равен "X"
то 10
иначе
если буква равен "L"
то 50
иначе
если буква равен "C"
то 100
иначе
если буква равен "D"
то 500
иначе
если буква равен "M" то 1000 иначе 0
функция «Символы с позиции»
принимает текст: строка, позиция: число
возвращает список строки
пример «С последнего символа»
дано текст равно "IV"
дано позиция равно 2
ожидается ["V"]
если позиция больше (длина текст)
то пустой список
иначе
пусть буква равно подстрока текст с позиция по позиция
«Приписать строку в начало» от буква и («Символы с позиции» от текст и (позиция плюс 1))
функция «Римское в число»
принимает текст: строка
возвращает число
пример «Пример 1 из условия»
дано текст равно "III"
ожидается 3
пример «Пример 2 из условия»
дано текст равно "LVIII"
ожидается 58
пример «Пример 3 из условия»
дано текст равно "MCMXCIV"
ожидается 1994
пример «Вычитание»
дано текст равно "IV"
ожидается 4
пусть начальное равно запись «Разбор римского» с сумма равным 0 и предыдущее равным 0
пусть итог равно свёртка («Символы с позиции» от текст и 1) начиная с начальное как акк и буква
пусть значение равно «Значение цифры» от буква
пусть поправка равно если акк.предыдущее меньше значение то (2 умножить на акк.предыдущее) иначе 0
запись «Разбор римского» с сумма равным (акк.сумма плюс значение минус поправка) и предыдущее равным значение
итог.сумма
14Наибольший общий префикс
Longest Common Prefix
доказательства нетстроки7/7 примеров0/3 функций тотальны60 строк
Свёртка по словам поверх посимвольного сравнения двух слов. Сравнение идёт рекурсией по номеру позиции — её анализ убывания не принимает.
flang check
модуль «Общий префикс» проходит проверку типов. Главная функция «Наибольший общий префикс» не тотальна: убывание не доказано, конечность держится на входных данных.
flang test
7 примеров в файле, сошлось 7, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Общий префикс»
// LeetCode 14. Longest Common Prefix.
// Найти наибольший общий начальный кусок для списка слов.
//
// Обычная. Свёртка по списку слов тотальна, а вот сравнение двух слов идёт
// по позициям — рекурсия по числу, которую анализ завершаемости не
// принимает: «позиция плюс 1» не является частью значения «позиция».
// Ни один известный приём здесь не спасает: топливо (см. задачу 704) можно
// было бы взять из списка слов, но длина слова с длиной списка не связана.
функция «Общий префикс до позиции»
принимает первое: строка, второе: строка, позиция: число
возвращает строка
пример «Совпало два символа»
дано первое равно "flower"
дано второе равно "flow"
дано позиция равно 1
ожидается "flow"
пусть предел равно если (длина первое) меньше (длина второе) то (длина первое) иначе (длина второе)
если позиция больше предел
то подстрока первое с 1 по предел
иначе
если (подстрока первое с позиция по позиция) равен (подстрока второе с позиция по позиция)
то «Общий префикс до позиции» от первое и второе и (позиция плюс 1)
иначе подстрока первое с 1 по (позиция минус 1)
функция «Общий префикс двух»
принимает первое: строка, второе: строка
возвращает строка
пример «Общее начало»
дано первое равно "flower"
дано второе равно "flight"
ожидается "fl"
пример «Ничего общего»
дано первое равно "dog"
дано второе равно "racecar"
ожидается ""
«Общий префикс до позиции» от первое и второе и 1
функция «Наибольший общий префикс»
принимает слова: список строки
возвращает строка
пример «Пример 1 из условия»
дано слова равно ["flower", "flow", "flight"]
ожидается "fl"
пример «Пример 2 из условия»
дано слова равно ["dog", "racecar", "car"]
ожидается ""
пример «Одно слово»
дано слова равно ["abc"]
ожидается "abc"
пример «Пустой список»
дано слова равно пустой список
ожидается ""
разбор слова
случай пусто
то ""
случай голова и хвост
свёртка хвост начиная с голова как акк и слово → «Общий префикс двух» от акк и слово
20Правильные скобки
Valid Parentheses
доказательства нетстроки12/12 примеров3/5 функций тотальны99 строк
Стек живёт строкой в накопителе свёртки, поломка запоминается префиксом «!». Списочная версия тотальна, строковая — нет.
flang check
модуль «Правильные скобки» проходит проверку типов. Главная функция «Скобки в строке» не тотальна: убывание не доказано, конечность держится на входных данных.
flang test
12 примеров в файле, сошлось 12, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Правильные скобки»
// LeetCode 20. Valid Parentheses.
// Проверить, что скобки трёх видов закрываются в правильном порядке.
//
// Две функции нарочно. «Скобки в списке» принимает список символов и
// тотальна: стек живёт в накопителе свёртки (обычной строкой — стек строк
// потребовал бы отдельного типа), а свёртка конечна по построению.
// «Скобки в строке» принимает строку, как в условии, и тотальной быть не
// может: чтобы получить символы, строку надо обойти, а убывание строки
// анализ завершаемости не признаёт — «пусто» и «голова и хвост» работают
// только со списком.
//
// Прерывать свёртку нельзя, поэтому ошибка запоминается в самом стеке:
// строка, начинающаяся с «!», означает «уже сломано».
тотальная функция «Приписать строку в начало»
принимает первая: строка, элементы: список строки
возвращает список строки
пример «В непустой»
дано первая равно "а"
дано элементы равно ["б"]
ожидается ["а", "б"]
свёртка элементы начиная с [первая] как акк и эл → добавить эл к акк
тотальная функция «Парная открывающая»
принимает закрывающая: строка
возвращает строка
пример «Круглая»
дано закрывающая равно ")"
ожидается "("
пример «Квадратная»
дано закрывающая равно "]"
ожидается "["
если закрывающая равен ")"
то "("
иначе
если закрывающая равен "]" то "[" иначе "{"
тотальная функция «Скобки в списке»
принимает символы: список строки
возвращает признак
пример «Пример 1 из условия»
дано символы равно ["(", ")"]
ожидается да
пример «Пример 2 из условия»
дано символы равно ["(", ")", "[", "]", "{", "}"]
ожидается да
пример «Пример 3 из условия»
дано символы равно ["(", "]"]
ожидается нет
пример «Незакрытая скобка»
дано символы равно ["(", "["]
ожидается нет
пусть стек равно свёртка символы начиная с "" как акк и буква
если акк начинается с "!"
то акк
иначе
если "([{" содержит буква
то соединить акк с буква
иначе
если (длина акк) равен 0
то "!"
иначе
пусть верх равно подстрока акк с (длина акк) по (длина акк)
если верх равен («Парная открывающая» от буква)
то подстрока акк с 1 по ((длина акк) минус 1)
иначе "!"
стек равен ""
функция «Символы с позиции»
принимает текст: строка, позиция: число
возвращает список строки
пример «С последнего символа»
дано текст равно "()"
дано позиция равно 2
ожидается [")"]
если позиция больше (длина текст)
то пустой список
иначе
пусть буква равно подстрока текст с позиция по позиция
«Приписать строку в начало» от буква и («Символы с позиции» от текст и (позиция плюс 1))
функция «Скобки в строке»
принимает текст: строка
возвращает признак
пример «Пример 1 из условия»
дано текст равно "()"
ожидается да
пример «Пример 2 из условия»
дано текст равно "()[]{}"
ожидается да
пример «Пример 3 из условия»
дано текст равно "(]"
ожидается нет
пример «Вложенные скобки»
дано текст равно "{[()]}"
ожидается да
«Скобки в списке» от («Символы с позиции» от текст и 1)
49Группировка анаграмм
Group Anagrams
завершение доказаностроки6/6 примеров5/5 функций тотальны70 строк
Подпись слова — свёртка по алфавиту с подсчётом через «разделить»; словарь заменён списком записей «Группа» с линейным поиском.
flang check
модуль «Группировка анаграмм» проходит проверку типов, объявлено типов: 1. Главная функция «Группировать анаграммы» тотальна: компилятор принял доказательство завершения.
flang test
6 примеров в файле, сошлось 6, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Группировка анаграмм»
// LeetCode 49. Group Anagrams.
// Разложить слова по группам: в одной группе — слова из одних и тех же букв.
//
// Тотальная. Каноническое решение — словарь «подпись → список слов», а
// ассоциативных массивов в языке нет. Замена: список записей «Группа» и
// линейный поиск по ключу. Подпись слова тоже строится без обхода символов —
// свёрткой по алфавиту с подсчётом через «разделить» (тот же приём, что в
// задаче 242). Порядок групп — порядок первого появления.
//
// Цена отсутствия словаря: O(слов × групп × 26) вместо O(суммарной длины).
объект «Группа»
ключ является строкой
слова является список строки
тотальная функция «Счёт буквы»
принимает текст: строка, буква: строка
возвращает число
пример «Две буквы a»
дано текст равно "abac"
дано буква равно "a"
ожидается 2
(длина (разделить текст по буква)) минус 1
тотальная функция «Подпись»
принимает слово: строка
возвращает строка
пример «Анаграммы дают одну подпись»
дано слово равно "eat"
ожидается "1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,"
пример «Другое слово — другая подпись»
дано слово равно "bat"
ожидается "1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,"
свёртка ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] начиная с "" как акк и буква
соединить (соединить акк с (к строке («Счёт буквы» от слово и буква))) с ","
тотальная функция «Есть группа»
принимает группы: список «Группа», ключ: строка
возвращает признак
свёртка группы начиная с нет как акк и группа
если акк равен да то да иначе группа.ключ равен ключ
тотальная функция «Добавить слово»
принимает группы: список «Группа», ключ: строка, слово: строка
возвращает список «Группа»
если «Есть группа» от группы и ключ
то
отобразить группы как группа
если группа.ключ равен ключ
то запись «Группа» с ключ равным ключ и слова равным (добавить слово к группа.слова)
иначе группа
иначе добавить (запись «Группа» с ключ равным ключ и слова равным [слово]) к группы
тотальная функция «Группировать анаграммы»
принимает слова: список строки
возвращает список список строки
пример «Пример 1 из условия»
дано слова равно ["eat", "tea", "tan", "ate", "nat", "bat"]
ожидается [["eat", "tea", "ate"], ["tan", "nat"], ["bat"]]
пример «Пример 2 из условия»
дано слова равно [""]
ожидается [[""]]
пример «Пример 3 из условия»
дано слова равно ["a"]
ожидается [["a"]]
пусть группы равно свёртка слова начиная с пустой список как акк и слово
«Добавить слово» от акк и («Подпись» от слово) и слово
отобразить группы как группа → группа.слова
125Палиндром
Valid Palindrome
доказательства нетстроки12/12 примеров3/7 функций тотальны98 строк
Нижний регистр — таблицей из двух алфавитов: встроенной формы «код символа» нет. Нетотальна из-за обхода символов; кириллица решением не покрыта.
flang check
модуль «Палиндром» проходит проверку типов. Главная функция «Палиндром» не тотальна: убывание не доказано, конечность держится на входных данных.
flang test
12 примеров в файле, сошлось 12, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Палиндром»
// LeetCode 125. Valid Palindrome.
// Строка считается палиндромом, если после выбрасывания всего, кроме букв и
// цифр, и приведения к нижнему регистру она читается одинаково в обе стороны.
//
// Обычная. Причина та же, что у остальных строковых задач: чтобы пройти по
// символам, нужна рекурсия по строке, а её анализ завершаемости не признаёт.
// Заметно и второе ограничение: нижний регистр приходится делать таблицей
// из двух алфавитов — встроенной формы «код символа» в языке нет, поэтому
// связать «A» и «a» иначе нечем. Кириллица этим решением не покрыта.
тотальная функция «Приписать строку в начало»
принимает первая: строка, элементы: список строки
возвращает список строки
пример «В непустой»
дано первая равно "a"
дано элементы равно ["b"]
ожидается ["a", "b"]
свёртка элементы начиная с [первая] как акк и эл → добавить эл к акк
тотальная функция «Позиция подстроки»
принимает текст: строка, часть: строка
возвращает число
пример «В середине»
дано текст равно "abcd"
дано часть равно "c"
ожидается 3
пример «Нет вхождения»
дано текст равно "abc"
дано часть равно "z"
ожидается 0
если текст содержит часть
то (длина (голова (разделить текст по часть))) плюс 1
иначе 0
тотальная функция «Значащий символ»
принимает буква: строка
возвращает строка
пример «Заглавная становится строчной»
дано буква равно "P"
ожидается "p"
пример «Цифра остаётся»
дано буква равно "7"
ожидается "7"
пример «Знак препинания выбрасывается»
дано буква равно ","
ожидается ""
пусть позиция равно «Позиция подстроки» от "ABCDEFGHIJKLMNOPQRSTUVWXYZ" и буква
если позиция больше 0
то подстрока "abcdefghijklmnopqrstuvwxyz" с позиция по позиция
иначе
если "abcdefghijklmnopqrstuvwxyz0123456789" содержит буква то буква иначе ""
функция «Символы с позиции»
принимает текст: строка, позиция: число
возвращает список строки
пример «С последнего символа»
дано текст равно "ab"
дано позиция равно 2
ожидается ["b"]
если позиция больше (длина текст)
то пустой список
иначе
пусть буква равно подстрока текст с позиция по позиция
«Приписать строку в начало» от буква и («Символы с позиции» от текст и (позиция плюс 1))
функция «Очистить»
принимает текст: строка
возвращает строка
пример «Знаки и регистр»
дано текст равно "A man, a plan"
ожидается "amanaplan"
свёртка («Символы с позиции» от текст и 1) начиная с "" как акк и буква
соединить акк с («Значащий символ» от буква)
функция «Обратить строку»
принимает текст: строка
возвращает строка
пример «Слово»
дано текст равно "abc"
ожидается "cba"
свёртка («Символы с позиции» от текст и 1) начиная с "" как акк и буква → соединить буква с акк
функция «Палиндром»
принимает текст: строка
возвращает признак
пример «Пример 1 из условия»
дано текст равно "A man, a plan, a canal: Panama"
ожидается да
пример «Пример 2 из условия»
дано текст равно "race a car"
ожидается нет
пример «Пример 3 из условия»
дано текст равно " "
ожидается да
пусть чистое равно «Очистить» от текст
чистое равен («Обратить строку» от чистое)
242Анаграмма
Valid Anagram
завершение доказаностроки5/5 примеров2/2 функций тотальны47 строк
Тотальная задача про строки: счёт буквы = число частей «разделить» минус один, перебор идёт по литеральному алфавиту из 26 букв, а не по символам слова.
flang check
модуль «Анаграмма» проходит проверку типов. Главная функция «Анаграмма» тотальна: компилятор принял доказательство завершения.
flang test
5 примеров в файле, сошлось 5, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Анаграмма»
// LeetCode 242. Valid Anagram.
// Проверить, что второе слово — перестановка первого. По условию буквы
// только строчные латинские.
//
// Тотальная — и это неожиданно, потому что задача про строки. Приём:
// считать буквы не обходом строки, а встроенной «разделить». Число вхождений
// буквы равно числу частей минус один, а перебирать надо не символы слова,
// а заранее известный алфавит из 26 букв — конечный список, записанный
// литералом. Свёртка по литеральному списку тотальна всегда.
тотальная функция «Счёт буквы»
принимает текст: строка, буква: строка
возвращает число
пример «Две буквы a»
дано текст равно "abac"
дано буква равно "a"
ожидается 2
пример «Буквы нет»
дано текст равно "bbb"
дано буква равно "a"
ожидается 0
(длина (разделить текст по буква)) минус 1
тотальная функция «Анаграмма»
принимает первое: строка, второе: строка
возвращает признак
пример «Пример 1 из условия»
дано первое равно "anagram"
дано второе равно "nagaram"
ожидается да
пример «Пример 2 из условия»
дано первое равно "rat"
дано второе равно "car"
ожидается нет
пример «Разная длина»
дано первое равно "a"
дано второе равно "ab"
ожидается нет
если (длина первое) не равен (длина второе)
то нет
иначе
свёртка ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] начиная с да как акк и буква
если акк равен нет
то нет
иначе («Счёт буквы» от первое и буква) равен («Счёт буквы» от второе и буква)
53Максимальная подпоследовательность
Maximum Subarray
завершение доказанодинамика4/4 примеров1/1 функций тотальны41 строк
Кадане: состояние из двух чисел укладывается в запись, запись — в накопитель свёртки. Единственная динамика, которая ложится на язык без сопротивления.
flang check
модуль «Максимальная подпоследовательность» проходит проверку типов, объявлено типов: 1. Главная функция «Наибольшая сумма куска» тотальна: компилятор принял доказательство завершения.
flang test
4 примеров в файле, сошлось 4, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Максимальная подпоследовательность»
// LeetCode 53. Maximum Subarray.
// Найти наибольшую сумму непрерывного куска списка (алгоритм Кадане).
//
// Тотальная. Это самая «динамическая» из задач, которые язык принимает без
// сопротивления: состояние Кадане — два числа, а два числа складываются в
// запись, и запись отлично живёт накопителем свёртки. Настоящая динамика
// с таблицей (Coin Change, Edit Distance) так не переносится — там нужен
// массив с произвольным доступом по индексу, которого в языке нет.
объект «Состояние Кадане»
текущая является числом
лучшая является числом
тотальная функция «Наибольшая сумма куска»
принимает элементы: список числа
возвращает число
пример «Пример 1 из условия»
дано элементы равно [-2, 1, -3, 4, -1, 2, 1, -5, 4]
ожидается 6
пример «Пример 2 из условия»
дано элементы равно [1]
ожидается 1
пример «Пример 3 из условия»
дано элементы равно [5, 4, -1, 7, 8]
ожидается 23
пример «Пустой список»
дано элементы равно пустой список
ожидается 0
разбор элементов
случай пусто
то 0
случай голова и хвост
пусть начальное равно запись «Состояние Кадане» с текущая равным голова и лучшая равным голова
пусть итог равно свёртка хвост начиная с начальное как акк и эл
пусть продолженная равно акк.текущая плюс эл
пусть текущая равно если продолженная больше эл то продолженная иначе эл
пусть лучшая равно если текущая больше акк.лучшая то текущая иначе акк.лучшая
запись «Состояние Кадане» с текущая равным текущая и лучшая равным лучшая
итог.лучшая
70Ступени
Climbing Stairs
доказательства нетдинамика5/5 примеров0/2 функций тотальны45 строк
Линейный проход с двумя накопителями. Нетотальна: счётчик «осталось минус 1» — результат арифметики, а не часть значения.
flang check
модуль «Ступени» проходит проверку типов. Главная функция «Ступени» не тотальна: убывание не доказано, конечность держится на входных данных.
flang test
5 примеров в файле, сошлось 5, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Ступени»
// LeetCode 70. Climbing Stairs.
// Сколькими способами подняться на n ступеней шагами по одной и по две.
//
// Обычная. Ответ — число Фибоначчи, и считается он за один проход с двумя
// накопителями; тотальной функция всё равно не становится, потому что
// «осталось минус 1» не является частью значения «осталось». Это та же
// граница, что в модуле stdlib/numbers.flang: рекурсия по числу анализу
// недоступна в принципе, ей нужна мера, а мер он не знает.
//
// Обход существует: передать список из n элементов и рекурсировать по нему.
// Но список из n элементов сам по себе строится рекурсией по n — то есть
// обычной функцией, и тотальность не спасена, а лишь передвинута.
функция «Ступени шагом»
принимает осталось: число, предыдущее: число, текущее: число
возвращает число
пример «Один шаг»
дано осталось равно 1
дано предыдущее равно 1
дано текущее равно 1
ожидается 2
если осталось не больше 0
то текущее
иначе «Ступени шагом» от (осталось минус 1) и текущее и (предыдущее плюс текущее)
функция «Ступени»
принимает н: число
возвращает число
пример «Пример 1 из условия»
дано н равно 2
ожидается 2
пример «Пример 2 из условия»
дано н равно 3
ожидается 3
пример «Одна ступень»
дано н равно 1
ожидается 1
пример «Десять ступеней»
дано н равно 10
ожидается 89
если н не больше 1
то 1
иначе «Ступени шагом» от (н минус 1) и 1 и 1
121Лучшая сделка
Best Time to Buy and Sell Stock
завершение доказанодинамика3/3 примеров1/1 функций тотальны36 строк
Один проход свёрткой, состояние — запись «минимальная цена, лучшая прибыль».
flang check
модуль «Лучшая сделка» проходит проверку типов, объявлено типов: 1. Главная функция «Лучшая прибыль» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Лучшая сделка»
// LeetCode 121. Best Time to Buy and Sell Stock.
// По списку цен по дням найти наибольшую прибыль от одной покупки и одной
// последующей продажи. Если прибыли нет — ноль.
//
// Тотальная: один проход свёрткой, состояние — запись из двух чисел
// (минимальная цена и лучшая прибыль).
объект «Сделка»
минимум является числом
прибыль является числом
тотальная функция «Лучшая прибыль»
принимает цены: список числа
возвращает число
пример «Пример 1 из условия»
дано цены равно [7, 1, 5, 3, 6, 4]
ожидается 5
пример «Пример 2 из условия»
дано цены равно [7, 6, 4, 3, 1]
ожидается 0
пример «Пустой список»
дано цены равно пустой список
ожидается 0
разбор цены
случай пусто
то 0
случай голова и хвост
пусть начальное равно запись «Сделка» с минимум равным голова и прибыль равным 0
пусть итог равно свёртка хвост начиная с начальное как акк и цена
пусть минимум равно если цена меньше акк.минимум то цена иначе акк.минимум
пусть сегодня равно цена минус акк.минимум
пусть прибыль равно если сегодня больше акк.прибыль то сегодня иначе акк.прибыль
запись «Сделка» с минимум равным минимум и прибыль равным прибыль
итог.прибыль
110Сбалансированное дерево
Balanced Binary Tree
завершение доказанодеревья3/3 примеров5/5 функций тотальны76 строк
Конъюнкция трёх условий записана вложенными «если»: логических «и» и «или» в языке нет.
flang check
модуль «Сбалансированное дерево» проходит проверку типов, объявлено типов: 1. Главная функция «Сбалансировано для списка» тотальна: компилятор принял доказательство завершения.
flang test
3 примеров в файле, сошлось 3, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Сбалансированное дерево»
// LeetCode 110. Balanced Binary Tree.
// Проверить, что для каждого узла глубины поддеревьев отличаются не более
// чем на единицу.
//
// Тотальная. Логических «и» и «или» в языке нет (SPEC перечисляет только
// арифметику и сравнения), поэтому конъюнкция трёх условий записана
// вложенными «если» — читается длиннее, но означает ровно то же.
тип «Дерево»
вариант Лист
вариант Узел содержит значение: число, левое: «Дерево», правое: «Дерево»
тотальная функция «Вставить в дерево»
принимает дерево: «Дерево», число: число
возвращает «Дерево»
разбор дерева
случай Лист
то Узел с значение равным число и левое равным вариант Лист и правое равным вариант Лист
случай вариант Узел с значение как зн и левое как л и правое как п
если число меньше зн
то
пусть новое равно «Вставить в дерево» от л и число
Узел с значение равным зн и левое равным новое и правое равным п
иначе
пусть новое равно «Вставить в дерево» от п и число
Узел с значение равным зн и левое равным л и правое равным новое
тотальная функция «Дерево из списка»
принимает элементы: список числа
возвращает «Дерево»
свёртка элементы начиная с вариант Лист как акк и эл → «Вставить в дерево» от акк и эл
тотальная функция «Глубина»
принимает дерево: «Дерево»
возвращает число
разбор дерева
случай Лист
то 0
случай вариант Узел с левое как л и правое как п
пусть слева равно «Глубина» от л
пусть справа равно «Глубина» от п
1 плюс (если слева больше справа то слева иначе справа)
тотальная функция «Сбалансировано»
принимает дерево: «Дерево»
возвращает признак
разбор дерева
случай Лист
то да
случай вариант Узел с левое как л и правое как п
если («Сбалансировано» от л) равен нет
то нет
иначе
если («Сбалансировано» от п) равен нет
то нет
иначе
пусть разница равно («Глубина» от л) минус («Глубина» от п)
если разница меньше 0
то (0 минус разница) не больше 1
иначе разница не больше 1
тотальная функция «Сбалансировано для списка»
принимает элементы: список числа
возвращает признак
пример «Сбалансированное дерево»
дано элементы равно [2, 1, 3]
ожидается да
пример «Цепочка не сбалансирована»
дано элементы равно [1, 2, 3, 4]
ожидается нет
пример «Пустое дерево сбалансировано»
дано элементы равно пустой список
ожидается да
«Сбалансировано» от («Дерево из списка» от элементы)
226Развернуть дерево
Invert Binary Tree
завершение доказанодеревья5/5 примеров7/7 функций тотальны88 строк
Само «Развернуть» примера иметь не может — оно возвращает вариант. Проверяется наблюдаемое следствие: обход по порядку до и после разворота.
flang check
модуль «Развернуть дерево» проходит проверку типов, объявлено типов: 1. Главная функция «Обход после разворота» тотальна: компилятор принял доказательство завершения.
flang test
5 примеров в файле, сошлось 5, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Развернуть дерево»
// LeetCode 226. Invert Binary Tree.
// Поменять местами левое и правое поддерево у каждого узла.
//
// Тотальная. Само «Развернуть» примера иметь не может: оно возвращает
// вариант, а значение варианта в «ожидается» не записывается — парсер
// разворачивает конструктор в запись из его полей и теряет имя варианта.
// Поэтому проверяется наблюдаемое следствие: обход по возрастанию до и после
// разворота. У дерева поиска обход по порядку отсортирован, у развёрнутого —
// отсортирован в обратную сторону.
тип «Дерево»
вариант Лист
вариант Узел содержит значение: число, левое: «Дерево», правое: «Дерево»
тотальная функция «Соединить списки»
принимает первый: список числа, второй: список числа
возвращает список числа
пример «Склейка»
дано первый равно [1]
дано второй равно [2, 3]
ожидается [1, 2, 3]
свёртка второй начиная с первый как акк и эл → добавить эл к акк
тотальная функция «Вставить в дерево»
принимает дерево: «Дерево», число: число
возвращает «Дерево»
разбор дерева
случай Лист
то Узел с значение равным число и левое равным вариант Лист и правое равным вариант Лист
случай вариант Узел с значение как зн и левое как л и правое как п
если число меньше зн
то
пусть новое равно «Вставить в дерево» от л и число
Узел с значение равным зн и левое равным новое и правое равным п
иначе
пусть новое равно «Вставить в дерево» от п и число
Узел с значение равным зн и левое равным л и правое равным новое
тотальная функция «Дерево из списка»
принимает элементы: список числа
возвращает «Дерево»
свёртка элементы начиная с вариант Лист как акк и эл → «Вставить в дерево» от акк и эл
тотальная функция «Развернуть»
принимает дерево: «Дерево»
возвращает «Дерево»
разбор дерева
случай Лист
то вариант Лист
случай вариант Узел с значение как зн и левое как л и правое как п
пусть слева равно «Развернуть» от п
пусть справа равно «Развернуть» от л
Узел с значение равным зн и левое равным слева и правое равным справа
тотальная функция «Обход по порядку»
принимает дерево: «Дерево»
возвращает список числа
разбор дерева
случай Лист
то пустой список
случай вариант Узел с значение как зн и левое как л и правое как п
пусть слева равно «Обход по порядку» от л
пусть справа равно «Обход по порядку» от п
«Соединить списки» от (добавить зн к слева) и справа
тотальная функция «Обход дерева из списка»
принимает элементы: список числа
возвращает список числа
пример «Дерево поиска обходится по возрастанию»
дано элементы равно [4, 2, 7, 1, 3, 6, 9]
ожидается [1, 2, 3, 4, 6, 7, 9]
«Обход по порядку» от («Дерево из списка» от элементы)
тотальная функция «Обход после разворота»
принимает элементы: список числа
возвращает список числа
пример «Пример 1 из условия»
дано элементы равно [4, 2, 7, 1, 3, 6, 9]
ожидается [9, 7, 6, 4, 3, 2, 1]
пример «Пример 2 из условия»
дано элементы равно [2, 1, 3]
ожидается [3, 2, 1]
пример «Пустое дерево»
дано элементы равно пустой список
ожидается пустой список
«Обход по порядку» от («Развернуть» от («Дерево из списка» от элементы))
704Двоичный поиск
Binary Search
завершение доказанопоиск5/5 примеров3/3 функций тотальны75 строк
Приём «топливо»: рядом с настоящими аргументами едет список, у которого на каждом шаге берётся хвост. Топливо убывает структурно, значит цикл доказуемо конечен.
flang check
модуль «Двоичный поиск» проходит проверку типов. Главная функция «Двоичный поиск» тотальна: компилятор принял доказательство завершения.
flang test
5 примеров в файле, сошлось 5, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Двоичный поиск»
// LeetCode 704. Binary Search.
// В отсортированном списке найти номер цели (с нуля) или −1.
//
// Тотальная — но приёмом, который стоит запомнить, потому что он показывает
// границу анализа завершаемости. Двоичный поиск сужает пару чисел «низ» и
// «верх», а убывание по числам totality.mjs не признаёт: «середина плюс 1» —
// результат арифметики, а не часть значения. Прямая запись даёт
// FLANG_NOT_TOTAL.
//
// Обход: рядом с настоящими аргументами едет «топливо» — список, у которого
// на каждом шаге берётся хвост. Топливо убывает структурно, значит цикл
// доказуемо конечен; а поскольку топливом служит сам список, шагов заведомо
// хватает (их нужно log₂n, а есть n). Приём честный, но это именно приём:
// в языке не хватает убывания по мере.
тотальная функция «Элемент»
принимает элементы: список числа, номер: число
возвращает число
пример «Второй элемент»
дано элементы равно [10, 20, 30]
дано номер равно 2
ожидается 20
разбор элементов
случай пусто
то 0
случай голова и хвост
если номер не больше 1
то голова
иначе «Элемент» от хвост и (номер минус 1)
тотальная функция «Поиск в диапазоне»
принимает топливо: список числа, элементы: список числа, цель: число, низ: число, верх: число
возвращает число
пример «Нашли середину»
дано топливо равно [1, 2, 3]
дано элементы равно [1, 2, 3]
дано цель равно 2
дано низ равно 1
дано верх равно 3
ожидается 1
разбор топливо
случай пусто
то -1
случай голова и хвост
если низ больше верх
то -1
иначе
пусть сумма равно низ плюс верх
пусть середина равно (сумма минус (сумма остаток от 2)) делить на 2
пусть значение равно «Элемент» от элементы и середина
если значение равен цель
то середина минус 1
иначе
если значение меньше цель
то «Поиск в диапазоне» от хвост и элементы и цель и (середина плюс 1) и верх
иначе «Поиск в диапазоне» от хвост и элементы и цель и низ и (середина минус 1)
тотальная функция «Двоичный поиск»
принимает элементы: список числа, цель: число
возвращает число
пример «Пример 1 из условия»
дано элементы равно [-1, 0, 3, 5, 9, 12]
дано цель равно 9
ожидается 4
пример «Пример 2 из условия»
дано элементы равно [-1, 0, 3, 5, 9, 12]
дано цель равно 2
ожидается -1
пример «Один элемент»
дано элементы равно [5]
дано цель равно 5
ожидается 0
«Поиск в диапазоне» от элементы и элементы и цель и 1 и (длина элементы)
268Пропущенное число
Missing Number
завершение доказаночисла4/4 примеров2/2 функций тотальны32 строк
Сумма прогрессии минус сумма списка. Единственная задача набора, где язык не мешает ничем.
flang check
модуль «Пропущенное число» проходит проверку типов. Главная функция «Пропущенное число» тотальна: компилятор принял доказательство завершения.
flang test
4 примеров в файле, сошлось 4, разошлось 0. Примеры — часть исходника, а не отдельный тестовый файл.
модуль «Пропущенное число»
// LeetCode 268. Missing Number.
// В списке лежат все числа от 0 до n, кроме одного. Найти пропущенное.
//
// Тотальная и без рекурсии: сумма арифметической прогрессии минус сумма
// списка. Здесь язык не мешает вовсе — вся задача выражается двумя свёртками
// и арифметикой.
тотальная функция «Сумма»
принимает элементы: список числа
возвращает число
пример «Сумма трёх»
дано элементы равно [1, 2, 3]
ожидается 6
свёртка элементы начиная с 0 как акк и эл → акк плюс эл
тотальная функция «Пропущенное число»
принимает элементы: список числа
возвращает число
пример «Пример 1 из условия»
дано элементы равно [3, 0, 1]
ожидается 2
пример «Пример 2 из условия»
дано элементы равно [0, 1]
ожидается 2
пример «Пример 3 из условия»
дано элементы равно [9, 6, 4, 2, 3, 5, 7, 0, 1]
ожидается 8
пусть предел равно длина элементы
пусть полная равно (предел умножить на (предел плюс 1)) делить на 2
полная минус («Сумма» от элементы)
Рабочие примеры
Сервис, допуск агента, кодогенерация, форма и готовый CI.
Файлы лежат в репозитории курса, покрыты тестами и запускаются одной командой: npm run fts:examples. Ниже их настоящее содержимое.
Скидка как HTTP-сервис
Node.js отвечает за сокет и коды ответа, FTS — за правило. Примеры модели выполняются на старте: не прошёл предметный тест — сервис не поднялся.
/**
* Допуск команды «отгрузить заказ» через доказательство на реальных данных.
*
* Это шаблон для агентских сценариев: модель (или человек) формулирует
* утверждение на FTS, а решение принимает не текст ответа, а движок. Если факт
* в данных не подтверждается, prove() отклоняет теорему и команда не выполняется.
* Эффект (реальная отгрузка) живёт снаружи и вызывается только после allow.
*
* Запуск: node examples/fts/shipment-guard/guard.mjs ЗК-7781
* node examples/fts/shipment-guard/guard.mjs ЗК-7781 --blocked
*/
import { readFile } from 'node:fs/promises';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { resolve } from 'node:path';
import { compile, prove } from '../../../static/js/vendor/fts/browser.js';
const here = fileURLToPath(new URL('.', import.meta.url));
const models = resolve(here, '../../../static/fts/models');
/* Кавычки внутри имени сломали бы разбор, поэтому номер заказа проверяется
до подстановки: в теорему попадают только допустимые идентификаторы. */
const ORDER_NUMBER = /^[\p{L}\p{N}-]{1,32}$/u;
export function shipmentTheorem(orderNumber) {
if (!ORDER_NUMBER.test(orderNumber)) throw new Error(`недопустимый номер заказа: ${orderNumber}`);
return `категория «Исполнение заказа»
объект Заказ
номер является строкой
клиент является строкой
оплачен является признаком
«склад подтвердил» является признаком
«готов к отгрузке» является состоянием «Готов к отгрузке»
морфизм «Готовый заказ можно отгрузить»
если «Готов к отгрузке»
то «Отгрузить заказ разрешено»
теорема «Заказ ${orderNumber} можно отгрузить»
дано Заказ имеет «готов к отгрузке» равное да
в данных заказы найти где номер равен «${orderNumber}»
по морфизму «Готовый заказ можно отгрузить»
следовательно «Отгрузить заказ разрешено»
`;
}
export function decideShipment(orderNumber, context) {
const source = shipmentTheorem(orderNumber);
try {
const proof = prove(compile(source), context);
return { allowed: true, source, proof };
} catch (error) {
return {
allowed: false,
source,
reason: error instanceof Error ? error.message : String(error),
diagnostics: error?.diagnostics ?? [],
};
}
}
export async function loadContext(blocked = false) {
const file = resolve(models, blocked ? 'order-shipment.blocked.context.json' : 'order-shipment.context.json');
return JSON.parse(await readFile(file, 'utf8'));
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const orderNumber = process.argv[2] ?? 'ЗК-7781';
const decision = decideShipment(orderNumber, await loadContext(process.argv.includes('--blocked')));
process.stdout.write(`${JSON.stringify(decision, null, 2)}\n`);
/* Ненулевой код возврата делает guard пригодным для shell и CI. */
process.exit(decision.allowed ? 0 : 1);
}
Сгенерированный код не расходится с моделью
Из модели получаются реализация и тесты на node:test. Режим --check роняет сборку, если правило поправили в TypeScript мимо модели.
// Generated by FTS. Do not edit by hand.
export interface FtsInput0 {
"сумма": number
"постоянный клиент": boolean
}
export const ftsUtilities = {
"Рассчитать скидку": (input: FtsInput0): number => {
let result: number = 0
if (input["сумма"] >= 10000 && input["сумма"] <= 100000) {
result += (10 / 100) * input["сумма"]
}
if (input["постоянный клиент"] === true && input["сумма"] > 0 && input["сумма"] <= 100000) {
result += (5 / 100) * input["сумма"]
}
if (input["сумма"] > 100000) {
result += 15000
}
if (!(result <= 15000)) throw new Error("Нарушено свойство «Скидка ограничена»")
return result
},
} as const
Форма знает поля, потому что их объявила модель
Схема формы выводится из объекта FTS: контрол, тип, обязательность. Компонент React не дублирует контракт, который уже проверяет бэкенд.
Схема
node examples/fts/form-schema/schema.mjs Покупка
Необязательность
иногда является отображается в required: false
examples/fts/react-form/DiscountForm.jsx
/**
* Форма скидки на React.
*
* Компонент не знает, что покупка состоит из суммы и признака постоянного
* клиента: поля приходят из той же FTS-модели, которую проверяет бэкенд.
* Добавили в модель поле — форма получила контрол без правки JSX.
*
* Предпросчёт скидки выполняется прямо в браузере тем же компилятором, поэтому
* пользователь видит результат без сетевого запроса. Сервер всё равно считает
* заново: браузеру доверять нельзя, но и ждать его не нужно.
*/
import { useMemo, useState } from 'react';
import { compile, executeUtility } from '@digitable-lol/fts/browser';
import { ftsFormSchema } from '../form-schema/schema.mjs';
export function DiscountForm({ source, objectName = 'Покупка', utility = 'Рассчитать скидку', onSubmit }) {
const document = useMemo(() => compile(source), [source]);
const schema = useMemo(() => ftsFormSchema(document, objectName), [document, objectName]);
const [value, setValue] = useState(() =>
Object.fromEntries(schema.fields.map((field) => [field.name, field.control === 'checkbox' ? false : ''])),
);
const preview = useMemo(() => {
try {
const input = Object.fromEntries(
schema.fields.map((field) => [
field.name,
field.control === 'checkbox' ? Boolean(value[field.name]) : Number(value[field.name] || 0),
]),
);
return { ok: true, amount: executeUtility(document, utility, input) };
} catch (error) {
/* Нарушенное свойство модели — это нормальный ответ формы, а не падение UI. */
return { ok: false, reason: error.message };
}
}, [document, schema, utility, value]);
return (
<form
onSubmit={(event) => {
event.preventDefault();
onSubmit?.(value);
}}
>
<h2>{schema.title}</h2>
{schema.fields.map((field) => (
<label key={field.name}>
<span>{field.label}</span>
<input
type={field.control === 'checkbox' ? 'checkbox' : field.control === 'text' ? 'text' : 'number'}
required={field.required}
checked={field.control === 'checkbox' ? Boolean(value[field.name]) : undefined}
value={field.control === 'checkbox' ? undefined : value[field.name]}
onChange={(event) =>
setValue((current) => ({
...current,
[field.name]: field.control === 'checkbox' ? event.target.checked : event.target.value,
}))
}
/>
</label>
))}
<output>
{preview.ok ? `Скидка: ${preview.amount} ₽` : `Правило не выполняется: ${preview.reason}`}
</output>
<button type="submit" disabled={!preview.ok}>
Оформить
</button>
</form>
);
}
Предметные правила как обычный шаг CI
Проверка модели, выполнение примеров, сверка сгенерированного кода и тесты интеграций — четыре шага, которые ловят расхождение текста правила и кода.
Файл
.github/workflows/fts-check.yml
Локально
npm run fts:examples
examples/fts/ci/fts-check.yml
# Предметные правила как обычный шаг CI.
#
# Смысл шага не в «ещё одном линтере»: примеры внутри .fts — это тесты,
# написанные на языке предметной области, а сгенерированный TypeScript
# сверяется с моделью. Правило, изменённое в коде мимо модели, ломает сборку.
#
# Файл кладётся в .github/workflows/fts-check.yml
name: fts
on:
push:
branches: [master]
pull_request:
jobs:
rules:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
# Модель разобралась, типы сошлись, ссылки на объекты и поля существуют.
- name: Проверить модели
run: npx fts check static/fts/models/order-discount.fts
# Примеры из .fts выполняются как предметные unit-тесты.
- name: Выполнить предметные примеры
run: npx fts test static/fts/models/order-discount.fts --pretty
# Сгенерированный TypeScript обязан совпадать с закоммиченным.
- name: Сверить сгенерированный код
run: node examples/fts/typescript-codegen/generate.mjs --check
# Интеграции: HTTP-сервис, guard допуска команды, схема формы.
- name: Тесты интеграций
run: node --test "examples/fts/**/*.test.mjs"
Где проходит граница
FTS не заменяет React, Node.js или Python — он отдаёт им решение, посчитанное на снимке данных.
Внешних эффектов у языка нет вовсе: ни HTTP, ни SQL, ни оплаты. Поэтому интеграция у всех стеков одна и та же и состоит из четырёх шагов, из которых язык занимает ровно один.
Приложение читает данныеБаза, HTTP, очередь — всё, что умеет ваш стек. Язык в этом не участвует и знать о нём не должен.
FTS считает решениеПо переданному снимку и без обращений наружу: одни и те же входы всегда дают один и тот же ответ.
Приложение проверяет результатНарушено свойство или разошёлся пример — придёт диагностика с кодом (FTS_UTILITY_PROPERTY) и ненулевой код выхода, а не тихое «безопасное» число.
Приложение выполняет эффектСписать деньги, отгрузить заказ, разрешить команду агенту — это делает ваш код, уже зная ответ и его обоснование.
Каждый кейс называет входные факты, роль FTS, выходной артефакт и место интеграции.
РитейлMiddle
Рассчитать скидку корзины
Retail: deterministic calculation
ВходСумма корзины, сегмент и промокод
FTSСложить применимые проценты и проверить верхний предел
На выходеЧисловая скидка без скрытых веток
РитейлSenior
Разрешить возврат товара
Retail: command eligibility
ВходПокупка, срок и состояние товара
FTSВывести право на возврат из проверяемых фактов
На выходеКоманда «Оформить возврат» разрешена или отклонена
РитейлQA / Middle
Проверить бесплатную доставку
Retail: executable examples
ВходСумма, регион и вес
FTSВыполнить примеры на границах тарифа
На выходеРегрессия тарифа блокирует сборку
РитейлJunior
Собрать форму товарной карточки
Retail: generated form
ВходСтруктура товара и обязательность полей
FTSСкомпилировать поля в каноническую модель
На выходеОписание React-формы
РитейлJunior / Middle
Собрать таблицу остатков
Retail: generated table
ВходSKU, склад, остаток и резерв
FTSПревратить типы полей в колонки
На выходеКонфигурация таблицы без ручного дублирования
РитейлSenior / Lead
Доказать готовность заказа к выдаче
Retail: verified transition
ВходОплата и факт комплектации
FTSСкомпозировать состояния в разрешение выдачи
На выходеПроверяемый сертификат перехода
РитейлAI agent
Дать агенту расчёт цены
Retail: agent tool
ВходЗапрос покупателя и снимок корзины
FTSВызвать детерминированную FTS-утилиту через MCP
На выходеСтруктурированный ответ с диагностикой
РитейлTeam
Защитить правила промокодов
Retail: regression gate
ВходНабор граничных примеров
FTSЗапустить примеры и свойства в CI
На выходеНенулевой exit code при расхождении
МаркетплейсыMiddle
Рассчитать комиссию продавца
Marketplaces: deterministic calculation
ВходКатегория, оборот и тариф
FTSПрименить последовательность комиссионных правил
На выходеСумма комиссии и сгенерированный тест
МаркетплейсыSenior
Разрешить публикацию товара
Marketplaces: command eligibility
ВходКарточка, документы и ограничения
FTSПроверить свидетельства допуска
На выходеКоманда публикации с объяснимым guard
МаркетплейсыQA / Middle
Проверить SLA отгрузки
Marketplaces: executable examples
ВходДедлайн, статус и схема FBO/FBS
FTSЗафиксировать примеры нарушения SLA
На выходеВоспроизводимый тест бизнес-сроков
МаркетплейсыJunior
Собрать форму нового SKU
Marketplaces: generated form
ВходАтрибуты категории товара
FTSПолучить поля из одной структуры FTS
На выходеДинамическая форма кабинета
МаркетплейсыJunior / Middle
Собрать таблицу выплат
Marketplaces: generated table
ВходПериод, продажи, удержания, итог
FTSСопоставить типы с форматтерами колонок
На выходеТаблица сверки выплат
МаркетплейсыSenior / Lead
Доказать право на выплату
Marketplaces: verified transition
ВходДоставка, период спора и KYC
FTSСвязать факты морфизмами допуска
На выходеСертификат разрешения выплаты
МаркетплейсыAI agent
Дать агенту проверку листинга
Marketplaces: agent tool
ВходТекст карточки и факты продавца
FTSПередать исходник в fts_check и fts_execute
На выходеРешение агента, ограниченное валидатором
МаркетплейсыTeam
Защитить сетку комиссий
Marketplaces: regression gate
ВходПримеры по категориям и порогам
FTSГенерировать node:test из FTS
На выходеСтабильный релиз тарификации
БанкингMiddle
Рассчитать кредитный лимит
Banking: deterministic calculation
ВходДоход, нагрузка и риск-класс
FTSВычислить лимит и проверить максимум
На выходеДетерминированный лимит
БанкингSenior
Разрешить выдачу кредита
Banking: command eligibility
ВходKYC, скоринг и риск-проверка
FTSСкомпозировать свидетельства допуска
На выходеGuard команды выдачи
БанкингQA / Middle
Проверить пороги скоринга
Banking: executable examples
ВходГраничные баллы и ожидаемые классы
FTSИсполнить предметные примеры
На выходеТесты скоринговой политики
БанкингJunior
Собрать форму заявки
Banking: generated form
ВходПоля клиента и финансов
FTSСкомпилировать структуру заявки
На выходеReact-форма с типами
БанкингJunior / Middle
Собрать таблицу просрочек
Banking: generated table
ВходДоговор, DPD и остаток долга
FTSПолучить колонки из структуры
На выходеОперационная таблица коллекшна
БанкингSenior / Lead
Доказать допустимость операции
Banking: verified transition
ВходЛимит, санкционный контроль и MFA
FTSПровести типизированную цепочку морфизмов
На выходеПроверяемый сертификат операции
БанкингAI agent
Дать агенту pre-check заявки
Banking: agent tool
ВходАнкета без сетевых секретов
FTSВызвать read-only MCP-инструменты FTS
На выходеСтруктурированный pre-check
БанкингTeam
Защитить риск-политику
Banking: regression gate
ВходНабор одобренных и отказных заявок
FTSПроверить свойства и примеры в CI
На выходеЗапрет мержа при изменении решения
ПлатежиMiddle
Рассчитать комиссию платежа
Payments: deterministic calculation
ВходМетод, валюта и сумма
FTSПрименить тарифные правила по порядку
На выходеКомиссия для checkout
ПлатежиSenior
Разрешить списание
Payments: command eligibility
Вход3DS, лимит и статус получателя
FTSПроверить необходимые свидетельства
На выходеGuard команды debit
ПлатежиQA / Middle
Проверить округление валют
Payments: executable examples
ВходСуммы на границах minor units
FTSИсполнить точные числовые примеры
На выходеРегресс-тест округления
ПлатежиJunior
Собрать форму возврата
Payments: generated form
ВходПлатёж, причина и сумма
FTSПреобразовать структуру в descriptor
На выходеФорма refund в React
ПлатежиJunior / Middle
Собрать таблицу сверки
Payments: generated table
ВходТранзакция, провайдер и расхождение
FTSВывести колонки и формат денег
На выходеТаблица reconciliation
ПлатежиSenior / Lead
Доказать право на capture
Payments: verified transition
ВходАвторизация, срок и антифрод
FTSСкомпозировать факты в разрешение
На выходеСертификат capture
ПлатежиAI agent
Дать агенту расчёт refund
Payments: agent tool
ВходСнимок платежа и политика
FTSИсполнить FTS без доступа к платёжному API
На выходеБезопасная рекомендация до эффекта
ПлатежиTeam
Защитить антифрод-порог
Payments: regression gate
ВходПоложительные и отрицательные примеры
FTSЗапустить fts test перед деплоем
На выходеCI-блокировка рискованного изменения
СтрахованиеMiddle
Рассчитать страховую премию
Insurance: deterministic calculation
ВходОбъект, риск и франшиза
FTSСложить коэффициенты и проверить предел
На выходеПремия полиса
СтрахованиеSenior
Разрешить страховую выплату
Insurance: command eligibility
ВходПокрытие, событие и документы
FTSВывести право выплаты из фактов
На выходеGuard команды payout
СтрахованиеQA / Middle
Проверить исключения покрытия
Insurance: executable examples
ВходНабор событий и условий
FTSИсполнить примеры полиса
На выходеТесты исключений
СтрахованиеJunior
Собрать форму заявления
Insurance: generated form
ВходСтруктура страхового случая
FTSСгенерировать поля из FTS
На выходеФорма FNOL
СтрахованиеJunior / Middle
Собрать таблицу убытков
Insurance: generated table
ВходПолис, резерв и статус
FTSПреобразовать типы в колонки
На выходеClaims dashboard table
СтрахованиеSenior / Lead
Доказать завершение урегулирования
Insurance: verified transition
ВходЭкспертиза, согласование и платёж
FTSСкомпозировать переходы статуса
На выходеСертификат закрытия кейса
СтрахованиеAI agent
Дать агенту проверку покрытия
Insurance: agent tool
ВходУсловия полиса и событие
FTSВызвать FTS через MCP
На выходеОбъяснимый результат без галлюцинации правила
СтрахованиеTeam
Защитить тарифную модель
Insurance: regression gate
ВходЭталонные профили риска
FTSСгенерировать тесты и запустить свойства
На выходеКонтроль изменения премий
ЛогистикаMiddle
Рассчитать стоимость доставки
Logistics: deterministic calculation
ВходМаршрут, вес и сервис
FTSПрименить тарифы и надбавки
На выходеЦена перевозки
ЛогистикаSenior
Разрешить отгрузку заказа
Logistics: command eligibility
ВходКомплектация, оплата и маркировка
FTSВывести разрешение команды
На выходеShipping command guard
ЛогистикаQA / Middle
Проверить сроки доставки
Logistics: executable examples
ВходЗоны, cut-off и календарные примеры
FTSИсполнить SLA-сценарии
На выходеТест обещанной даты
ЛогистикаJunior
Собрать форму отправления
Logistics: generated form
ВходАдрес, места и опасный груз
FTSСкомпилировать структуру
На выходеФорма создания shipment
ЛогистикаJunior / Middle
Собрать таблицу рейсов
Logistics: generated table
ВходРейс, окно и загрузка
FTSПолучить колонки из типов
На выходеDispatch table
ЛогистикаSenior / Lead
Доказать готовность груза
Logistics: verified transition
ВходДокументы, упаковка и слот
FTSСкомпозировать состояния
На выходеСертификат допуска на погрузку
ЛогистикаAI agent
Дать агенту выбор тарифа
Logistics: agent tool
ВходПараметры посылки
FTSИсполнить утилиту через MCP
На выходеСтруктурированный тарифный ответ
ЛогистикаTeam
Защитить правила зон
Logistics: regression gate
ВходЭталонные адреса и веса
FTSПрогнать примеры в CI
На выходеКонтроль тарифных регрессий
ЗдравоохранениеMiddle
Рассчитать приоритет записи
Healthcare: deterministic calculation
ВходСимптомы, возраст и срочность
FTSПрименить утверждённые правила приоритета
На выходеКатегория очереди, не диагноз
ЗдравоохранениеSenior
Разрешить доступ к карте
Healthcare: command eligibility
ВходРоль, согласие и цель
FTSПроверить факты доступа
На выходеGuard чтения медицинских данных
ЗдравоохранениеQA / Middle
Проверить расписание приёма
Healthcare: executable examples
ВходДлительность и ограничения слотов
FTSИсполнить примеры календаря
На выходеТест конфликтов записи
ЗдравоохранениеJunior
Собрать форму анамнеза
Healthcare: generated form
ВходСтруктура анкеты пациента
FTSСкомпилировать обязательные поля
На выходеДоступная React-форма
ЗдравоохранениеJunior / Middle
Собрать таблицу назначений
Healthcare: generated table
ВходПрепарат, доза и период
FTSПолучить безопасные колонки
На выходеТаблица назначений
ЗдравоохранениеSenior / Lead
Доказать наличие согласия
Healthcare: verified transition
ВходВерсия согласия, подпись и срок
FTSСвязать свидетельства морфизмами
На выходеСертификат правового основания
ЗдравоохранениеAI agent
Дать агенту проверку анкеты
Healthcare: agent tool
ВходЗаполненные поля без постановки диагноза
FTSВызвать fts_check и fts_test
На выходеДиагностика структуры данных
ЗдравоохранениеTeam
Защитить правила доступа
Healthcare: regression gate
ВходМатрица ролей и согласий
FTSЗапускать примеры в CI
На выходеРегресс-гейт приватности
ФармацевтикаMiddle
Рассчитать запас упаковок
Pharmaceuticals: deterministic calculation
ВходНазначение, дозировка и длительность
FTSВычислить количество по утверждённой формуле
На выходеЧисло упаковок
ФармацевтикаSenior
Разрешить отпуск препарата
Pharmaceuticals: command eligibility
ВходРецепт, срок и ограничения
FTSПроверить свидетельства допуска
На выходеGuard команды отпуска
ФармацевтикаQA / Middle
Проверить температурный режим
Pharmaceuticals: executable examples
ВходДиапазон и измеренные значения
FTSИсполнить граничные примеры
На выходеТест cold chain
ФармацевтикаJunior
Собрать форму партии
Pharmaceuticals: generated form
ВходСерия, срок и производитель
FTSСкомпилировать структуру партии
На выходеФорма приёмки
ФармацевтикаJunior / Middle
Собрать таблицу карантина
Pharmaceuticals: generated table
ВходПартия, причина и статус
FTSСгенерировать колонки
На выходеQA quarantine table
ФармацевтикаSenior / Lead
Доказать выпуск серии
Pharmaceuticals: verified transition
ВходПротоколы, отклонения и подписи
FTSСкомпозировать состояния качества
На выходеСертификат release decision
ФармацевтикаAI agent
Дать агенту проверку серии
Pharmaceuticals: agent tool
ВходСтруктурированные QC-факты
FTSОграничить решение FTS-инструментом
На выходеДиагностика без изменения записей
ФармацевтикаTeam
Защитить спецификацию качества
Pharmaceuticals: regression gate
ВходЭталонные измерения
FTSЗапустить свойства и примеры
На выходеCI-гейт изменения пределов
HR и наймMiddle
Рассчитать вилку оффера
HR and hiring: deterministic calculation
ВходГрейд, регион и коэффициенты
FTSПрименить прозрачные правила диапазона
На выходеПредлагаемая вилка
HR и наймSenior
Разрешить перевод сотрудника
HR and hiring: command eligibility
ВходСогласования и срок в роли
FTSПроверить условия команды
На выходеTransfer command guard
HR и наймQA / Middle
Проверить отпускной баланс
HR and hiring: executable examples
ВходНачисления, списания и даты
FTSИсполнить граничные примеры
На выходеТест HR-политики
HR и наймJunior
Собрать форму вакансии
HR and hiring: generated form
ВходРоль, уровень и требования
FTSПолучить поля из структуры
На выходеФорма публикации вакансии
HR и наймJunior / Middle
Собрать таблицу кандидатов
HR and hiring: generated table
ВходЭтап, оценка и ответственный
FTSСгенерировать колонки ATS
На выходеRecruiting pipeline table
HR и наймSenior / Lead
Доказать готовность оффера
HR and hiring: verified transition
ВходИнтервью, бюджет и approvals
FTSСкомпозировать свидетельства
На выходеСертификат допуска оффера
HR и наймAI agent
Дать агенту проверку процесса
HR and hiring: agent tool
ВходОбезличенные факты кандидата
FTSВызвать read-only FTS tools
На выходеОбъяснимое состояние процесса
HR и наймTeam
Защитить грейдовую матрицу
HR and hiring: regression gate
ВходЭталонные профили уровней
FTSГенерировать тесты из примеров
На выходеКонтроль случайного сдвига вилок
SaaS и подпискиMiddle
Рассчитать счёт подписки
SaaS: deterministic calculation
ВходПлан, seats и usage
FTSСложить базу и потребление
На выходеInvoice subtotal
SaaS и подпискиSenior
Разрешить смену тарифа
SaaS: command eligibility
ВходОплата, лимиты и статус аккаунта
FTSВывести право на upgrade
На выходеPlan change guard
SaaS и подпискиQA / Middle
Проверить prorating
SaaS: executable examples
ВходДаты смены и расчётный период
FTSИсполнить примеры частичного месяца
На выходеТест prorated amount
SaaS и подпискиJunior
Собрать форму workspace
SaaS: generated form
ВходНазвание, регион и владелец
FTSСкомпилировать структуру
На выходеOnboarding form
SaaS и подпискиJunior / Middle
Собрать таблицу usage
SaaS: generated table
ВходМетрика, квота и фактическое значение
FTSПолучить колонки и формат чисел
На выходеUsage table
SaaS и подпискиSenior / Lead
Доказать право на feature
SaaS: verified transition
ВходПлан, entitlement и rollout
FTSСкомпозировать типизированные переходы
На выходеСертификат feature access
SaaS и подпискиAI agent
Дать агенту расчёт апгрейда
SaaS: agent tool
ВходТекущая подписка и новый план
FTSИсполнить FTS-утилиту через MCP
На выходеТочная оценка без вызова billing API
SaaS и подпискиTeam
Защитить биллинг
SaaS: regression gate
ВходЭталонные аккаунты и периоды
FTSЗапускать fts test в CI
На выходеРегресс-гейт счетов
Поддержка клиентовMiddle
Рассчитать приоритет тикета
Customer support: deterministic calculation
ВходКлиент, влияние и срочность
FTSПрименить SLA-правила
На выходеPriority score
Поддержка клиентовSenior
Разрешить компенсацию
Customer support: command eligibility
ВходПричина, сумма и история
FTSПроверить полномочия и факты
На выходеCompensation guard
Поддержка клиентовQA / Middle
Проверить эскалацию
Customer support: executable examples
ВходВозраст тикета и число ответов
FTSИсполнить сценарии SLA
На выходеТест маршрутизации
Поддержка клиентовJunior
Собрать форму обращения
Customer support: generated form
ВходТема, канал и вложения
FTSСкомпилировать структуру тикета
На выходеSupport form
Поддержка клиентовJunior / Middle
Собрать таблицу очереди
Customer support: generated table
ВходПриоритет, владелец и таймер
FTSСгенерировать колонки
На выходеAgent queue table
Поддержка клиентовSenior / Lead
Доказать готовность закрытия
Customer support: verified transition
ВходОтвет, подтверждение и отсутствие блокеров
FTSСкомпозировать состояния
На выходеСертификат закрытия
Поддержка клиентовAI agent
Дать агенту классификацию
Customer support: agent tool
ВходТекст и структурированные признаки
FTSПроверить предложенную категорию утилитой
На выходеКласс с FTS-диагностикой
Поддержка клиентовTeam
Защитить SLA-матрицу
Customer support: regression gate
ВходПримеры по сегментам
FTSЗапустить generated node:test
На выходеCI-гейт приоритетов
ОбразованиеMiddle
Рассчитать итог курса
Education: deterministic calculation
ВходРаботы, веса и посещаемость
FTSВычислить результат и проверить диапазон
На выходеИтоговый балл
ОбразованиеSenior
Разрешить переход к экзамену
Education: command eligibility
ВходМодули, практики и дедлайны
FTSВывести допуск из фактов
На выходеExam command guard
ОбразованиеQA / Middle
Проверить рубрику оценки
Education: executable examples
ВходЭталонные ответы и баллы
FTSИсполнить предметные примеры
На выходеТест grading rubric
ОбразованиеJunior
Собрать форму задания
Education: generated form
ВходУсловие, критерии и срок
FTSСкомпилировать структуру
На выходеAuthoring form
ОбразованиеJunior / Middle
Собрать таблицу прогресса
Education: generated table
ВходУченик, модуль и статус
FTSПолучить колонки из модели
На выходеProgress table
ОбразованиеSenior / Lead
Доказать завершение программы
Education: verified transition
ВходПройденные модули и проект
FTSСкомпозировать свидетельства
На выходеСертификат completion
ОбразованиеAI agent
Дать агенту проверку ответа
Education: agent tool
ВходОтвет и формальные критерии
FTSПередать проверяемую часть в FTS
На выходеРезультат с отделённым объяснением LLM
ОбразованиеTeam
Защитить правила оценивания
Education: regression gate
ВходНабор контрольных работ
FTSЗапустить примеры в CI
На выходеРегресс-гейт оценок
ПутешествияMiddle
Рассчитать цену билета
Travel: deterministic calculation
ВходТариф, багаж и сборы
FTSСложить детерминированные надбавки
На выходеFinal fare
ПутешествияSenior
Разрешить обмен билета
Travel: command eligibility
ВходFare rules, время и сегменты
FTSПроверить свидетельства права
На выходеExchange guard
ПутешествияQA / Middle
Проверить нормы багажа
Travel: executable examples
ВходКласс, маршрут и вес
FTSИсполнить граничные примеры
На выходеТест baggage policy
ПутешествияJunior
Собрать форму пассажира
Travel: generated form
ВходДокумент, контакты и preferences
FTSСкомпилировать поля
На выходеBooking form
ПутешествияJunior / Middle
Собрать таблицу сегментов
Travel: generated table
ВходРейс, время и статус
FTSСгенерировать колонки itinerary
На выходеTrip table
ПутешествияSenior / Lead
Доказать готовность к check-in
Travel: verified transition
ВходДокументы, оплата и открытое окно
FTSСкомпозировать состояния
На выходеСертификат check-in
ПутешествияAI agent
Дать агенту расчёт обмена
Travel: agent tool
ВходСнимок билета и политика
FTSИсполнить FTS без брони
На выходеПредварительная сумма и причины
ПутешествияTeam
Защитить тарифные правила
Travel: regression gate
ВходЭталонные маршруты
FTSГенерировать тесты из FTS
На выходеCI-гейт fare rules
ОтелиMiddle
Рассчитать стоимость проживания
Hospitality: deterministic calculation
ВходНочи, тариф и услуги
FTSПрименить rate rules
На выходеStay total
ОтелиSenior
Разрешить ранний заезд
Hospitality: command eligibility
ВходГотовность номера и тариф
FTSПроверить условия команды
На выходеEarly check-in guard
ОтелиQA / Middle
Проверить отмену брони
Hospitality: executable examples
ВходТариф и время до заезда
FTSИсполнить сценарии штрафов
На выходеТест cancellation policy
ОтелиJunior
Собрать форму бронирования
Hospitality: generated form
ВходГость, даты и номер
FTSСкомпилировать структуру брони
На выходеBooking form
ОтелиJunior / Middle
Собрать таблицу номерного фонда
Hospitality: generated table
ВходНомер, уборка и occupancy
FTSПолучить колонки
На выходеFront desk table
ОтелиSenior / Lead
Доказать готовность номера
Hospitality: verified transition
ВходУборка, инспекция и блокировки
FTSСкомпозировать состояния
На выходеСертификат ready room
ОтелиAI agent
Дать агенту проверку брони
Hospitality: agent tool
ВходДаты и тарифные факты
FTSВызвать FTS через MCP
На выходеСтруктурированная проверка
ОтелиTeam
Защитить rate plan
Hospitality: regression gate
ВходПримеры сезонов и дней
FTSЗапустить fts test
На выходеCI-гейт цен
НедвижимостьMiddle
Рассчитать арендный платёж
Real estate: deterministic calculation
ВходСтавка, площадь и индексация
FTSВычислить сумму по договорным правилам
На выходеMonthly rent
НедвижимостьSenior
Разрешить публикацию объявления
Real estate: command eligibility
ВходПраво, модерация и обязательные поля
FTSПроверить свидетельства
На выходеListing guard
НедвижимостьQA / Middle
Проверить ипотечный платёж
Real estate: executable examples
ВходСтавка, срок и сумма
FTSИсполнить контрольные примеры
На выходеТест калькулятора
НедвижимостьJunior
Собрать форму объекта
Real estate: generated form
ВходАдрес, площадь и характеристики
FTSСкомпилировать структуру
На выходеProperty form
НедвижимостьJunior / Middle
Собрать таблицу показов
Real estate: generated table
ВходОбъект, клиент и слот
FTSСгенерировать колонки
На выходеViewing schedule table
НедвижимостьSenior / Lead
Доказать готовность сделки
Real estate: verified transition
ВходПроверка титула, договор и оплата
FTSСкомпозировать этапы
На выходеСертификат closing readiness
НедвижимостьAI agent
Дать агенту проверку объявления
Real estate: agent tool
ВходФакты объекта
FTSОграничить вывод FTS-валидатором
На выходеСписок детерминированных нарушений
НедвижимостьTeam
Защитить комиссионные правила
Real estate: regression gate
ВходЭталонные сделки
FTSСгенерировать node:test
На выходеCI-гейт комиссии
ПроизводствоMiddle
Рассчитать потребность в материале
Manufacturing: deterministic calculation
ВходBOM, партия и отход
FTSВычислить количество по правилам
На выходеMaterial requirement
ПроизводствоSenior
Разрешить запуск партии
Manufacturing: command eligibility
ВходМатериал, оснастка и допуски
FTSПроверить факты готовности
На выходеProduction command guard
ПроизводствоQA / Middle
Проверить контрольные пределы
Manufacturing: executable examples
ВходИзмерения и спецификация
FTSИсполнить граничные примеры
На выходеQuality property tests
ПроизводствоJunior
Собрать форму техоперации
Manufacturing: generated form
ВходПараметры процесса
FTSСкомпилировать структуру
На выходеOperator form
ПроизводствоJunior / Middle
Собрать таблицу OEE
Manufacturing: generated table
ВходЛиния, простой и выпуск
FTSПолучить колонки
На выходеOperations table
ПроизводствоSenior / Lead
Доказать выпуск партии
Manufacturing: verified transition
ВходКонтроль, отклонения и подписи
FTSСкомпозировать состояния
На выходеRelease certificate
ПроизводствоAI agent
Дать агенту проверку запуска
Manufacturing: agent tool
ВходСнимок производственных фактов
FTSВызвать read-only FTS
На выходеДиагностика готовности
ПроизводствоTeam
Защитить технологические правила
Manufacturing: regression gate
ВходGolden batch примеры
FTSЗапускать fts test в CI
На выходеГейт изменения процесса
ЭнергетикаMiddle
Рассчитать тариф потребления
Energy: deterministic calculation
ВходЗона, период и объём
FTSПрименить ступени тарифа
На выходеCharge amount
ЭнергетикаSenior
Разрешить подключение объекта
Energy: command eligibility
ВходТехусловия, договор и осмотр
FTSПроверить свидетельства
На выходеConnection guard
ЭнергетикаQA / Middle
Проверить пороги нагрузки
Energy: executable examples
ВходМощность и режим
FTSИсполнить граничные сценарии
На выходеТест защитных порогов
ЭнергетикаJunior
Собрать форму показаний
Energy: generated form
ВходСчётчик, дата и значение
FTSСкомпилировать поля
На выходеMeter reading form
ЭнергетикаJunior / Middle
Собрать таблицу аварий
Energy: generated table
ВходУзел, влияние и статус
FTSСгенерировать колонки
На выходеIncident table
ЭнергетикаSenior / Lead
Доказать готовность включения
Energy: verified transition
ВходИспытания, наряд и блокировки
FTSСкомпозировать состояния
На выходеSwitching certificate
ЭнергетикаAI agent
Дать агенту расчёт тарифа
Energy: agent tool
ВходПотребление без управляющего доступа
FTSИсполнить FTS-утилиту
На выходеОбъяснимый расчёт
ЭнергетикаTeam
Защитить тарифные ступени
Energy: regression gate
ВходЭталонные профили
FTSГенерировать тесты
На выходеCI-гейт начислений
ТелекомMiddle
Рассчитать счёт абонента
Telecom: deterministic calculation
ВходПлан, минуты и трафик
FTSПрименить квоты и overage
На выходеBilling total
ТелекомSenior
Разрешить перенос номера
Telecom: command eligibility
ВходИдентификация, долг и окно
FTSПроверить условия MNP
На выходеPorting guard
ТелекомQA / Middle
Проверить fair-use
Telecom: executable examples
ВходПотребление и пороги
FTSИсполнить сценарии ограничения
На выходеТест policy
ТелекомJunior
Собрать форму подключения
Telecom: generated form
ВходАбонент, адрес и тариф
FTSСкомпилировать структуру
На выходеActivation form
ТелекомJunior / Middle
Собрать таблицу сети
Telecom: generated table
ВходУзел, сигнал и статус
FTSПолучить колонки
На выходеNOC table
ТелекомSenior / Lead
Доказать готовность активации
Telecom: verified transition
ВходSIM, договор и provisioning
FTSСкомпозировать этапы
На выходеActivation certificate
ТелекомAI agent
Дать агенту подбор тарифа
Telecom: agent tool
ВходПрофиль потребления
FTSИсполнить детерминированную утилиту
На выходеСтруктурированная рекомендация
ТелекомTeam
Защитить биллинговые квоты
Telecom: regression gate
ВходЭталонные CDR-наборы
FTSЗапустить fts test
На выходеCI-гейт начислений
Медиа и подпискиMiddle
Рассчитать выплату автору
Media: deterministic calculation
ВходПросмотры, территория и ставка
FTSПрименить royalty rules
На выходеRoyalty amount
Медиа и подпискиSenior
Разрешить показ контента
Media: command eligibility
ВходЛицензия, регион и возраст
FTSПроверить права доступа
На выходеPlayback guard
Медиа и подпискиQA / Middle
Проверить окна лицензии
Media: executable examples
ВходНачало, конец и территория
FTSИсполнить граничные даты
На выходеRights tests
Медиа и подпискиJunior
Собрать форму публикации
Media: generated form
ВходМетаданные и права
FTSСкомпилировать структуру
На выходеEditorial form
Медиа и подпискиJunior / Middle
Собрать таблицу каталога
Media: generated table
ВходТайтл, окно и статус
FTSСгенерировать колонки
На выходеCatalog table
Медиа и подпискиSenior / Lead
Доказать готовность релиза
Media: verified transition
ВходQC, права и расписание
FTSСкомпозировать состояния
На выходеRelease certificate
Медиа и подпискиAI agent
Дать агенту проверку прав
Media: agent tool
ВходКонтент и контекст пользователя
FTSВызвать FTS до playback API
На выходеДетерминированное решение
Медиа и подпискиTeam
Защитить royalty rules
Media: regression gate
ВходЭталонные отчёты
FTSСгенерировать тесты
На выходеCI-гейт выплат
Рекламные платформыMiddle
Рассчитать ставку кампании
Advertising: deterministic calculation
ВходАукцион, сегмент и лимит
FTSПрименить bidding rules
На выходеBid amount
Рекламные платформыSenior
Разрешить запуск креатива
Advertising: command eligibility
ВходМодерация, бюджет и права
FTSПроверить свидетельства
На выходеCampaign guard
Рекламные платформыQA / Middle
Проверить frequency cap
Advertising: executable examples
ВходПоказы, окно и пользователь
FTSИсполнить граничные примеры
На выходеCap tests
Рекламные платформыJunior
Собрать форму кампании
Advertising: generated form
ВходЦель, аудитория и бюджет
FTSСкомпилировать структуру
На выходеCampaign form
Рекламные платформыJunior / Middle
Собрать таблицу метрик
Advertising: generated table
ВходПоказы, клики и стоимость
FTSПолучить колонки
На выходеReporting table
Рекламные платформыSenior / Lead
Доказать готовность запуска
Advertising: verified transition
ВходОплата, модерация и расписание
FTSСкомпозировать этапы
На выходеLaunch certificate
Рекламные платформыAI agent
Дать агенту preflight
Advertising: agent tool
ВходНастройки кампании
FTSВызвать FTS MCP
На выходеСтруктурированный список блокеров
Рекламные платформыTeam
Защитить правила бюджета
Advertising: regression gate
ВходЭталонные кампании
FTSЗапустить properties в CI
На выходеSpend regression gate
Государственные услугиMiddle
Рассчитать размер пошлины
Public services: deterministic calculation
ВходУслуга, льгота и канал
FTSПрименить утверждённые ставки
На выходеFee amount
Государственные услугиSenior
Разрешить подачу заявления
Public services: command eligibility
ВходЛичность, документы и срок
FTSПроверить свидетельства
На выходеSubmission guard
Государственные услугиQA / Middle
Проверить сроки услуги
Public services: executable examples
ВходТип заявления и календарь
FTSИсполнить SLA-примеры
На выходеDeadline tests
Государственные услугиJunior
Собрать форму заявления
Public services: generated form
ВходСтруктура государственной услуги
FTSСкомпилировать поля
На выходеAccessible service form
Государственные услугиJunior / Middle
Собрать таблицу обращений
Public services: generated table
ВходНомер, этап и исполнитель
FTSСгенерировать колонки
На выходеBack-office table
Государственные услугиSenior / Lead
Доказать готовность результата
Public services: verified transition
ВходПроверки, подпись и доставка
FTSСкомпозировать состояния
На выходеCertificate of readiness
Государственные услугиAI agent
Дать агенту проверку комплекта
Public services: agent tool
ВходПеречень документов
FTSОграничить ответ FTS-правилами
На выходеДиагностика недостающих данных
Государственные услугиTeam
Защитить льготные правила
Public services: regression gate
ВходЭталонные заявители
FTSЗапустить примеры в CI
На выходеPolicy regression gate
Информационная безопасностьMiddle
Рассчитать риск доступа
Information security: deterministic calculation
ВходАктив, роль и контекст
FTSПрименить прозрачные коэффициенты
На выходеRisk score
Информационная безопасностьSenior
Разрешить выдачу секрета
Information security: command eligibility
ВходMFA, устройство и approval
FTSПроверить свидетельства
На выходеSecret access guard
Информационная безопасностьQA / Middle
Проверить политику пароля
Information security: executable examples
ВходДлина и признаки
FTSИсполнить positive/negative examples
На выходеPolicy tests
Информационная безопасностьJunior
Собрать форму исключения
Information security: generated form
ВходРиск, срок и владелец
FTSСкомпилировать структуру
На выходеException form
Информационная безопасностьJunior / Middle
Собрать таблицу уязвимостей
Information security: generated table
ВходCVE, критичность и SLA
FTSПолучить колонки
На выходеVulnerability table
Информационная безопасностьSenior / Lead
Доказать право privileged access
Information security: verified transition
ВходЗапрос, согласования и окно
FTSСкомпозировать состояния
На выходеAccess certificate
Информационная безопасностьAI agent
Дать агенту policy check
Information security: agent tool
ВходПредлагаемое действие и факты
FTSВызвать fts_verify перед эффектом
На выходеПроверяемое разрешение или отказ
Информационная безопасностьTeam
Защитить access policy
Information security: regression gate
ВходМатрица ролей и контекстов
FTSЗапускать FTS tests в CI
На выходеSecurity regression gate
DevOps и SREMiddle
Рассчитать бюджет ошибок
DevOps and SRE: deterministic calculation
ВходSLO, окно и неуспешные запросы
FTSВычислить остаток бюджета
На выходеError budget
DevOps и SRESenior
Разрешить production deploy
DevOps and SRE: command eligibility
ВходТесты, change window и approvals
FTSПроверить release evidence
На выходеDeploy guard
DevOps и SREQA / Middle
Проверить пороги алерта
DevOps and SRE: executable examples
ВходSLI и длительность окна
FTSИсполнить сценарии burn rate
На выходеAlert policy tests
DevOps и SREJunior
Собрать форму change request
DevOps and SRE: generated form
ВходСервис, риск и rollback
FTSСкомпилировать структуру
На выходеChange form
DevOps и SREJunior / Middle
Собрать таблицу инцидентов
DevOps and SRE: generated table
ВходSeverity, owner и время
FTSСгенерировать колонки
На выходеIncident table
DevOps и SRESenior / Lead
Доказать готовность релиза
DevOps and SRE: verified transition
ВходАртефакт, тесты и подписи
FTSСкомпозировать состояния
На выходеRelease certificate
DevOps и SREAI agent
Дать агенту pre-deploy check
DevOps and SRE: agent tool
ВходСнимок CI и окружения
FTSПотребовать fts_verify до команды
На выходеAgent gate перед деплоем
DevOps и SRETeam
Защитить SLO-политику
DevOps and SRE: regression gate
ВходЭталонные временные окна
FTSЗапускать generated tests
На выходеCI-гейт observability rules
Сельское хозяйствоMiddle
Рассчитать норму полива
Agriculture: deterministic calculation
ВходКультура, почва и влажность
FTSПрименить агрономические правила
На выходеWater amount
Сельское хозяйствоSenior
Разрешить обработку поля
Agriculture: command eligibility
ВходПогода, препарат и интервал
FTSПроверить свидетельства
На выходеTreatment guard
Сельское хозяйствоQA / Middle
Проверить нормы внесения
Agriculture: executable examples
ВходПлощадь и дозировка
FTSИсполнить граничные примеры
На выходеApplication tests
Сельское хозяйствоJunior
Собрать форму осмотра
Agriculture: generated form
ВходПоле, культура и наблюдения
FTSСкомпилировать структуру
На выходеMobile inspection form
Сельское хозяйствоJunior / Middle
Собрать таблицу участков
Agriculture: generated table
ВходПоле, фаза и влажность
FTSПолучить колонки
На выходеAgronomy table
Сельское хозяйствоSenior / Lead
Доказать готовность уборки
Agriculture: verified transition
ВходВлажность, зрелость и техника
FTSСкомпозировать состояния
На выходеHarvest certificate
Сельское хозяйствоAI agent
Дать агенту расчёт нормы
Agriculture: agent tool
ВходИзмеренные факты
FTSВызвать FTS-утилиту
На выходеОбъяснимый расчёт без управления техникой
Сельское хозяйствоTeam
Защитить агрономические правила
Agriculture: regression gate
ВходЭталонные участки
FTSЗапускать примеры в CI
На выходеPolicy regression gate
Legal и complianceMiddle
Рассчитать срок хранения
Legal and compliance: deterministic calculation
ВходТип записи, юрисдикция и событие
FTSПрименить retention rules
На выходеRetention date
Legal и complianceSenior
Разрешить обработку данных
Legal and compliance: command eligibility
ВходЦель, основание и согласие
FTSПроверить юридические свидетельства
На выходеProcessing guard
Legal и complianceQA / Middle
Проверить договорный лимит
Legal and compliance: executable examples
ВходСумма и полномочия подписанта
FTSИсполнить граничные примеры
На выходеAuthority tests
Legal и complianceJunior
Собрать форму согласования
Legal and compliance: generated form
ВходКонтрагент, риск и условия
FTSСкомпилировать структуру
На выходеApproval form
Legal и complianceJunior / Middle
Собрать таблицу обязательств
Legal and compliance: generated table
ВходПункт, срок и владелец
FTSСгенерировать колонки
На выходеObligations table
Legal и complianceSenior / Lead
Доказать готовность договора
Legal and compliance: verified transition
ВходReview, approvals и подпись
FTSСкомпозировать состояния
На выходеContract readiness certificate
Legal и complianceAI agent
Дать агенту compliance check
Legal and compliance: agent tool
ВходСтруктурированные факты операции
FTSВызвать FTS MCP и сохранить diagnostics
На выходеПроверяемый pre-check, не юридическое заключение
Legal и complianceTeam
Защитить retention policy
Legal and compliance: regression gate
ВходЭталонные типы документов
FTSЗапускать тесты в CI
На выходеPolicy regression gate
E-commerce operationsMiddle
Рассчитать резерв товара
E-commerce operations: deterministic calculation
ВходСпрос, остаток и safety stock
FTSВычислить доступное количество
На выходеAvailable-to-promise
E-commerce operationsSenior
Разрешить замену позиции
E-commerce operations: command eligibility
ВходНаличие, цена и согласие
FTSПроверить условия замены
На выходеSubstitution guard
E-commerce operationsQA / Middle
Проверить минимальный заказ
E-commerce operations: executable examples
ВходКорзина и зона
FTSИсполнить граничные примеры
На выходеMinimum order tests
E-commerce operationsJunior
Собрать форму комплектации
E-commerce operations: generated form
ВходЗаказ, ячейка и количество
FTSСкомпилировать структуру
На выходеPicker form
E-commerce operationsJunior / Middle
Собрать таблицу волн
E-commerce operations: generated table
ВходWave, заказы и прогресс
FTSСгенерировать колонки
На выходеFulfillment table
E-commerce operationsSenior / Lead
Доказать готовность волны
E-commerce operations: verified transition
ВходРезерв, персонал и слот
FTSСкомпозировать состояния
На выходеWave release certificate
E-commerce operationsAI agent
Дать агенту проверку резерва
E-commerce operations: agent tool
ВходСнимок складских фактов
FTSИсполнить FTS без записи в WMS
На выходеДетерминированный pre-check
E-commerce operationsTeam
Защитить правила substitution
E-commerce operations: regression gate
ВходЭталонные заказы
FTSГенерировать node:test
На выходеCI-гейт комплектации
БухгалтерияMiddle
Рассчитать налоговую сумму
Accounting: deterministic calculation
ВходБаза, ставка и льгота
FTSПрименить правила расчёта
На выходеTax amount
БухгалтерияSenior
Разрешить проведение документа
Accounting: command eligibility
ВходПериод, реквизиты и approvals
FTSПроверить условия проводки
На выходеPosting guard
БухгалтерияQA / Middle
Проверить баланс документа
Accounting: executable examples
ВходДебет и кредит
FTSИсполнить примеры равенства
На выходеAccounting invariant tests
БухгалтерияJunior
Собрать форму счёта
Accounting: generated form
ВходКонтрагент, позиции и НДС
FTSСкомпилировать структуру
На выходеInvoice form
БухгалтерияJunior / Middle
Собрать таблицу проводок
Accounting: generated table
ВходСчёт, сумма и аналитика
FTSПолучить колонки
На выходеLedger table
БухгалтерияSenior / Lead
Доказать готовность закрытия
Accounting: verified transition
ВходСверки, периоды и approvals
FTSСкомпозировать состояния
На выходеPeriod close certificate
БухгалтерияAI agent
Дать агенту проверку документа
Accounting: agent tool
ВходСтруктурированные реквизиты
FTSВызвать fts_check и fts_execute
На выходеДиагностика до записи в ERP
БухгалтерияTeam
Защитить налоговые правила
Accounting: regression gate
ВходЭталонные документы
FTSЗапускать примеры в CI
На выходеCalculation regression gate
ЗакупкиMiddle
Рассчитать оценку предложения
Procurement: deterministic calculation
ВходЦена, срок и качество
FTSПрименить весовые правила
На выходеBid score
ЗакупкиSenior
Разрешить заказ поставщику
Procurement: command eligibility
ВходБюджет, vendor check и approvals
FTSПроверить свидетельства
На выходеPurchase order guard
ЗакупкиQA / Middle
Проверить лимиты согласования
Procurement: executable examples
ВходСумма и роль
FTSИсполнить граничные примеры
На выходеApproval matrix tests
ЗакупкиJunior
Собрать форму заявки
Procurement: generated form
ВходПозиции, центр затрат и обоснование
FTSСкомпилировать структуру
На выходеRequisition form
ЗакупкиJunior / Middle
Собрать таблицу тендеров
Procurement: generated table
ВходЭтап, участники и дедлайн
FTSСгенерировать колонки
На выходеSourcing table
ЗакупкиSenior / Lead
Доказать готовность PO
Procurement: verified transition
ВходВыбор, договор и бюджет
FTSСкомпозировать состояния
На выходеPO readiness certificate
ЗакупкиAI agent
Дать агенту pre-check закупки
Procurement: agent tool
ВходФакты заявки
FTSВызвать FTS без вызова ERP
На выходеСтруктурированный список блокеров
ЗакупкиTeam
Защитить approval matrix
Procurement: regression gate
ВходЭталонные суммы и роли
FTSГенерировать тесты
На выходеCI-гейт полномочий
Identity и доступMiddle
Рассчитать уровень доверия
Identity and access: deterministic calculation
ВходMFA, устройство и сессия
FTSПрименить risk rules
На выходеAssurance level
Identity и доступSenior
Разрешить повышение роли
Identity and access: command eligibility
ВходЗапрос, approvals и separation of duties
FTSПроверить свидетельства
На выходеRole elevation guard
Identity и доступQA / Middle
Проверить session policy
Identity and access: executable examples
ВходВозраст, idle и риск
FTSИсполнить граничные сценарии
На выходеSession tests
Identity и доступJunior
Собрать форму запроса доступа
Identity and access: generated form
ВходРесурс, роль и срок
FTSСкомпилировать структуру
На выходеAccess request form
Identity и доступJunior / Middle
Собрать таблицу прав
Identity and access: generated table
ВходСубъект, ресурс и grant
FTSПолучить колонки
На выходеEntitlements table
Identity и доступSenior / Lead
Доказать право администратора
Identity and access: verified transition
ВходMFA, approval и active window
FTSСкомпозировать состояния
На выходеPrivileged access certificate
Identity и доступAI agent
Дать агенту authorization check
Identity and access: agent tool
ВходДействие и snapshot identity
FTSПотребовать fts_verify
На выходеМашинно-проверяемый допуск
Identity и доступTeam
Защитить RBAC-политику
Identity and access: regression gate
ВходМатрица субъектов и ресурсов
FTSЗапустить FTS tests
На выходеAuthorization regression gate
По этим фильтрам кейсов нет. Попробуйте убрать один из фильтров.
Границы
Чего язык не делает.
Внешних эффектов нет. Ни HTTP, ни SQL, ни оплаты: приложение выполняет действие уже после результата FTS.
Коллекций и функций высшего порядка в FTS нет.map, filter, groupBy, debounce остаются в JavaScript и переезжать не должны.
Доказательство завершения — не всё. Тотальная функция может честно закончиться и вернуть неверное число: за правильность отвечают примеры, за сложность — никто.
Часть задач не выражается. Из взятого набора LeetCode 12 задач не пишутся вовсе, а у 6 решений завершение не доказано.
Чего не хватает языку: 16 пунктов и 12 невыразимых задач
разложение строки
Нет встроенной формы «символы строки» и нет образцов для строк: «пусто» и «голова и хвост» работают только со списком. Любой посимвольный проход — обычная функция. Это самая дорогая из недостач: она одна делает нетотальными задачи 13, 14, 20, 125.
убывание по мере
Анализ завершаемости знает только структурное убывание. Рекурсия «n минус 1», Евклид, двоичный поиск по границам — всё это FLANG_NOT_TOTAL, хотя завершается очевидно. Обход есть (список-топливо), но он засоряет сигнатуру.
ассоциативный массив
Нет ни словаря, ни множества. Каждая задача «посчитай, сколько раз встретилось» превращается из O(n) в O(n²): 1, 136, 169, 217, 49.
массив с индексным доступом
Список читается только с головы; «Элемент по номеру» пишется рекурсией и стоит O(n). Кучи, таблицы динамики и двумерные сетки на этом ломаются.
сравнение строк
types.mjs разрешает «больше»/«меньше» для строк, а interpret.mjs на них бросает FLANG_TYPE. Это расхождение слоёв: программа проходит проверку и падает при запуске. Сортировка строк невыразима.
логические операции
Нет «и», «или», «не» для признаков. Конъюнкция пишется вложенными «если», отрицание — сравнением «равен нет». Читается заметно хуже написанного условия.
параметрический полиморфизм
«Обратить» для списка числа и для списка строки — две разные функции с одинаковым телом. Опциональное значение приходится заводить отдельно под каждый тип, а имена вариантов обязаны быть уникальны в модуле.
модульность
Заголовок «модуль … использует …» разбирается, но связывания между файлами нет: каждое решение переписывает нужные ему «Приписать в начало» и «Соединить списки» заново. Стандартной библиотекой нельзя воспользоваться из решения.
значение варианта в примере
parseLiteralValue сворачивает конструктор варианта в запись из его полей и теряет имя варианта. Поэтому функция, принимающая или возвращающая сумму типов, примера иметь не может — приходится обкладывать её скалярными обёртками.
структурное сравнение в примерах
runExamples (compat.mjs) сравнивает результат с ожидаемым через Object.is, поэтому любой пример, возвращающий список или запись, считается провалившимся, даже когда значения совпадают поэлементно. Команда «flang test» на таких файлах не работает; тесты в flang/test сравнивают структурно (valuesEqual) и проходят.
встроенная форма «символ»
Парсер и builtins.mjs передают «символ N в текст» как (число, строка), а types.mjs ждёт (строка, число). Любое использование формы «символ» не проходит check; вместо неё приходится писать «подстрока текст с N по N».
встроенная форма «пусто»
В позиции выражения слово «пусто» всегда читается как литерал пустого списка, поэтому до одноимённой встроенной формы (проверка «список или строка пусты») из исходника не добраться. Пишется «(длина x) равен 0».
списочная форма «соединить»
builtins.mjs умеет «соединить список с разделителем», но поверхностный синтаксис «соединить X с Y» всегда даёт склейку двух строк. Соединение списка строк написано в stdlib заново.
вызов функции без аргументов
Применение записывается только как «Имя» от аргумента, поэтому функцию с пустым списком параметров вызвать нечем. Константы приходится делать функциями от неиспользуемого аргумента или встраивать выражением.
псевдоним типа
«тип «X» это список числа» разбирается в узел alias, но types.mjs раскладывает объявления на суммы и записи, и alias становится записью без полей. Пользоваться псевдонимами нельзя.
обработка ошибок
Отказ встроенной формы («к числу» от «abc», «голова» от пустого списка) прекращает вычисление целиком и не перехватывается. Проверять пригодность аргумента приходится заранее, а выразить эту проверку удаётся не всегда.
3Longest Substring Without Repeating Characters — Скользящее окно по строке: две границы, двигающиеся по числовым индексам, и множество символов в окне. Ни окна, ни множества, ни убывания по индексам язык не даёт; выразимо лишь квадратичным перебором подстрок в обычном классе, что уже не решение задачи.
4Median of Two Sorted Arrays — Требуемое O(log(m+n)) — двоичный поиск по разделяющей позиции сразу в двух массивах с произвольным доступом. Индексный доступ в flang линеен, поэтому даже правильный алгоритм даст O(n log n), а тотальность потребует топлива для каждого из двух поисков.
23Merge k Sorted Lists — Сам результат выразим (свёртка слиянием), но условие говорит о связных списках и требует O(N log k) через кучу. Связный список в flang неотличим от обычного списка, а куча требует индексного доступа. Задача в её настоящей формулировке отсутствует в языке вместе со структурой данных.
76Minimum Window Substring — Скользящее окно плюс счётчики символов в словаре. Ни того, ни другого; перебор всех подстрок требует обхода строки по двум индексам — нетотально и за пределами разумного лимита шагов.
139Word Break — Нужны и разрезы строки по всем позициям (обход строки), и запоминание уже посчитанных суффиксов. Первого нет тотально, второго нет никак: мемоизация — это состояние.
146LRU Cache — Требуется объект с состоянием и набором методов (get/put), сохраняющий его между вызовами. В flang нет ни изменяемых значений, ни объектов с методами, ни способа завести долгоживущее состояние: функция — чистое отображение аргументов в результат.
155Min Stack — То же: задача формулируется как API из четырёх операций над разделяемым состоянием. Выразить можно только «состояние в аргументе, состояние в результате», и это уже другая задача — соответствия условию не будет.
179Largest Number — Сортировка строк по правилу «a+b против b+a». Сравнивать строки в flang нельзя вообще: types.mjs считает строки упорядоченными, а интерпретатор на «больше»/«меньше» для строк бросает FLANG_TYPE («сравнения порядка допустимы только для чисел»). Обойти это можно только собственной посимвольной функцией сравнения — то есть нетотально и через несуществующее разложение строки.
200Number of Islands — Обход в глубину по сетке помечает посещённые клетки. Без изменяемых структур пометки пришлось бы возвращать новой сеткой на каждом шаге, а рекурсия «сетка после пометки» не убывает структурно — доказать конец нельзя. В обычном классе решение раздувается до неузнаваемости и всё равно упирается в отсутствие индексного доступа.
208Implement Trie — Опять API с состоянием; кроме того, узел бора — это словарь «символ → узел», а ассоциативных массивов нет. Список пар с линейным поиском превращает O(длины слова) в O(длины × алфавит) и перестаёт быть бором по существу.
295Find Median from Data Stream — Поток и две кучи: и то, и другое — состояние, живущее между вызовами. Куча к тому же требует индексного доступа к массиву, которого в языке нет (только «голова», «хвост» и обход).
322Coin Change — Динамика по таблице сумм: нужен массив длиной amount с чтением произвольной ячейки и записью в неё. Список flang читается только с головы, а построение таблицы свёрткой требует на каждом шаге линейного поиска — и всё равно рекурсия по сумме нетотальна. Наивный перебор экспоненциален и не укладывается в лимит шагов.
Дальше
Курс: от первого правила до production и проверяемых сертификатов.
23 модулей по FTS и 14 по flang — синтаксис, утилиты, тесты, диагностика, Node.js, React, DDD, AI/MCP, CI, тотальность и кодогенерация. В каждом модуле упражнение, которое открывается прямо в песочнице на шаге 3.
Курсы. Поиск объединяет публикации по учебным трекам и сравнивает запрос
с названиями, описаниями, тегами, заголовками и фрагментами материалов.
Материалы. Этот режим ранжирует отдельные статьи и видео, когда вы уже
знаете тему или название.
Текст запроса не отправляется на сервер или во внешнюю ML-систему. Это статистическая
модель, а не генеративный AI: она рекомендует существующее и ничего не придумывает.
Граф знаний
Карта портала
Ступени, треки и материалы портала. Кликните ступень, чтобы раскрыть треки,
трек — чтобы увидеть статьи, статью — чтобы увидеть, с чем она связана
по общим темам.
заметные: 2+ у материалов, 4+ у треков
СтупеньТрекМатериалВы здесь
Загружаем карту знаний…
Колесо — масштаб, перетаскивание — панорама. Стрелки и Tab — переход между
узлами, Enter — раскрыть или выбрать. Повторный клик по материалу
показывает его соседей по общим темам.