16

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

这是一个非常简短的问题 - 不确定我可以提供哪些额外信息。

根据是否使用堆栈或阴谋集团来了解是否以不同方式处理这也将很有帮助。


基本上我的问题与目前设置为的阴谋集团约束有关:

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

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

4

4 回答 4

13

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

  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. 忘记这一切:根本没有版本限制,并让包的消费者负责确保构建计划中的兼容性。这样做的缺点是可能会创建无效的构建计划,但好处是您的界限不会消除潜在的良好计划。(这基本上是误报与误报的权衡。)

Stackage项目每晚运行构建,通常可以让您知道您的包何时被新版本的依赖项破坏,并通过提供已知有效的预构建快照使用户更容易使用您的包。这对案例 (3) 尤其有帮助,对于 (2) 中的宽松下限也有一点帮助。

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

于 2017-12-13T14:53:43.593 回答
6

我假设您知道Haskell 包版本控制政策 (PVP)。这提供了一些指导,既隐含在它赋予版本的前三个组件(“ABC”)的含义中,也包括一些关于 Cabal 版本范围的明确建议。

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

import qualified Something as S
import Something (foo, bar)

您可以安全地编写以下形式的依赖项:

something >= 1.2.0 && < 1.6

例如,假设您已经1.2.0通过测试1.5.6,并且您确信它将继续与所有 future 1.5.xs(非破坏性更改)一起运行,但可以想象会在 future 上中断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

(在很多情况下,这些更广泛的依赖关系都在同一个作者编写的包上,他显然可以确保不会破坏自己的包,因此可以稍微松懈一些。)

于 2017-12-13T15:01:20.210 回答
2

边界的目的是确保您使用的依赖版本具有您需要的功能。有一些最早的版本X介绍了所有这些功能,因此您需要一个至少为X. 可能从更高版本中删除了所需的功能,Y在这种情况下,您需要指定一个小于的上限Y

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

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

foo == X应该很少使用;它基本上是 的缩写foo >= X && <= X,并声明您正在使用在版本 X 中的功能;它不在早期版本中,并且在更高版本中被删除。如果您发现自己处于这种情况,最好尝试重写您的代码以不再依赖该功能,以便您可以重新使用foo >= Z(通过完全放宽对版本的要求X,您可能可以获得使用更早版本Z < Xfoo)。

于 2017-12-13T14:42:57.133 回答
2

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

$ cabal configure --constraint='safe==XYZ' && cabal test

对于每个版本XYZ = 0.3.14等。

实际上,这有点偏执。特别是,包遵循包版本控制政策是一种很好的礼仪,它要求新的次要版本不应该破坏任何构建。即,如果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包裹作为示例?

于 2017-12-13T15:05:27.913 回答