在发布Haskell库时,如何确定合理的包依赖性边界?

时间:2017-12-13 14:14:52

标签: haskell dependency-management hackage

在Hackage上发布库时,如何确定依赖项的合理边界?

这是一个非常简短的问题 - 不确定我能提供哪些其他信息。

根据是否使用了堆叠或阴谋,了解这是否有所不同也是有帮助的。

基本上我的问题涉及当前设置为:

的cabal约束
library
  hs-source-dirs: src
  default-language: Haskell2010
  exposed-modules: Data.ByteUnits
  build-depends:       base >=4.9 && <4.10
                       , safe == 0.3.15

我不认为==是个好主意。

4 个答案:

答案 0 :(得分:10)

这是一个棘手的问题,因为社区中有关于最佳实践的不同意见,并且在计算边界的容易性和提供与可能的依赖版本的最大兼容性之间存在权衡。在我看来,基本上你可以采取三种方法:

  1. 查看您当前使用的依赖项版本,例如safe-0.3.15。假设软件包遵循PVP并且不会在版本0.4之前发布重大更改,并添加:safe >= 0.3.15 && < 0.4
  2. 以上情况很好,但限制了很多潜在有效的构建计划。您可以花时间测试其他版本的依赖项。例如,如果你测试大约0.2.12和0.4.3并且它们似乎都有效,你可能想要扩展到safe >= 0.2.12 && < 0.5
    • 注意:突然出现的一个常见错误是,在您的软件包的未来版本中,您忘记检查与旧版本的兼容性,结果发现您正在使用引入的新功能说安全-0.4.1,使旧界限无效。遗憾的是,自动化工具的检查方式并不多。
  3. 忘记所有内容:根本没有版本限制,并让包的使用者负责确保构建计划中的兼容性。这有一个缺点,即可以创建无效的构建计划,但是你的界限不会消除潜在好的构建计划的好处。 (这基本上是假阳性与假阴性权衡。)
  4. Stackage project运行夜间版本,通常可以让您知道新版本的依赖项何时破坏了软件包,并通过提供已知可用的预构建快照使用户更容易使用软件包。这特别有助于案例(3),以及(2)中松散下限的一点点。

    您可能还想考虑使用Travis配置对旧Stackage快照进行测试,例如: https://github.com/commercialhaskell/stack/blob/master/doc/travis-complex.yml

答案 1 :(得分:6)

我假设您已了解Haskell Package Versioning Policy (PVP)。这提供了一些指导,隐含在它分配给版本的前三个组件的意义上(&#34; A.B.C&#34;)以及一些关于Cabal版本范围的明确建议。

粗略地说,未来的版本具有相同的&#34; A.B&#34;不会引入任何重大更改(包括引入可能会改变其他代码行为的孤立实例),但可能添加了新的绑定,类型等。前提是您只使用了合格的导入或显式导入列表:

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}} s(非破坏性的变化),但可以想象会打破未来的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

(在很多情况下,这些更广泛的依赖关系是由同一个作者编写的包,他显然可以确保他没有破坏自己的包,所以可能会更加松懈。)

答案 2 :(得分:2)

边界的目的是确保您使用的依赖项版本具有您需要的功能。最新版本X引入了所有这些功能,因此您需要至少X的下限。 可能从以后的版本Y 删除了 ,在这种情况下,您需要指定一个较小的上限比Y

build-depends:    foo >= X && < Y

理想情况下,您需要的功能永远不会被删除,在这种情况下,您可以删除上限。这意味着仅当您知道您的功能从更高版本中消失时才需要上限。否则,假设foo >= X足够,直到您有相反的证据。

foo == X应该很少使用;它基本上是foo >= X && <= X的缩写,并声明您在版本X中使用的功能;它不是在早期版本中,而是在更高版本中删除。如果您发现自己处于这种情况,那么尝试重写代码以便不再依赖该功能可能会更好,这样您就可以返回使用foo >= Z(通过放宽对{{1}的要求确切地说,您可以使用X的更早版本Z < X来实现。

答案 3 :(得分:2)

“万无一失”的答案是:准确地说明您确定会成功运行的那些版本!如果您只使用safe-0.3.15编译项目,那么从技术上讲,您不知道它是否也可以与safe-0.3.15一起使用,因此cabal提供的约束是对。如果您想要与其他版本兼容,请先连续向后试用测试。通过完全禁用.cabal文件中的约束然后执行

,可以最简单地完成此操作
$ cabal configure --constraint='safe==XYZ' && cabal test

对于每个版本XYZ = 0.3.14等..

实际上,这有点像偏执狂。特别是,对于跟随Package Versioning Policy的程序包来说,它是一个很好的礼仪,它要求新的次要版本永远不会破坏任何构建。也就是说,如果0.3.15有效,那么0.3.16等等也无论如何都会起作用。因此,如果您仅检查0.3.15,则保守约束实际上是safe >=0.3.15 && <0.4。也许safe >=0.3 && <0.4也是安全的。 PVP还要求您不要使用比您确认工作更宽松的主要版本边界,即它要求<0.4约束。

通常,这仍然是不必要的严格。这取决于你使用某种包装的紧密程度。特别是,有时您需要明确依赖于包,只是为了更重要的依赖项所使用的类型的一些额外配置函数。在这种情况下,我倾向于不给予辅助包装任何界限。举一个极端的例子,如果我依赖于diagrams-lib,则没有充分的理由给diagrams-core任何界限,因为这无论如何都与diagrams-lib耦合。

我也常常为containers等非常稳定和标准的套餐而烦恼。例外情况当然是base

您是否 选择safe包作为示例?