Pull to refresh
2
Doniyor Botirov@botiroff

User

-1
Rating
2
Subscribers
Send message

По asm.js вы правы, и я сказал неточно: в V8 AOT для asm.js не было никогда — там он шёл обычным JIT. AOT делал Firefox в OdinMonkey: модуль с “use asm” валидировался и компилировался до исполнения. Отсюда и разночтение.

А вот «у AOT аннотация бы растворилась» — это как раз то, что AOT с ней и делает. Валидатор по |0 доказывает, что значение помещается в int32, и генерирует целочисленную арифметику; сама аннотация в машинный код не попадает именно потому, что её потребили на этапе компиляции. Растворение — признак того, что она сработала, а не того, что её там не было.

И вы точно описали мотив: сужали намеренно, ради памяти и скорости, компилируя из C/C++. Это ровно моя мысль, только сказанная лучше — |0 был инструментом производительности в статически типизированном подмножестве, а не типом в языке. Поэтому перенести его в динамический интерпретатор нельзя: аннотировать нечего, а диапазон сузится.

Про GC вы правы, и это самый сильный пункт: модель языка я действительно не описал. Отвечу конкретно, потому что от неё всё и зависит.

Без сборщика Sable не обойдётся, и вот почему — три свойства, каждое из которых у него уже есть:

  1. Замыкание переживает кадр, в котором создано fn счётчик() { let n = 0; return fn() { n += 1; retur

  2. Взаимные ссылки — обычная структура, не экзотик struct Узел { имя, сосед = nil } a.сосед = b; b.сосед = a

  3. Цикл замыкается и через функцию h.дай = fn() { return h }

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

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

Путь Blazor вы назвали верно: собственный сборщик в леалистично и не требует ждать wasm-gc — просто этоотдельная работа размером с сам интерпретатор.

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

Спасибо, по первому пункту разберу подробно — там смешаны три разные вещи.

number|0 — это не целочисленный тип, а приведение к int32. И для языка с числами-double это шаг назад, а не вперёд: double даёт точные целые до 9007199254740991, а |0 — до 2147483647. На большем оно не «магически превращается», а ломается:

231 | 0 → -2147483648 (253 - 1) | 0 → -1

В asm.js |0 работал не как тип, а как аннотация для AOT-компилятора: разметка статически типизированного подмножества, чтобы движок скомпилировал его заранее. В динамическом интерпретаторе аннотировать нечего — значение всё равно останется боксированным, а диапазон сузится в четыре миллиона раз.

Uint8Array — это буфер байтов, а не скалярный числовов языке целые» отношения не имеет.

BigInt — единственный реальный кандидат, и я его расс, и вот чем платят:

Number: 20 мс BigInt: 110 мс → в 5,5 раза медленн 1n + 1 → TypeError: Cannot mix BigInt and other types 7n / 2n → 3n (а 7 / 2 = 3.5) JSON.stringify(1n) → TypeError

То есть это не «добавить тип», а решить заново, что в языке значит /, что происходит при смешивании, и как это сериализуетсцелочисленный тип у меня в открытых вопросах, а не в атье он честно назван отсутствующим, а не «почтиесть».
Про мотивацию — вопрос справедливый. Язык здесь не цель, а предмет: статья про систему проверок, которая позволила переписацеликом и не сдвинуть ни одного эталона. Но принцип уательный — ошибаться громко и рано, впротивоположность JavaScript:

  • выход за границы списка и отсутствующий ключ — ошибка, а не nil; - переполнение — ошибка: ни бесконечности, ни NaN в я

  • "сумма: " + 5 — ошибка, а не склейка; - у каждой ошибки позиция, строка исходника с кареткоа про опечатку. Молчаливый nil и тихий inf — источник багов, которые троен так, чтобы они всплывали в момент написания. Про запуск — согласен, зависимость от Node реальна. Эсборки, нет зависимостей в рантайме, весь тулчейн —node src/cli.ts file.sable. Проект в первую очередь читают, а не устанавливают, и здесь важнее, чтобы между исходником и застояло ничего. wasm со своим рантаймом — это другой проект: понадобира, либо ждать wasm-gc. Разумный путь, если цельсменится на «раздавать бинарник». Self-hosted — честно: пока нет. Чтобы компилятор написать на самом Sable, языку нужны вещи, которых у него нет на 0.2. Это классическая веха, и она интереснее wasm, но сначала работа со строками, иначе получится компилятор,который стыдно показать.

Попробовать без установки, если интересно: https://botiroff-d.github.io/sable/

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

Из практики добавлю два вопроса, которые стоит задать такому состоянию. Первый: у него есть граница по времени? Пока не сказано, за сколько неопределённость обязана разрешиться и что происходит, если архив накопителя недоступен дольше этого срока, состояние тихо превращается в свалку, которую никто не разбирает. Второй: кто владелец очереди неразрешённых? Технически они не мешают, поэтому мониторинг их не показывает, а бизнес-смысл у каждой записи есть.

И про тестирование этого контура. Такие вещи бесполезно ловить нагрузкой: нужный отказ должен случиться ровно между «команда отправлена» и «ответ получен», и на настоящем железе это окно почти не поймать. Обычно помогает вынести всё, что связано со временем и вводом-выводом, за границу логики, а в тестах подменять их управляемым источником: тогда обрыв ставится ровно в нужную точку, а не подкарауливается.

Хорошо разложено, особенно мысль про то, что восстановление зависимости и восстановление системы — разные события: 12:00 сервис здоров, 12:03 разобрана очередь.

Добавлю то, чего мне не хватило в разделе про fault injection, — воспроизводимость. Хаос-эксперимент ловит нарушение инварианта один раз, а дальше начинается самое дорогое: попытка повторить. Если задержки, потери и порядок доставки берутся из настоящей сети, конкретное расписание не повторяется, и находка превращается в «мы такое однажды видели». Практический выход — сделать источник недетерминизма своим: время, таймеры, порядок готовых колбэков и решения «доставить / потерять / продублировать» брать из генератора с сидом. Тогда упавший прогон адресуется одним числом, кладётся в регрессию и воспроизводится на чужой машине.

И второе, ближе к вашему списку инвариантов. У набора инвариантов та же беда, что у любых тестов: если он всегда зелёный, по цвету не отличить «ловит» от «не смотрит». Дешёвая проверка — временно выключить в системе правило, которое эти инварианты обязаны защищать (например, атомарность проверки ключа идемпотентности), и потребовать, чтобы тесты покраснели с конкретным именем нарушенного инварианта, а не просто упали. Если не покраснели — проверяется не то, что кажется.

Information

Rating
Does not participate
Location
Worland, Wyoming, США
Date of birth
Registered
Activity

Specialization

Фулстек разработчик, Инженер встраиваемых систем
Ведущий
Английский язык
Python
Git
Базы данных
Redis
PostgreSQL
Docker