Ромб у Unison ломается не от подъёма версии, а от правки типа: за семь версий base не изменился ни один тип
Поправка к unison-measured, и она в пользу Unison. Замер прогоном там верен, но поставлен на худший случай: тип нарочно переписали (Money Nat → Money Nat Text), и ошибка типов на границе — правильный ответ на такую правку. Вопрос, который замер не задал: как часто версия вообще меняет тип.
Ответ — почти никогда, и это проверено по хешам в Unison Share.
| что сравнивали | различающихся типов |
|---|---|
@unison/base 7.12.0 против 7.19.2 (семь минорных версий) | 0 |
в том числе data.List — 170 сущностей | 0 (изменился один терм документации) |
в том числе Text — 108 сущностей | 0 (два терма, три добавленных) |
systemfw_concurrent 8.2.0 против 9.0.0 (мажор) | 0: ConcurrentMap, Signal, Threads — хеш в хеш |
Причина названа самим Unison и она нарочная. Заголовок их же теста unique-type-churn.md: «unique types no longer always get a fresh GUID: they share GUIDs with already-saved unique types of the same name». Пол Кьюзано формулирует правило (unison#2196): «if the type has the same structure and the same name, it gets the same guid and hash».
Иначе говоря, чисто структурная адресация типов оказалась негодной, туда подмешали GUID — а потом обнаружили, что случайный GUID тоже негоден, и привязали его к имени. Круг замкнулся: идентичность типа в языке, построенном на содержимом, определяется именем и структурой. Это стоит запомнить прежде, чем повторять их схему у себя (what-goes-into-the-hash говорит о том же с другой стороны).
Второй демпфер: базовые типы base — structural (Optional, Either, кортежи), а structural хешируется чисто по форме и потому одинаков во всех версиях всех библиотек.
Честный итог по ромбу, в три строки.
- Конфликт имён Unison действительно убирает — обещание «dependency conflicts … are just not a thing» верно ровно про имена.
- Конфликт типов остаётся, и признан вскользь, в скобке той же страницы: «you can … even write ordinary functions to convert between one and the other» (The Big Idea). Переходник нужен потому, что типы не сходятся.
- Но случается это редко, потому что бамп версии сам по себе тип не форкает. Ломается только реально изменённый тип и всё, что от него зависит.
И три вещи, которые обещание не покрывает вовсе.
upgradeчинит только ваш код. Дословно из PR 4386: перепарсить и перепроверить «all of their dependents … outsidelib». Если библиотека A внутри себя держитlib.a.lib.c_v1, вашupgradeдоc_v2на неё не влияет никак — надо форкать A. Механизм при этом не миграция типов, а перепривязка имён через текст: код рендерится в исходник и парсится заново в окружении с новыми именами.- Сборщика мусора нет. Официальный FAQ: «The codebase stores its complete history … In the future, we may introduce a "prune" operation». Команд
gc,prune,compact,vacuumвInputPatterns.hsне существует. Единственное средство — сплющивание истории при релизе. Отчёт пользователя: «my sqlite file is currently just above 500MB» (unison#5544, открыт с января 2025) — там же беда, что каждая миграция схемы оставляет полную копию файла рядом и не убирает её. - Разрешения совместимости у Unison Share нет. Хранилище нормализовано по хешу содержимого (Postgres, таблица
bytesсcontent_hash), а сверху — обычный SemVer отдельными колонкамиmajor_version/minor_version/patch_version(share-api), тип в компиляторе жёсткий:data Semver = Semver !Int !Int !Int. Но диапазонов версий, решателя ограничений и замка нет: версия только упорядочивает, она не контракт. Живая проверка:latestReleaseу@unison/base—7.19.2, то есть шесть ломающих мажоров у стандартной библиотеки языка, который «избавился от версий».
Чем подтверждено. Разбор внешних источников 2026-08-18: сравнение хешей типов через публичный API api.unison-lang.org по двум парам версий; цитаты и ссылки приведены построчно выше. Замер на этой машине (ucm release/1.3.0, 2026-08-15) не отменяется — он про другой случай.
Чем ограничено.
- Сравнение хешей — по двум парам библиотек, не по выборке. «Ни один тип за семь версий» — это про
@unison/base7.12.0→7.19.2 иsystemfw_concurrent8.2.0→9.0.0, а не про экосистему. - Прямого признания командой проблемы идентичности типов найти не удалось ни на сайте, ни в блоге, ни в задачах. Есть скобка в The Big Idea, сторонняя формулировка и симптом в дикой природе: unison#5021 — «I've run into it 3 times today with
Http.handlercoming transitively through several libs». - Объём трафика при
lib.installне измерен: sync-эндпоинты требуют токен. Полнота выкачки установлена по устройству, а не по байтам, — см. unison-pull-brings-all-the-code-not-references. - Для flang эта поправка ничего не меняет по существу. У нас две версии не уживаются при любой адресации (names-not-hashes), так что «ромб ломается редко» — довод в пользу Unison, а не в пользу его копирования.
Связано: unison-measured, unison-pull-brings-all-the-code-not-references, what-goes-into-the-hash, names-not-hashes, other-peoples-packages-must-be-stored