openlang

Новая перегрузка label(Integer) ломает label(null) при пересборке

На бумаге изменение мелкое. Сейчас в библиотеке есть label(String value), а следующий релиз оставляет его и добавляет label(Integer value).

Уже скомпилированным вызовам ничего не грозит. В их class-файлах записана сигнатура String, её они и продолжают вызывать. С пересборкой всё иначе. Нетронутый label(null) против новой пары перестаёт компилироваться, потому что null подходит обеим перегрузкам и ни одна не специфичнее другой. И для этого исходному методу вовсе не нужно было меняться.

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

Что должна говорить инструкция по миграции, и в какой момент новое имя метода просто бережнее к пользователям?

Разговор продолжается 4

Следуя доводам
  1. Ссылка на ответ

    Новое имя. labelNumber(Integer value), и всё. Если заставить каждый существующий вызов приводить свои null, расходы расползутся по коду, который к этому изменению отношения не имеет. Да и патч в библиотеке выйдет меньше. Общее имя должно давать что-то конкретное, кроме аккуратного списка методов, а этого я пока не вижу.

  2. Ссылка на ответ

    Два артефакта, и держать их порознь. Клиентский бинарник, собранный против старого объявления. Его исходник как фикстура для пересборки. Они отвечают на разные вопросы.

    Бинарник показывает, какую перегрузку называет старое место вызова, и больше ничего. Если менялось тело метода, его проверяют отдельно. Для этого релиза доказательства такие. Запуск старого бинарника и пересборка исходника с label(null). Ни один результат не заменяет другой.

  3. Ссылка на ответ

    Да, и есть бонус для того, кто будет править этот вызов следующим. По labelNumber(null) сразу видно, какая операция имелась в виду. Приведение типа в label(null) тоже сохраняет прежний выбор, но позднему читателю оно кажется мусором. Кто-нибудь при уборке удалит его как лишнее, и сборка снова сломается. Если победит вариант с приведением, в заметке о миграции нужна строчка о том, зачем оно там.

  4. Ссылка на ответ

    Отдельное имя здесь разумно по умолчанию. Но инструкция по миграции всё равно должна различать двух вызывающих, которым нужно разное. Первому нужно прежнее строковое поведение, и он может написать label((String) null). Второму нужна новая числовая операция, и ему следует вызывать числовой метод. Обе правки снимают неоднозначность. Это не одна и та же правка.

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

Другие стороны того же вопроса

  1. implicit против given в наполовину переведённом файле Scala 3

    Честно говоря, оставить implicit в Scala 3 было правильно, и меня это всё равно раздражает каждую неделю. Правильно, потому ч…

    Ответы 2scala