Как определить разумные границы зависимостей пакета при выпуске библиотеки Haskell?
Когда вы выпускаете библиотеку в Hackage, как я могу определить разумные границы для моих зависимостей?
Это очень короткий вопрос - не знаю, какую дополнительную информацию я могу предоставить.
Также было бы полезно узнать, обрабатывается ли это по-разному в зависимости от того, используется ли стек или кабаль.
По существу мой вопрос относится к ограничениям, которые в настоящее время установлены как:
library
hs-source-dirs: src
default-language: Haskell2010
exposed-modules: Data.ByteUnits
build-depends: base >=4.9 && <4.10
, safe == 0.3.15
Я не думаю, что ==
- хорошая идея.
Ответы
Ответ 1
Это сложный вопрос, поскольку в сообществе есть разные мнения о лучших практиках, и есть компромисс между легкостью определения границ и обеспечения максимальной совместимости с версиями зависимостей. Как я вижу, есть три подхода, которые вы можете предпринять:
- Посмотрите на версию зависимостей, которые вы используете в настоящее время, например.
safe-0.3.15
. Предположим, что пакет соответствует PVP и не будет выпускать прерывание до версии 0.4 и добавит следующее: safe >= 0.3.15 && < 0.4
- Вышеприведенное замечательно, но ограничивает множество потенциально допустимых планов сборки. Вы можете провести тестирование времени с другими версиями зависимостей. Например, если вы протестируете около 0.2.12 и 0.4.3, и оба они работают, вы можете перейти на
safe >= 0.2.12 && < 0.5
.
- ПРИМЕЧАНИЕ. Общей ошибкой, которая возникает, является то, что в будущей версии вашего пакета вы забываете проверить совместимость со старыми версиями, и, оказывается, вы используете новую функцию, представленную в скажем, safe-0.4.1, что делает старые ограничения недействительными. К сожалению, не так много средств автоматизации для проверки этого.
- Просто забудьте все это: никаких ограничений в отношении версий и не отвечайте за потребитель пакета, чтобы обеспечить совместимость в плане сборки. Это имеет недостаток, что можно создать недопустимые планы сборки, но потенциал роста, который ваши ограничения не устранит потенциально хороших. (Это в основном ложный положительный результат против ложных отрицательных компромиссов.)
Проект Stackage запускает ночные сборки, которые могут часто сообщать вам, когда ваш пакет поврежден новыми версиями зависимостей, и упростить его пользователи потребляют ваш пакет, предоставляя предварительно созданные снимки, которые, как известно, работают. Это особенно помогает в случае (3) и немного с ослабленными нижними границами в (2).
Вы также можете рассмотреть возможность использования конфигурации Travis для тестирования старых снимков Stackage, например. https://github.com/commercialhaskell/stack/blob/master/doc/travis-complex.yml
Ответ 2
Предполагаю, что вы знаете Haskell Package Versioning Policy (PVP). Это дает некоторые рекомендации, как неявно в том значении, которое он присваивает первым трем компонентам версии ( "A.B.C" ), так и некоторым явным советам по диапазонам версий Cabal.
Грубо говоря, будущие версии с одним и тем же "AB" не будут вносить никаких изменений (включая введение сиротских экземпляров, которые могли бы изменить поведение другого кода), но могли бы добавить новые привязки, типы и т.д. Если у вас есть используются только квалифицированные импортные или явные списки импорта:
import qualified Something as S
import Something (foo, bar)
вы можете смело написать зависимость формы:
something >= 1.2.0 && < 1.6
где предположение состояло бы в том, что вы протестировали 1.2.0
через 1.5.6
, скажем, и уверены, что он будет продолжать работать со всеми будущими 1.5.x
(неразрывными изменениями), но возможно перерыв в будущем 1.6
.
Если вы импортировали пакет неквалифицированный (что может быть очень хорошо, если вы повторно экспортируете большой кусок его API), вам понадобится вариант:
the-package >= 1.2.0 && < 1.5.4 -- tested up to 1.5.3 API
the-package >= 1.5.3 && < 1.5.4 -- actually, this API precisely
Существует также оговорка (см. PVP), если вы определяете экземпляр сирота.
Наконец, при импорте некоторых простых, стабильных пакетов, в которых вы импортировали только наиболее очевидные стабильные компоненты, вы, вероятно, могли бы предположить, что:
the-package >= 1.2.0 && < 2
будет довольно безопасным.
Глядя на файл Cabal для большого, сложного, хорошо написанного пакета, вы можете получить представление о том, что сделано на практике. Пакет lens
, например, широко использует зависимости формы:
array >= 0.3.0.2 && < 0.6
но имеет случайные зависимости вроде:
free >= 4 && < 6
(Во многих случаях эти более широкие зависимости находятся на пакетах, написанных тем же автором, и он, очевидно, может гарантировать, что он не сломает свои собственные пакеты, поэтому может быть немного более слабым.)
Ответ 3
Цель этих ограничений состоит в том, чтобы убедиться, что версия используемой вами зависимости имеет необходимые вам функции. Существует некоторая ранняя версия X
, которая вводит все эти функции, поэтому вам нужна нижняя граница, которая не менее X
. Возможно, требуемая функция удалена из более поздней версии Y
, и в этом случае вам нужно будет указать верхнюю границу, которая меньше Y
:
build-depends: foo >= X && < Y
В идеале, функция, которую вам никогда не нужно удалять, в этом случае вы можете сбросить верхнюю границу. Это означает, что верхняя граница нужна только тогда, когда вы знаете, что ваша функция исчезает из более поздней версии. В противном случае предположим, что foo >= X
является достаточным, пока у вас не будет доказательств обратного.
foo == X
редко следует использовать; он в основном не подходит для foo >= X && <= X
и указывает, что вы используете функцию, которая находится только в версии X; это было не в более ранних версиях, и оно было удалено в более поздней версии. Если вы окажетесь в такой ситуации, скорее всего, лучше попытаться переписать код, чтобы больше не полагаться на эту функцию, чтобы вы могли вернуться к использованию foo >= Z
(отделив требование к версии X
в точности, вы можете пройти с еще более ранней версией Z < X
foo
).
Ответ 4
Ответ на "надежный" будет: разрешить именно те версии, которые, как вы уверены, будут работать успешно! Если вы только что скомпилировали свой проект с помощью safe-0.3.15
, то, с технической точки зрения, вы не знаете, будет ли он работать с safe-0.3.15
, поэтому ограничение, предлагаемое предложениями, является правильным. Если вам нужна совместимость с другими версиями, проверьте их, последовательно отступая назад. Это можно сделать проще всего, полностью отключив ограничение в файле .cabal
, а затем сделав
$ cabal configure --constraint='safe==XYZ' && cabal test
Для каждой версии XYZ = 0.3.14
и т.д.
Практически говоря, это немного параноидальный подход. В частности, это хороший этикет для пакетов, которые следуют Политика версий пакетов, которая требует, чтобы новые младшие версии никогда не нарушали никаких сборок. I.e, если 0.3.15
работает, тогда 0.3.16
и т.д. Также должны работать. Таким образом, консервативное ограничение, если вы проверили только 0.3.15
, было бы safe >=0.3.15 && <0.4
. Вероятно, safe >=0.3 && <0.4
был бы безопасным & dagger; тоже. PVP также требует, чтобы вы не использовали более слабые ограничения главной версии, чем вы можете подтвердить, чтобы работать, т.е. Он задает ограничение <0.4
.
Часто это по-прежнему бесполезно строго. Это зависит от того, насколько сильно вы работаете с каким-то пакетом. В частности, иногда вам нужно явно зависеть от пакета только для некоторой дополнительной функции конфигурации типа, используемого более важной зависимостью. В таком случае я, как правило, не даю никаких ограничений для вспомогательного пакета. В крайнем случае, если я полагаюсь на diagrams-lib
, нет никаких оснований давать какие-либо оценки diagrams-core
, потому что это все равно связано с diagrams-lib
.
Я также обычно не беспокоюсь о границах для очень стабильных и стандартных пакетов, таких как containers
. Исключение составляет, конечно, base
.
& dagger; Вам нужно было выбрать пакет safe
в качестве примера?