DigitableCourses
Настройки портала
Показать возможности портала

Локально и без аккаунта. Аккаунта нет, регистрация не нужна: введённое в инструменты остаётся в localStorage браузера и на сервер не уходит.

Свой счётчик считает открытия страниц и дочитывания: уезжает адрес и десятая доля текста. Без cookies и чужих счётчиков, IP не хранится, Do Not Track уважается. Как это проверить

Репозиторий портала не выложен, «открытым кодом» мы его не зовём. Открыто это:

Живёт портал на донатах, платных консультациях и разборах по запросу и покупке Workbench.

Планов делать курсы платными нет.

flang — описание языка

flang — язык программирования с исполняемой спецификацией: компилятор различает функции с доказанным завершением и обычные, печатает программу в девять целевых языков, собирается из закоммиченного семени и ставится одной командой brew.

flang

flang — язык программирования с исполняемой спецификацией. Его отличительная черта — разделение программ на два класса, которые различает компилятор: программы, завершение которых доказано, и все остальные. Поверхность языка словесная: в синтаксисе нет символов, которые нельзя набрать на обычной клавиатуре.

Язык вырос из FTS (Formal Type Surface) — языка исполняемых спецификаций для описания предметных областей. Сегодня это две поверхности одного проекта, и путать их не стоит.

Справка двоичного обещает, что .fts он не берёт: «Мост из FTS („.fts“-модели, законы поверх них) бинарник не принимает вовсе» (flang check --help). На прогоне выходит хуже отказа. flang check разбирает модель как программу без функций и печатает зелёное с кодом 0 — ровно то же, что на пустом файле. flang check --proof договаривает: «нет объявленных функций», «нет объявленных законов», а теорема — «объявлено, не доказано: старая форма (наследие FTS)». Зелёный ответ на .fts проверки не значит, и опираться на него нельзя.

Канал поставки FTS закрыт решением владельца 31 августа 2026 года. Пакет @digitable-lol/fts при этом не исчез: версия 0.4.7 стоит в зависимостях портала, и из неё собрана вендорная копия, на которой работает песочница раздела. Язык ставится формулой — brew install digitable-lol/tap/flang, двоичный 0.7.3.

Предметную область сегодня записывают на самом flang: это fspec, и о нём отдельный раздел ниже.

Парадигма функциональная, декларативная
Появился август 2026
Типизация статическая, сильная, суммы и произведения типов
Лицензия BSD 2-Clause
Расширения файлов .flang, .fp, .фп, .фланг
Репозиторий digitable-lol/flang

Особенности

Два класса программ

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

Доказательство ведётся двумя способами. Структурное убывание: каждый рекурсивный вызов получает структурно меньший аргумент — хвост списка, поле записи, поле варианта. Числовая мера: аргумент уменьшается на постоянный шаг и снизу ограничен проверкой-неравенством. Что не берут оба, то отвергается, и это не придирка: размер минус позиция уменьшается, но постоянного шага у него нет — позиция растёт от витка к витку, и обосновать этим завершение нельзя без рассуждений, которых компилятор не ведёт.

Разделение имеет прямое следствие: во встраиваемом режиме проверки фактов допускаются только тотальные функции. Система, отвечающая на вопрос «подтверждено или нет», не имеет права зависнуть.

тотальная функция «Длина»
  принимает элементы: список числа
  возвращает число
  разбор элементов
    случай пусто
      то 0
    случай голова и хвост
      то 1 плюс «Длина» от хвоста

Печать в целевые языки

Программа печатается в исходный код на девяти языках: C, C++, Go, Rust, Python, Java, C#, Elixir и JavaScript.

Печать сопровождается дифференциальной сверкой: напечатанный код обязан давать те же значения, те же коды и те же тексты ошибок, что интерпретатор. Для каждой цели это проверяется на 35 программах, 214 функциях и 3071 точке сетки входов, а сетка строится не только из примеров, но и из порчи каждого аргумента заведомо чужими значениями — иначе диагностики остались бы непроверенными.

Такая сверка находит дефекты, невидимые для тестов отдельно взятого бэкенда. За время разработки так были найдены: печать некомпилируемого C при совпадении имени варианта и функции; литерал варианта, превращавшийся в запись в бэкенде Go; знаковый ноль при делении на бесконечность в Elixir.

Словесный синтаксис

В синтаксисе используются только слова: из … в …, после, цепочка … сначала … затем …, сохраняет композицию. Русская и английская поверхности равноправны и компилируются в одно синтаксическое дерево.

Категорная поверхность

Морфизм объявляет стрелку между объектами категории; композиция записывается словом после, а длинная цепочка — в порядке чтения:

морфизм «отгрузить» из «Заказ» в «Отгрузка»
морфизм «выставить» из «Отгрузка» в «Счёт»
морфизм «оформить» это «выставить» после «отгрузить»

цепочка «провести заказ»
  сначала «отгрузить»
  затем «выставить»
  затем «оплатить»

Стыковка композиции доказуема из объявлений: «б» после «а» собирается тогда и только тогда, когда кодомен «а» равен домену «б». Несостыковка называет, что с чем не сошлось:

FLANG_COMPOSE_MISMATCH: «выставить» приводит в «Счёт», а «отгрузить» ожидает «Заказ»

Дальше нужна оговорка, без которой весь раздел читается неверно: правил категорной поверхности двоичный компилятор сегодня не судит вовсе. Он разбирает объявления, проверяет типы, завершаемость, доказательства и примеры, а дойдя до законов поверхности, называет непроверенное поимённо и выходит кодом 2 — «проверено НЕ ДО КОНЦА», потому что «замечаний нет» здесь читалось бы как «проверено». Судья не потерян, он не подключён: слои, считающие эти законы, написаны на самом flang (flang/self/monoid.flang, monad.flang, iso.flang, functor.flang), но в сборку двоичного не входят, а прежняя реализация на JavaScript, которая их считала, снята. Всё, что ниже сказано словом «проверяется», — это контракт flang/cat/SPEC.md, а не сегодняшний ответ команды.

Функтор отображает объекты и стрелки, и три его закона относятся к доказанному, а не к проверенному на примерах: образ стрелки обязан вести из образа домена в образ кодомена, образ композиции обязан быть композицией образов в том же порядке, образ единицы — единицей образа. Доказательство здесь возможно потому, что морфизм — объявление, а не значение: домен и кодомен известны до запуска, и законы проверяются сравнением объявлений, без сетки и без решателя. Имена категорий при этом остаются пометкой для читателя: категория отдельной сущностью не объявляется, и утверждать принадлежность объекта именно ей не на чем.

Моноид объявляется носителем, операцией и единицей; с обращением он становится группой — отдельного слова нет, потому что группа и есть моноид с обращением. Здесь граница «доказано / проверено» проходит внутри одной конструкции: доказывается устройство (операция — функция двух аргументов носителя, единица — значение носителя), а сами законы — ассоциативность, нейтральность, обратимость — проверяются на конечной сетке из примеров операции. Доказать их нельзя: это равенства вычислений на всех значениях носителя.

Изоморфизм и бифунктор объявляются и проверяются тем же сличением объявлений. У изоморфизма при этом само равенство «обратный после прямого = единица» не проверяется вовсе — ни доказательством, ни на сетке: у стрелки категории нет тела, вычислять нечего. Это допущение автора, и компилятор отвечает лишь на то, осмысленно ли оно сформулировано.

Монада объявляется на параметрическом типе и называет две функции — возврат и соединение. Отображение эндофунктора при этом не объявляется вовсе: у полиномиального функтора оно ровно одно, и компилятор выводит его из устройства типа. На том же стоит форма в монаде — do-нотация словами, которая разворачивается внутри разбора в обычные вызовы, поэтому все девять целей получают её даром (flang/cat/MONAD.md).

Естественные преобразования по-прежнему только описаны в контракте flang/cat/SPEC.md и не реализованы, но причина сместилась: раньше мешал сам параметрический полиморфизм, теперь он есть — мешает то, что связь морфизмов знает имя типа, а не применение (flang/cat/POLY.md, фаза 3).

Разбор суждений

Помимо ошибок синтаксиса и типов проверка сообщает о дефектах в самих правилах: недостижимых свойствах, дырах в покрытии входа и перекрытии правил. Анализ интервальный и точный для условий, сравнивающих поле с константой; условия, связывающие поля между собой, честно помечаются непроанализированными.

FTS_COVERAGE_HOLE          при «сумма» ∈ (−∞, 10000) не срабатывает ни одно
                           правило — результат остаётся начальным (0)
FTS_PROPERTY_VIOLATED      свойство «Скидка ограничена» нарушается при
                           «сумма» ∈ (−∞, 0): результат 0 против предела −200000
FTS_PROPERTY_UNATTAINABLE  предел «результат ≤ 20 % от поля «сумма»» не берётся
                           нигде, где правила меняют результат

Диагностика выдаётся в JSON с кодом, уровнем и местом, поэтому пригодна для машинного разбора — это существенно для сценария, где код пишет не человек. Но код с приставкой FTS_ — свойство прежней поверхности, и поставленный двоичный 0.7.3 таких кодов не печатает вовсе: в исходниках компилятора (flang/self/) коды диагностик — с приставкой FLANG_, ни одного с приставкой FTS_. Три примера выше показывает песочница этой страницы; инструмента, который выдал бы их на вашей машине, сегодня нет.

Реализация

Реализация одна: компилятор flang написан на flang — 118 918 строк в 60 файлах flang/self/**. Он печатает сам себя и ещё в девять целевых языков.

Реализация-свидетель на TypeScript и JavaScript, служившая определением поведения языка, снята 20 августа 2026 года: 48 файлов, 56 072 строки. Разницу это меняет не только в счёте файлов. Всё, что считала она и чего перенос на flang ещё не догнал, сегодня не считается никем — и дерево называет это прямо, а не молчит: пробы, написанные против свидетеля, лежат в flang/test/ и не запускаются, потому что обрываются на ввозе снятого файла.

Лексер самоприменения тотален целиком — 99 функций из 99, — и это стоит отметить, потому что раньше их было 54 из 88. Разница ровно в одном: посимвольный проход перестал ходить по позиции. Убывала разность «размер минус позиция», то есть число без постоянного шага; теперь строка раскладывается в список, и рекурсия по хвосту доказывается.

Круг раскрутки замыкается семенем: в дереве лежит тот же компилятор, заранее напечатанный в C99. Из семени одним make собирается двоичный, тот печатает исходники заново, из печати собирается второй двоичный — и второй обязан совпасть с первым байт в байт. Сверяет это sh scripts/raskrutka.sh --check по семи файлам, а расхождение называет файлом, байтом и строкой, а не словом «не совпало». Семя перепечатывают руками, и пока оно не догнало исходники, круг разомкнут: проверка отказывает и говорит, на чём.

Установка и переносимость

Релиз содержит компилятор, уже напечатанный в C99, поэтому установка требует только компилятора C:

brew install digitable-lol/tap/flang

Формула кладёт один двоичный flang, библиотеку с заголовками, исходники рантайма C в share/flang/c и страницу руководства man flang. Из внешнего ей нужен только make; Node.js не нужен ни при установке, ни в работе. Такой способ распространения применяли и другие самоприменяющиеся языки: Go долго возил сгенерированный C, Nim возит до сих пор. Проблема начальной загрузки остаётся у тех, кто развивает сам язык.

Второй путь — собрать из исходников; он же нужен тому, кто правит сам язык:

git clone https://github.com/digitable-lol/flang.git
cd flang
make -C bootstrap -j8

На выходе bootstrap/flang — тот же инструмент, что кладёт формула. Node в сборке не участвует.

Что поставилось, спрашивают у самого двоичного: flang --version и flang -v отвечают flang 0.7.3, flang --help и flang -h перечисляют команды, man flang описывает их подробно.

Проверка живёт прямо в этом бинарнике: flang check файл.flang проходит разбор, связывание, типы, завершаемость, доказательства и примеры и печатает замечания словами — с кодом и местом. На сошедшемся файле в ответе стоит слово «проверено»; файл с ошибкой типов уводит двоичный кодом 1 и называет код беды — FLANG_TYPE. Ведомость доказательства печатает flang check --proof: машине — --json, в файл — --записать.

Команд у него двенадцать — check, test, run, emit, ast, tokens, facts, io, lock, package, repl и lsp. Считает двоичный тоже сам: flang run файл.flang --function «Имя» вычисляет функцию без Node и без cc. Печатает — во все девять целей: flang emit файл.flang --target c и так же go, rust, java, js, elixir, python, csharp; каталог из --out заводится сам.

Служба для ИИ-помощника лежит в том же двоичном, но подана не подкомандой, а ключом: flang --mcp-mode — JSON-RPC по стандартным потокам, по сообщению на строку, и запускает её помощник, а не человек. Средств у неё два: flang_check проверяет программу целиком и вместе с ведомостью, flang_prove отвечает, чем доказано одно названное обещание. Поля «ок» в ответах нет намеренно: три вердикта уходят порознь — «доказано» обо всех входах, «сетка N» на значениях автора и «объявлено, не доказано». Как прописать службу помощнику, печатает flang --mcp-mode --help.

Одну оговорку про печать надо знать заранее, иначе она встретится на первой же команде. Рантайм цели уезжает в вывод дословно, то есть читается с диска — ищется в --runtime, затем в $FLANG_RUNTIME_DIR, затем рядом с поставленным двоичным, в share/flang/<цель>. Формула кладёт один рантайм из восьми — share/flang/c, и в архиве релиза лежит только он. Значит из brew-установки сразу работает печать в C, а для остальных семи целей --runtime придётся указать на дерево исходников (flang/src/emit/<цель>).

Оболочка flang repl вычислитель пока не зовёт: она печатает сессию в C, собирает её системным cc против поставленного рядом рантайма и запускает; без cc она не выключается, а продолжает проверять разбор, типы и завершаемость.

Рантайм не содержит архитектурно-зависимых конструкций; выравнивание вычисляется объединением базовых типов, то есть берётся максимальное для платформы. Компилятор собран кросс-компилятором под RISC-V и запущен под эмуляцией без единой правки исходного кода: значения совпали с x86-64 до последнего знака, включая 0.1 плюс 0.2 = 0.30000000000000004, NaN и Infinity.

Ограничения

Функции первого класса есть и печатаются во все девять целей. Ограничение снято дефункционализацией: значение-функция — это тег, применение — диспетчер по конечному списку тегов. Поэтому цели без замыканий не страдают, и доказуемость завершения тоже: список тегов конечен, программа видна целиком, и анализ завершаемости не изменился ни на строку — изменилось, чем он питается.

Печать понижает программу ОДНИМ проходом перед бэкендами: тег становится вариантом, применение — вызовом диспетчера, и ни один из восьми бэкендов высшего порядка не видит вовсе. На программе без функций-значений проход возвращает тот же объект, поэтому печать остального не может измениться по построению — это проверено побайтово на всех программах репозитория.

Одно расхождение названо и оставлено: тег, которого программа не строит, можно подать снаружи, и отвергают его обе стороны, но разными кодами — вычислитель своим, напечатанный разбор своим. Свести их значило бы завести встроенную форму «возбудить ошибку с этим текстом» и править восемь рантаймов, то есть ровно то, ради отсутствия чего проход и написан. На значении, которое программа способна построить сама, расхождения нет.

Параметрический полиморфизм работает и в репозитории. Параметры типа объявляются у типов и у функций, применяются, выводятся при вызове, и печать их не стоит ничего — все девять целей типы и так стирают. Долгое время пользоваться этим было нельзя: парсер самоприменения полиморфизма не понимал, а корпус сверки собирается по маске каталога, поэтому первый же такой файл в библиотеке ронял неподвижную точку. Теперь понимает — и монада выразима: тип «Возможно» от «А», функция «Обернуть» от «А», связывание с параметром типа функция из «А» в («Возможно» от «Б») проходят разбор, типы и завершаемость. Библиотека на это переведена: flang/stdlib/optional.flang — это «Возможно» от «А», flang/stdlib/result.flang — «Результат» от «Значение» и «Беда», и туда же перешёл flang/stdlib/higher-order.flang, где четыре функции с попарно одинаковыми телами стали двумя, не потеряв ни одного примера.

Пользовательские свойства проверяются, а не доказываются. Доказываются завершение, типы и то, что принимает ядро доказательств: аксиом у него ноль, каждый шаг обязан назвать факт, а ядро только сверяет — искать оно не умеет и не должно. Пользовательские свойства и примеры проверяются на конечной сетке входов. Ведомость об этом печатает flang check --proof, и слова в ней не взаимозаменяемы: «доказано» стоит только у утверждений про все входы, а у сетки написано «нарушений не найдено», потому что «не нашли» и «нет» — разные утверждения; а там, где прогона примеров не было, стоит «нарушений НЕ ИСКАЛИ», и это третье, не сводимое к первым двум. Законы категорной поверхности сегодня не считает никто: слои есть, но к двоичному не подключены. Документация обязана различать эти случаи: «проверено на N входах» — не то же самое, что «доказано».

Расширение доказуемого возможно: условия, укладывающиеся в линейную арифметику, разрешимы, и подключение решателя к условиям верификации остаётся открытой задачей.

Эффекты выражаются описанием, а не выполняются. Язык чистый; работа с сетью или файлами — это построение описания действия, которое исполняет хозяин, среда, в которую напечатан модуль. Поручения объявлены самим языком одной закрытой суммой «Поручение» — не автором программы, потому что поручение есть контракт между языком и хозяином; набор покрывает файлы и октеты в них, каталоги, запуск процессов, соединения, запрос по сети, время и случайное число. Рядом — объявление план и команда flang io с четырьмя кодами выхода: 0 — дошли, 1 — программа сдалась сама, 2 — кривой вызов, 3 — сломался инструмент. Хозяин здесь свой, а не на Node, и границы у него названы отказом, а не молчанием: экрана нет (FLANG_IO_NO_SCREEN), своей криптографии нет ни байта, а https исполняет внешний curl — нет его, и в ответе FLANG_IO_NO_TLS. Функции, строящие поручения, проверяются обычными примерами и остаются тотальными — сеть в язык не входит. Монады ввода-вывода при этом нет, и вместо неё машина продолжений (flang/cat/SPEC.md, «Чем это не монада»). А вот слой исполнения для НАПЕЧАТАННОЙ программы сделан для одной цели из восьми — Node.

fspec: доказанное правило и то, что не даёт его отменить

Механизм зовётся fspec, и держится он на одном: спека — не markdown, а программа на самом языке. Правило предметной области записывается рядом с функцией, и компилятор доказывает его для всех входов, а не для тех, что придут в бою:

модуль «Спека 1: потолок скидки»

тотальная функция «Потолок скидки»
  принимает сумма: число
  возвращает число
  обеспечивает «скидка не больше 30» результат не больше 30
  пример «потолок»
    дано сумма равно 1000
    ожидается 30
  30

Частей четыре, и каждая делает работу. обеспечивает «имя» <цель> — само правило, то, что верно про результат; его несёт ядро доказательств при каждой проверке. требует «имя» <условие> — когда правило применимо; это обязанность зовущего, и невыполнимое вместе условие ломает первый же вызов. пример … ожидается … — образец из жизни, прогоняемый при каждой проверке, а не по требованию. использует «Спека N» из "./…" — связь со спекой, написанной раньше: подключение втягивает её функции, и отчёт наследницы содержит утверждения обеих.

Ради последней строки всё и затевалось. Правило «скидка не больше 30» записано один раз; через месяц приходит требование «промо-заказу скидка больше», кто-то пишет вторую функцию и вторую спеку — и осталось ли первое правило верным, отвечает не вычитка двумя людьми, а прогон:

make -C bootstrap -j8                  # собрать компилятор, если его ещё нет
bootstrap/flang io fspec/guard.flang   # или короче: ./ярлык spec:check

Ответ — одна строка с числами:

спеки согласны: спек 42, утверждений 295, и каждое доказано из нуля аксиом; обещаний в слепке 100, и у каждого цель та же; видов перевода 2, и каждый обещает то же, что основной

Спека принимается, только если доказано каждое её утверждение и каждое утверждение предшественницы осталось доказанным в отчёте наследницы. Опознаётся утверждение парой «функция плюс имя», поэтому переименованное или отменённое правило перестаёт подтверждаться и это видно прогоном. Код возврата 0 значит «принято», 1 — беда, названная файлом, функцией и именем утверждения. Довод при таком правиле приёмки прямой: аксиом у языка ноль, а значит из набора доказанных утверждений нельзя вывести ложь — выводить не из чего.

См. также

  • flang/SPEC.md — спецификация языка

  • flang/core/SPEC.md — контракт ядра FTS, написанного на flang

  • flang/self/SPEC.md — контракт самоприменения и его долги

  • flang/cat/SPEC.md — контракт категорной поверхности

  • fspec/README.md — спеки на flang и проверка их согласия

  • docs/guide/project-layout.ru.md — раскладка репозитория и куда что кладут

  • Страница языка — песочница, быстрый старт и решения задач

  • Справочник конструкций — карточка с примером на каждую форму языка

  • Словарь языка — все формы со всеми написаниями и все конструкции подряд, для вычитки целиком

  • Практикум — восемь ступеней, вердикт по заданию выносит исполнение

  • Курс по flang — устройство компилятора изнутри

  • Курс по FTS — предметная область как исполняемая спецификация на прежней поверхности

  • Библиотеки на языке — flang-tui и flang-env: что на flang уже написано целиком, без второго языка в дереве

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов
Дальше