npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@digitable-lol/flang

v0.7.3

Published

flang: проверяемый язык с тотальными и обычными функциями, ядром доказательств на нуле аксиом и печатью в C, Go, Rust, Python, Java, C#, Elixir и JavaScript.

Readme

English · Русский · Сайт документации

flang — язык, в котором спецификация исполняется и печатается в язык проекта

Правило пишется один раз, словами той области, из которой оно пришло, — и этот же файл оказывается сразу реализацией, набором проверок и документацией: примеры лежат внутри функции и прогоняются при каждой проверке, завершение компилятор доказывает сам, а не верит на слово, а одна команда печатает то же правило в C, Go, Rust, Java, JavaScript, Elixir, Python или C#. Написанный документ расходится с кодом на следующий день после того, как его приняли, и расходится молча. Здесь расходиться нечему: спецификация и есть программа.

Авторская поверхность русская. Английская тоже есть и лексится в те же самые идентификаторы (функция / function, свёртка / fold), но имена в спецификации принадлежат той предметной области, которая их придумала, и потому не переводятся ни в одну сторону — ни в исходнике, ни в напечатанном коде.

Определение — одним абзацем

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

Что отличает его от прочих чистых языков — компилятор доказывает, а не верит. Слово тотальная перед функцией — это обещание, что она завершается на любом входе, и компилятор его доказывает, отказываясь принять файл, когда доказательства нет. Обещание о результате (обеспечивает) — тоже не комментарий: часть таких обещаний ядро доказательств доказывает обо ВСЕХ входах, а не проверяет на нескольких. Примеры функции лежат рядом с её телом и исполняются при каждой проверке файла.

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

Что это за язык

flang (.flang, .fp, .фп или .фланг — расширения равноправны, ADR-0018) — язык с отступами вместо скобок, в котором у функции рядом с телом стоят её примеры и её обещание о результате. Признак тотальная — не пожелание: компилятор доказывает завершаемость сам и отказывается принять функцию, для которой доказательства нет. В языке есть суммы типов, списки, строки как данные, рекурсия, сопоставление с образцом, связывание модулей, категорная поверхность и поверхность конкурентности, а из одного исходника печатаются восемь целевых языков.

Компилятор один, и написан он на flang. Лежит он в flang/self/ — 57 файлов, 114 164 строки (замер 29 августа 2026; с тремя файлами в подкаталогах — 60 и 118 918) — и собирается в единственный двоичный файл, которому нужен только компилятор C:

make -C bootstrap -j8    # cc -std=c99 -Wall -Wextra -Werror -pedantic -O2, ни одного предупреждения
./bootstrap/flang --version

Он же печатает сам себя. sh scripts/raskrutka.sh прогоняет двоичный по собственным исходникам компилятора и выдаёт те же семь файлов на C, из которых он собран, — 7 файлов (шесть *.c/*.h и Makefile), 26 598 071 байт; ключ --check сверяет их с закоммиченными побайтно, а --bystro задаёт дешёвый вопрос: те ли входы у печати, из которых снято семя.

Сегодня эта дешёвая проверка красная, и это самый крупный открытый пункт дерева:

sh scripts/raskrutka.sh --bystro
→ расхождений 45. Компилятор правили, семя не перепечатали.             (код 1)

Всё, что описано ниже, при этом собирается и работает: семя — рабочий компилятор. Оно просто не СЕГОДНЯШНИЙ компилятор: правки в flang/self/**, сделанные после последней перепечатки, в двоичный из make не попали. Печать плана процессов в C, например, в исходниках написана, а в семени её нет ни строки (grep -c 'flang_conc.c' bootstrap/compiler_flang.c → 0). Перепечатка стоит часы и сотни гигабайт, поэтому её делают намеренно, а не на каждое вливание.

Спецификация языка — flang/SPEC.md; чем пять слоёв компилятора обязаны друг другу — flang/self/SPEC.md. Эта страница за них не заходит.


Что где лежит

У корня 11 каталогов, и раскладка простая: почти всё, что относится к языку, лежит внутри flang/, а снаружи — только то, что языком не является: точка раскрутки, упаковка, замеры, документация и сами примеры.

bootstrap/        точка раскрутки: компилятор, напечатанный в C99 — «make -C bootstrap»
flang/self/       сам компилятор: лексер, разбор, типы, завершаемость, ядро доказательства, восемь целей печати
flang/core/       лексер, разборщик, вычислитель и печать JSON, написанные на flang
flang/src/        рантаймы целей, уезжающие в напечатанный код дословно — C, Go, Rust, Java, JS, Elixir, Python, C#
flang/stdlib/     стандартная библиотека языка; её указатель печатается из самих модулей
flang/proof/      что ядру доказательства позволено заключать, а что нет, и почему
flang/проверки/   проверки, написанные на flang; обходит их сам двоичный
flang/test/       прежний набор проб: написан против удалённой реализации и сегодня не запускается
flang/bin/        flang-lsp: переходник, отдающий вызов двоичному, а не место для смысла
flang/cat/        контракт категорной поверхности
flang/conc/       контракт конкурентности и её примеры
examples/         193 программы на flang в 22 наборах: leetcode, rosetta, crypto, io, web, db, wal, library-api и ещё четырнадцать
editors/          языковой сервер .flang, подсветка для vim и заготовка заявки в github-linguist
packaging/        Homebrew, asdf, запускатель для npm и страница flang.1
scripts/          перепечатка точки раскрутки, указателя библиотеки, журнала изменений и релизного C
benchmarks/       стенд, зафиксированная база измерений и замер авторства моделей
web/              flang во вкладке: сборка программы в WebAssembly и прогон её в браузере
.claude/          навыки помощника разработчика: правила базы знаний
fspec/            спецификация системы, написанная на самом языке, и проверка её согласия
docs/             документация; README и SPEC остаются рядом с кодом
tasks/            открытая работа дерева: по файлу на задачу, заводится и закрывается руками
.github/          CI и публикация в npm

Файлы, лежащие в корне россыпью, и что каждый там держит.

| файл | что держит его именно в корне | | --- | --- | | README.md · README.ru.md | первая страница репозитория. GitHub взял бы её и из .github/, и из docs/, но открывший репозиторий читатель попадает на корневую | | LICENSE · LICENSE-RU.md | лицензия BSD-2-Clause и её русская редакция. Определитель лицензии GitHub смотрит только корень: унеси LICENSE — и репозиторий станет «без лицензии». Перевод силы не имеет, но читают чаще его | | CONTRIBUTING.md | GitHub подставляет её в форму задачи и запроса на слияние; ищет в корне, в .github/ и в docs/ | | CHANGELOG.md · changelog.json | одна структура, две печати: страницу читает человек, JSON читает программа. Оба печатаются из тегов и тем коммитов (scripts/build-changelog.mjs), править руками запрещено | | AGENTS.md | указания агенту: помощник ищет файл с этим именем в корне рабочего дерева | | package.json · package-lock.json | манифест установки через npm. Оба печатаются из scripts/emit-package.flang и руками не правятся: ./ярлык пакет печатает, ./ярлык пакет:проверка отказывает, если файл разошёлся с объявлением. Зависимостей у манифеста ноль, и второго компилятора он не везёт: ставит он тот же двоичный, что и brew, собирая его из bootstrap/ при установке. npm читает манифест только из корня публикуемого пакета | | ярлык · ярлыки.flang | ярлыки этого дерева и точка входа, которая их запускает. ярлыки.flang — список: программа на flang, проверенная типами, с планом, который краснеет, если ярлык называет несуществующий файл; ярлык — оболочка на sh: 69 строк кода в 160, остальное пояснение; она спрашивает у двоичного строку команды и запускает её. Оба в корне потому, что именно там человек набирает ./ярлык спеки:проверка, и потому, что ярлык считает пути от своего каталога | | .gitignore · .gitattributes | git читает их из корня |

Двоичный собирается одним cc. make -C bootstrap — и всё; ни пакетного управляющего, ни постороннего языка на пути сборки нет. package.json описывает не сборку языка, а установку его из npm, и зависимостей у него ноль (npm ls --all печатает (empty)). npm install делает две вещи: кладёт flang в node_modules/.bin и зовёт make на том же bootstrap/, чтобы команда flang была ТЕМ ЖЕ двоичным компилятором, что ставит brew. Нужны cc и make; без них установка проходит, а отказ называет причину и что делать. Две команды, которые пакет кладёт в $PATH, — flang и flang-lsp — это тонкие запускатели, отдающие вызов тому самому двоичному: bin в npm обязан быть файлом, который запустит node. Ничего про язык в них не решается, и тихой подмены на случай, если двоичного нет, там тоже нет — вместо неё отказ с названной причиной.

Чем язык проверяется сегодня, а чем не проверяется. Прогонов два, и оба ведёт двоичный: sh flang/проверки/обход.sh — 180 проверок, написанных на самом flang, три секунды, — и

flang test <каталог>, который гоняет примеры, объявленные внутри функций (806 из них в наборе LeetCode). Записанная ведомость, с которой обход сличается, — flang/проверки/ведомость.txt, по строке на проверку; wc -l по ней и даёт 180.

эти проверки и примеры библиотеки и ядра; набор LeetCode и остальное дерево обходятся по тегу — этот обход идёт больше часа.

flang/test/ — остаток, и он невелик. В нём 175 файлов (git ls-files flang/test | wc -l),

из них 163 — образцы,

который их и гоняет; шага pretest нет вовсе. Прежняя реализация на JavaScript, против которой эти пробы писались, удалена, а оставшееся подрезано до того, что разрешается: каждый ввоз в этих четырёх файлах указывает на существующий файл — все пятнадцать, проверено 29 августа 2026. Это не то же самое, что «зелено», и разницу стоит назвать: npm test сегодня красный — 38 проверок, 29 прошли, 9 упали (замер 29 августа 2026). Шесть из девяти — сторож жаргона: он читает прозу самого дерева, находит 18 мест жаргона, а у одного отданного ему файла нет записанного долга. Ещё три — цель go, которая не собралась в этой среде, а не беда дерева. Сказано здесь, а не оставлено выясняться по красному логу.

Как раскладывать свой проект — отдельный документ: Раскладка проекта.


Установка

Чтобы поставить flang, хватит компилятора C99. Компилятор языка написан на самом языке и печатается в C, поэтому релиз содержит уже напечатанный C99.

brew install digitable-lol/tap/flang

Или прямо из релизного архива, где не нужно ничего, кроме cc и make:

tar -xzf flang-*-c.tar.gz   # внутри — исходники на C99, Makefile и страница flang.1
make                        # cc -std=c99 -Wall -Wextra -Werror -pedantic -O2
sudo make install           # с 0.5.1; в архиве 0.5.0 такой цели нет — там
                            # скопируйте `flang_cli` в bin/flang руками
flang --help                # двенадцать команд, языковой сервер среди них
flang check м.flang         # разбор, типы, завершаемость, доказательства, примеры — словами, не JSON
flang                       # на терминале — оболочка; под конвейером — прогонщик

flang emit --target c просит четыре файла рантайма, и до 0.5.3 их не было ни в архиве, ни в установке. Печать кладёт в вывод flang_runtime.h, flang_runtime.c, flang_cli.c и flang_repl.c дословно, а значит читает их с диска — по очереди из --runtime каталог, из $FLANG_RUNTIME_DIR и из share/flang/c рядом с командой. С 0.5.3 их кладут туда и формула, и плагин; в архивах 0.5.0—0.5.2 их нет вовсе, и flang emit отвечает «не найдены исходники рантайма C». Пока стоит старая версия — временный обход, проверенный прогоном: взять их из клона.

git clone https://github.com/digitable-lol/flang
flang emit м.flang --target c --out вывод --runtime flang/flang/src/emit/c
# то же самое разом: export FLANG_RUNTIME_DIR=$PWD/flang/flang/src/emit/c

Верхние файлы распакованного архива для этого не годятся, хотя имена те же: там лежат напечатанные копии, у них первой строкой стоит шапка «Сгенерировано flang», и печать их отвергает, чтобы не приписать шапку второй раз. Остальные семь целей берут свои рантаймы так же, из flang/src/emit/<цель>/.

Формула Homebrew — packaging/homebrew/flang.rb, и её раздаёт tap. Плагин asdf (и mise) ставит тот же архив из тех же релизов, его исходник — packaging/asdf/, но asdf клонирует плагин целым репозиторием, а этот репозиторий ещё не опубликован: пока плагин — исходник, а не способ установки. Ни тому, ни другому не нужно ничего, кроме компилятора C. Так распространяются самоприменяющиеся языки: Go долго возил сгенерированный C, Nim возит до сих пор.

Стоит назвать прямо, что это за бинарник. Он отвечает на все двенадцать команд языка, и языковой сервер редактора — одна из них: check, test, run, emit, ast, tokens, facts, io, lock, package, repl и lsp. Печатает он во все восемь целей. Чего у него нет — отдельного вычислителя, и потому flang repl вычисляет единственным честным для себя способом: печатает сессию в C, собирает её системным cc против рантайма, поставленного рядом, и запускает. Без cc оболочка не выключается: она по-прежнему проверяет разбор, типы и завершаемость и говорит об этом один раз. Чего он ещё не судит — категорной поверхности и процессов: моноидов, монад, морфизмов, надзора и объявленных свойств. Молча такую программу он при этом не пропускает: flang check называет, чего не проверил, и отвечает кодом 2, потому что «замечаний нет» читалось бы здесь как «проверено», а это неправда.

Ставьте из клона, а не из реестра. Пакет называется @digitable-lol/flang, но под этим именем в npm ещё ничего не выложено: то, что лежит там сегодня, — сборка от 7 августа под прежним именем, и она кладёт в $PATH команды, которых в этом дереве уже нет. Пока новое имя не приехало в реестр, берите клон и соберите компилятор из него:

git clone https://github.com/digitable-lol/flang && cd flang
make -C bootstrap -j4        # нужны только cc и make
sudo make -C bootstrap install
flang --version

Что такое точка раскрутки, чем она проверяется и как обновляется — bootstrap/README.md.


Одна функция, восемь целей

Это examples/leetcode/035-search-insert-position.flang — LeetCode 35: номер, под которым значение стоит в отсортированном списке, а если его там нет — номер, куда его следовало бы вставить. Одна свёртка, завершение доказано:

тотальная функция «Место вставки»
  принимает элементы: список числа, цель: число
  возвращает число
  пример «Пример 1 из условия»
    дано элементы равно [1, 3, 5, 6]
    дано цель равно 5
    ожидается 2
  свёртка элементы начиная с 0 как акк и эл → если эл меньше цель то акк плюс 1 иначе акк

Всё, что ниже, получено запуском

flang emit examples/leetcode/035-search-insert-position.flang --target c --out ./out-c
#              …и то же самое с go, rust, python, java, csharp, elixir, js

и вставлено дословно, а не переписано руками. Семь целей печатают модуль, рантайм, прогонщик «JSON на входе — JSON на выходе», файл сборки и — там, где у цели такое есть, — манифест пакета (go.mod, Cargo.toml, flang.csproj); цель JavaScript печатает один самодостаточный модуль и рядом тот же прогонщик (flang_cli.js, снимается ключом --no-cli) — им и держится объявленный предел глубины вызовов при обычном запуске. Здесь показаны две цели из восьми и только сама функция; остальные шесть читаются так же — запустите команду и посмотрите.

/*
 * Функция flang «Место вставки».
 *
 * Тотальная: завершение доказано анализом завершаемости (totality.mjs).
 * @param elementy — «элементы»: список: число
 * @param cel — «цель»: число
 * @return значение: число
 */
fl_status mesto_vstavki_mesto_vstavki(fl_ctx *ctx, fl_value elementy, fl_value cel, fl_value *result, fl_error *error) {
  fl_value fl_t1 = fl_nothing();
  FL_TRY(fl_require_list(ctx, elementy, "свёртка", &fl_t1, error));
  fl_value akk = fl_number(0.0); /* «акк» */
  const fl_mark fl_t3 = fl_region_open(ctx);
  for (size_t fl_t2 = 0; fl_t2 < fl_t1.as.list.count; fl_t2 += 1) {
    const fl_value el = fl_t1.as.list.items[fl_t2]; /* «эл» */
    if (el.tag != FL_NUMBER || cel.tag != FL_NUMBER) FL_TRY(fl_not_order(ctx, el, cel, error));
    bool fl_t4 = false;
    FL_TRY(fl_cond(ctx, fl_flag(el.as.number < cel.as.number), &fl_t4, error));
    fl_value fl_t5 = fl_nothing();
    if (fl_t4) {
      if (akk.tag != FL_NUMBER) FL_TRY(fl_not_numbers(ctx, "add", akk, fl_number(1.0), error));
      fl_t5 = fl_number(akk.as.number + 1.0);
    } else {
      fl_t5 = akk;
    }
    akk = fl_t5;
    FL_TRY(fl_region_recycle(ctx, fl_t3, &akk, error));
  }
  FL_TRY(fl_region_close(ctx, fl_t3, FL_OK, &akk, error));
  *result = akk;
  return FL_OK;
}

В шапке этого файла до сих пор назван totality.mjs — файл, которого в дереве больше нет. Строку эту пишет сама печать, и здесь она оставлена как напечаталась, а не подчищена: вставка, поправленная рукой, перестаёт быть уликой.

/**
 * Функция flang «Место вставки».
 *
 * Тотальная: завершение доказано анализом завершаемости (totality.mjs).
 *
 * @param {Array<number>} elementy — «элементы»
 * @param {number} cel — «цель»
 * @returns {number}
 */
export function mestoVstavki(elementy, cel) {
  const $t1 = $requireList(elementy, "свёртка")
  let akk = 0
  for (const el of $t1) {
    let $t2
    if ($cond($lt(el, cel))) {
      $t2 = $add(akk, 1)
    } else {
      $t2 = akk
    }
    akk = $t2
  }
  return akk
}

Цель JavaScript встраивает только те вспомогательные функции рантайма, которые этот модуль действительно использует, — поэтому сам модуль остаётся одним самодостаточным файлом, который идёт и в Node, и в браузере. Прогонщик печатается рядом отдельным файлом и в модуль не входит: браузеру он не нужен, а Node с ним получает объявленный предел глубины вызовов по-настоящему.

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

Насколько цели печати на самом деле проверены

Честно — по-разному.

Сильнее всех проверена C, и проверяет её самая большая программа, какая есть, — сам компилятор. sh scripts/raskrutka.sh печатает в C сам компилятор — flang/self/bootstrap/compiler.flang и 37 файлов на flang, которые он тянет за собой, плюс четыре файла на C, копируемых дословно; список записан в scripts/otpechatok-semeni, по хешированной строке на файл — 42 строки, — и сам этот список сейчас отстал от дерева: 41 хеш из 42 с исходниками не сходится (sha256sum -c по этому файлу), потому что переписывает его только перепечатка. make -C bootstrap собирает эти 25 МиБ под -std=c99 -Wall -Wextra -Werror -pedantic -O2 без единого предупреждения. Потом собранный из этого C двоичный печатает те же исходники ещё раз, и напечатанное сверяется с закоммиченным — все 26 598 071 байт. Цель печати, портящая хоть что-то на таком объёме, не пережила бы прогона через саму себя.

Оговорка с начала страницы верна и здесь: сегодня эта сверка красная — исходники ушли вперёд, а семя с тех пор не перепечатывали.

У остальных семи проверки, которая шла бы сегодня, нет. Каждую из них сверяли с удалённой реализацией: одна и та же программа печаталась в пустой каталог, собиралась настоящим инструментарием и запускалась настоящим процессом на сетке входов из её собственных примеров и намеренно неверных аргументов. Жили эти прогоны в flang/test/emit-*.test.mjs, ушли они вместе с тем, с чем сверялись, и замены им нет. Сказать сегодня можно ровно одно: печатают все восемь — и это проверено запуском каждой.

Чем эта дыра оборачивается, видно там же, где видна разница: flang emit не проверяет программу вовсе — ни типов, ни завершаемости. Значит, flang check зовите отдельно и читайте, что он скажет.


Зачем это нужно

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

Разобранный пример от исходника до напечатанного постусловия — Зачем это нужно.


Настоящая задача, а не «hello world»

LeetCode 121 — наибольшая прибыль от одной покупки и одной продажи, один проход, состояние в записи из двух полей. Это examples/leetcode/121-best-time-to-buy-and-sell-stock.flang целиком, без заголовочного комментария:

объект «Сделка»
  минимум является числом
  прибыль является числом

тотальная функция «Лучшая прибыль»
  принимает цены: список числа
  возвращает число
  пример «Пример 1 из условия»
    дано цены равно [7, 1, 5, 3, 6, 4]
    ожидается 5
  пример «Пример 2 из условия»
    дано цены равно [7, 6, 4, 3, 1]
    ожидается 0
  пример «Пустой список»
    дано цены равно пустой список
    ожидается 0
  разбор цены
    случай пусто
      то 0
    случай голова и хвост
      пусть начальное равно запись «Сделка» с минимум равным голова и прибыль равным 0
      пусть итог равно свёртка хвост начиная с начальное как акк и цена
        пусть минимум равно если цена меньше акк.минимум то цена иначе акк.минимум
        пусть сегодня равно цена минус акк.минимум
        пусть прибыль равно если сегодня больше акк.прибыль то сегодня иначе акк.прибыль
        запись «Сделка» с минимум равным минимум и прибыль равным прибыль
      итог.прибыль

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

flang test examples/leetcode/121-best-time-to-buy-and-sell-stock.flang
flang test examples/leetcode/     # все 82 файла и 806 примеров одним прогоном

Наборов примеров два. В examples/leetcode/ лежат 82 решения, в них 301 функция, из которых 299 доказаны тотальными; два исключения оставлены нарочно и объяснены в файле (202-happy-number.flang: цикл «пока число не повторилось» завершается, а доказать это языком нечем). У каждого решения комментарий объясняет не только алгоритм, но и то, где язык упёрся: почему проверка «символ уже в окне» линейна — множества в языке нет; почему таблица динамики стоит квадрата — приписывание в конец копирует список; почему Single Number выходит на O(n²) — побитовых операций нет. В examples/rosetta/ лежат 14 канонических задач Rosetta Code, каждая записана дважды — 28 файлов: на русской поверхности и на английской. Стандартная библиотека (flang/stdlib/) написана так же — 42 модуля, 1474 функции, из них 1467 доказаны тотальными, и 2713 примеров, которые прогоняются при каждой проверке:

ls flang/stdlib/*.flang | wc -l
cat flang/stdlib/*.flang | awk '/^(тотальная )?функция «/{f++} /^тотальная функция «/{t++} \
  /^[[:space:]]+пример «/{e++} END{print f, t, e}'

Кроме повседневных модулей (lists, strings, numbers, sets, hashmap, dictionary, tree, json, utf8, datetime, higher-order, optional, result) в ней теперь два драйвера базы данных (postgres по проводу, sqlite — чтение файла и сборка его с нуля), сеть (http, wire, redis, tls), криптография, написанная на самом flang (aes, x25519, sha1, sha256, hmac, kdf, der, x509, crl, rsa, ecdsa, scram), и движок регулярных выражений без возврата (automaton, 63 функции, все доказаны тотальными). higher-order — та, что на функциях-значениях: свёртка, отображение, фильтр, поиск, сортировка и композиция принимают функцию аргументом.


Что даёт признак тотальная

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

Какие виды убывания принимаются, что такое объявленная мера и почему это не педантизм — Что даёт признак тотальная.


Один компилятор и как он пересобирается

Тот компилятор, который собирают, и тот, который написан на flang, — один и тот же. Это flang/self/, пять слоёв от лексера до печати, плюс flang/core/ — лексер, разборщик, вычислитель и печать JSON, тоже на flang. Второй реализации ни одного из этих слоёв в дереве нет.

Остаётся классический вопрос, откуда берётся первый двоичный, и ответ на него закоммичен, а не обещан: в bootstrap/ лежит этот же компилятор, уже напечатанный в C99, — семь файлов, 26 598 071 байт, — так что одного make хватает, чтобы получить работающий flang. Дальше этот двоичный печатает исходники компилятора заново, и напечатанное ОБЯЗАНО совпасть с тем, что лежит в bootstrap/; sh scripts/raskrutka.sh --check — это и есть та сверка, и компилятор, у которого печать разъехалась с деревом, её не проходит. Сейчас не проходит: исходники обогнали семя на 45 входов, и перепечатка — первый открытый пункт дерева.

Про этот круг стоит знать две вещи, прежде чем на него опираться.

Сверка подорожала. Записанный замер — 19 минут 58 секунд и 25,1 ГиБ пика на 256 ядрах (scripts/raskrutka.sh, 22 августа 2026); нужны cc и make. Это нижняя граница, а не обещание: замыкание с тех пор выросло. Пока сравнивать могла вторая реализация, та же сверка стоила 3,4 секунды и не требовала компилятора C вовсе, — то есть самая сильная проверка репозитория подорожала примерно в триста раз. На обычный раннер GitHub она не помещается, поэтому в CI её нет: её зовут руками перед тем, как правка в flang/self/ или в flang/src/emit/c/ уедет в дерево.

И собственные исходники двоичный проверить не может. flang check flang/self/bootstrap/compiler.flang исчерпывает бюджет шагов и отвечает FLANG_RECURSION_LIMIT: бюджет здесь один на всю команду, а не свой на каждое вычисление, и ключа, поднимающего предел, у check нет. Печать при этом проходит — потому что flang emit не проверяет программу вовсе. То есть пересобрать компилятор можно, а убедиться, что пересобираешь исправный, — нечем.


Модули, стандартная библиотека и целый проект

examples/import-check.flang:

модуль «Проба импорта»
  использует «Lists»

тотальная функция «Сумма пробы»
  принимает элементы: список числа
  возвращает число
  «Сумма» от элементы

Пути в строке нет: модуль ищется по имени — по тому, что стоит первой строкой файла в модуль «Списки». Смотрят в каталоге самого файла, затем в каждом каталоге выше, пока там есть файлы .flang, затем в библиотеке, поставленной вместе с компилятором. Переложенный в другой каталог модуль продолжает находиться; лежащий в стороне называется прямо — использует «Lists» из "путь".

Выборочная форма берёт только то, что названо, — использует «Lists» только «Сумма», «Длина», — ею же разрешается конфликт имён между двумя модулями.

Как это раскладывается в полноразмерном проекте, показывают два примера, и различать их стоит.

examples/library-api — предметная половина сервиса библиотеки: правила, разбор и обработка данных, семь модулей на flang. HTTP и хранилище у него когда-то жили в хозяине на Node; хозяина сняли 20 августа 2026 вместе с остальной оснасткой на JavaScript, и на его место ничего не встало. Значит этот пример показывает, как разложить проект, а не как ответить на запрос.

examples/web/shortener — тот, который отвечает: сокращатель ссылок, где между входными и выходными байтами нет ни одной строки, написанной не на flang, включая хранилище, маршрутизацию, процессы и надзор. GET /здоровье — 200, POST /ссылки — 201, GET /с/{код} — 301 с сосчитанным переходом, DELETE — 204.

Граница проведена по одному правилу — если у куска логики может быть пример, он уезжает в модуль языка, где пример исполняется, а не лежит в комментарии, — а правила именования файлов, раскладки каталогов, нарезки на модули и прогона в CI, выведенные отсюда, собраны в Раскладке проекта.


Развитие языка

Работа идёт в клоне, и собирать нужно ровно одно — сам компилятор: make -C bootstrap -j8 занимает около минуты и даёт bootstrap/flang, который сразу отвечает на ./bootstrap/flang check flang/stdlib/lists.flang.

Что запускать после правки:

sh flang/проверки/обход.sh            # 180 проверок, написанных на flang, три секунды
./bootstrap/flang test flang/stdlib/  # примеры библиотеки — в модулях их написано 2287
./bootstrap/flang test flang/core/    # примеры ядра
sh scripts/raskrutka.sh               # только если менялся flang/self/ или рантайм на C
sh scripts/raskrutka.sh --check       # …и сверить перепечатанное с деревом

Точка раскрутки перепечатывается тем же коммитом, что и правка, которая её сдвинула. Первые три CI гоняет их на каждый пуш.

flang/test/ в этот список не входит и не войдёт, пока его не перепишут: см. выше про npm test.


Остальной репозиторий

  • Работающая служба — examples/web/shortener: сокращатель ссылок, где между байтами запроса и байтами ответа нет ничего, кроме flang.
  • Полноразмерная раскладка — examples/library-api: предметная половина REST-сервиса библиотеки, семь модулей на flang. Он отвечает на один вопрос — что где лежит и почему именно там.
  • Остальные примеры — ещё 178 программ в examples/, в 22 наборах; что где лежит, перечислено в examples/README.md.
  • Редакторы — языковой сервер .flang (flang lsp, обёрнутый для npm как editors/flang-lsp) и подсветка для vim в editors/vim.
  • Замеры — стенд скорости и замер авторства моделей в benchmarks/.

Вся документация с указателем — docs/README.md: руководство, отчёты замеров, база знаний, подача на конференцию. Часть её до сих пор описывает дерево, в котором реализаций было две, и за этой страницей не поспела.

Дальше по теме: Раскладка проекта · спецификация flang · контракт самоприменения · контракт ядра на flang · ядро доказательства · контракт теорката · контракт конкурентности.

Правило именования документации: файл .md без языкового суффикса — английский, X.ru.md — его русская версия. Исключение — README.md и SPEC.md рядом с кодом: они остаются под этими именами на том языке, на котором написаны, потому что GitHub показывает их титульной страницей каталога.


Известные ограничения

Названы прямо, потому что на проект с необозначенными границами нельзя опереться.

  • Доказать обычную функцию библиотеки по-прежнему чаще всего нельзя. Повторяемый замер — это двадцать функций стандартной библиотеки, взятых в порядке «файл, объявление» каждая девятая, чтобы нельзя было набрать удобных. На этой выборке ядро доказательства закрывает утверждения у четырнадцати функций из двадцати, а говорят о функции по существу одиннадцать: остальные либо ослаблены (утверждение переживает подмену тела заглушкой той же подписи, значит верно про любую функцию такой подписи), либо пересказывают тело. Разделяет их прогон, а не чтение: ./ярлык доказательства:20. Ряд по дням — 0 → 2 → 4 → 5 → 9 → 11. И два утверждения из двадцати недоказуемы потому, что неверны: одно ложно на бесконечности, у другого записано «или» там, где нужна импликация.
  • Второго мнения о языке больше нет. Прежняя сверка доказывала, что две независимые реализации понимают одну и ту же программу одинаково. Осталась сверка «закоммиченный C равен тому, что печатают сегодняшние исходники» — она ловит устаревший артефакт и не может поймать расхождение пониманий, потому что второго понимания больше не существует.
  • У семи целей печати из восьми проверки, которая шла бы сегодня, нет. Сказано выше.
  • Категорную поверхность и процессы двоичный не судит. Он говорит об этом сам и отвечает кодом 2, а не пропускает такую программу молча.

Полный список — что значит «доказано» против «проверено», чего нет в языке, где упирается категорная поверхность — Известные ограничения и flang/SPEC.md §10; оба писались, когда реализаций было две, и местами это в них ещё написано.


Состояние

0.x — фаза проектирования языка. Канонический вид JSON и коды диагностик считаются поверхностями совместимости; синтаксис растёт через задокументированные предложения.

Лицензия

BSD 2-Clause. Дословный текст лицензии лежит в LICENSE; LICENSE-RU.md объясняет намерение по-русски и юридической силы не имеет. Прежние версии выходили под Apache-2.0, и все, кто получил код так, эти права сохраняют: изменение относится к последующим версиям.