Cabal和Stack有什么区别?

164

昨天我了解了一种名为Stack的新Haskell工具。乍一看,它似乎与Cabal的功能类似。那么,它们之间有什么区别呢?Stack是否可以替代Cabal?在哪些情况下应该使用Stack而不是Cabal?Stack能做到Cabal做不到的事情有哪些?


1
公告:首个公共测试版 Stack 正式发布Stack 是一种新的构建工具,它在尽可能多地使用 Stackage 的基础上,取代了 cabal-install。虽然有可能在某些时候会将其重新整合到 cabal-install 中,但目前还不确定这是否是一个好事,因为这可能会分裂社区。 - Random Dev
据我所知,堆栈对于快速处理现有项目非常方便。如果您从头开始启动项目,您肯定需要使用cabal。 - mb14
@mb14 这并不是事实。你可以使用 stack 从零开始启动项目。事实上,stack 模板提供了一种简单的方法来做到这一点。 - Sibi
请查看这篇简短的文章,以获取良好的概述:https://www.scs.stanford.edu/16wi-cs240h/labs/stack.html - michid
3个回答

116
堆栈(Stack)是对Cabal的替代品吗?
是与否。
在哪些情况下应该使用Stack而不是Cabal?Stack可以做到Cabal不能做到的是什么?
Stack默认使用经过筛选的stackage软件包。因此,任何依赖项都已知能够一起构建,避免了版本冲突问题(在Haskell开发中,这种常见问题以前被称为“cabal地狱”)。最近的Cabal版本也采取了措施来预防冲突。但是,使用Stack更容易设置可复现的构建配置,您可以确切地知道将从存储库中获取哪些内容。请注意,Stack还支持使用非stackage软件包,因此即使某个软件包不存在于stackage快照中,您也可以正常运行。
个人而言,我喜欢Stack,并建议每个Haskell开发者使用它。他们的开发速度很快。它具有更好的用户体验。而且Stack可以提供一些Cabal尚未提供的功能。
  • Stack甚至为您下载GHC,并将其保存在一个隔离的位置。
  • Docker支持(这对于部署Haskell应用程序非常方便)
  • 可复现的Haskell脚本:您可以确定软件包的版本,并确保它始终能够正常执行。(Cabal也有一个脚本功能,但要完全确保可复现性并不那么直接。)
  • 能够执行stack build --fast --file-watch命令。如果您更改了本地文件,它会自动重新构建。与--pedantic选项一起使用,对我来说是一个决定性因素。
  • Stack支持使用模板创建项目。它还支持您自己的自定义模板。
  • Stack内置了hpack支持。它提供了一种使用yaml文件编写cabal文件的替代方法,这在行业中更广泛使用。
  • 在使用Stack时,Intero体验流畅when working with Stack
有一篇很好的博客文章解释了它们之间的区别:为什么不用Cabal而用Stack? 虽然自那篇文章以来,Cabal在这些问题上已经发展并克服了一些,但是关于Stack背后的设计目标和理念的讨论仍然具有相关性。

自从 Cabal 3 发布以来有没有任何更改? - William Rusnack
1
@WilliamRusnack 是的,有的。主要是默认的Cabal工作流现在包括依赖冲突避免,尽管它使用的策略与Stack明显不同。(@Sibi: 我已经更新了你的答案,以更好地反映当前的情况。) - duplode

58
以下将分别提到两个被比较的工具,它们是cabal-installstack。特别地,我会使用cabal-install以避免与Cabal库混淆,因为这是两个工具共同使用的基础设施。
总体上来说,我们可以说cabal-installstack都是Cabal的前端。这两个工具都使得在单个系统的限制范围内构建Haskell项目成为可能,而这些项目的依赖集可能会相互冲突。它们之间的关键区别在于它们如何实现这个目标:
默认情况下,当要求构建一个项目时,cabal-install 会查看其 .cabal 文件中指定的依赖关系,并使用依赖解析器来确定一组满足它的软件包和软件包版本。这个集合来自于 Hackage 的全部内容——所有的软件包和所有版本,过去和现在。一旦找到了可行的构建计划,所选的依赖项版本将被安装并索引到 ~/.cabal 中的某个数据库中。通过将已安装的软件包根据其版本(以及其他相关配置选项)进行索引,避免了依赖项之间的版本冲突,因此不同的项目可以检索到它们需要的依赖项版本而不会互相干扰。这种安排是 cabal-install 文档中所说的 "Nix-style local builds"
当要求构建一个项目时,stack 会查看 stack.yamlresolver 字段,而不是去 Hackage。在默认工作流程中,该字段指定了一个 Stackage 快照,它是 Hackage 软件包的子集,具有已知相互兼容的固定版本。然后,stack 将尝试使用仅由快照提供的内容来满足 .cabal 文件(或可能是 project.yaml 文件——不同的格式,相同的作用)中指定的依赖项。从每个快照安装的软件包都在单独的数据库中注册,彼此之间不会干扰。
我们可以说,stack方法在指定构建配置方面的直观性换来了一些设置灵活性。特别是,如果您知道您的项目使用LTS 15.3快照,您可以转到其Stackage页面,并且一眼就知道stack可能从Stackage中提取的任何依赖项版本。尽管如此,两个工具都提供超越基本工作流程的功能,因此总体而言,每个工具都可以执行另一个工具所能执行的所有操作(尽管可能不太方便)。例如,使用cabal-install有办法冻结已知良好构建配置的确切版本解决旧版Hackage的依赖关系,而使用stack则可以要求非Stackage依赖项或覆盖快照包版本
最后,cabal-installstack之间的另一个区别足以值得在此概述中提及,即stack旨在提供完整的构建环境,具有诸如自动GHC安装管理Docker集成等功能。相比之下,cabal-install旨在与生态系统的其他部分正交,并因此不尝试提供这种功能(特别是,例如通过ghcup工具单独安装和管理GHC版本)。

13

根据常见问题解答 (FAQ) 的信息,我了解到 Stack 使用 Cabal 库,但不使用 cabal-install 二进制文件(更准确地说是 cabal-install)。该项目的目标似乎是自动沙盒化和避免依赖问题。

换句话说,它使用相同的 Cabal 包结构,只是提供了一个不同的前端来管理这些内容。(我想是这样!)


除了 cabal,他们的源代码似乎还在使用 docker。虽然我不知道他们是否已经使用了它。 - Sibi
@Sibi 是的,看起来你可以选择性地将东西放入Docker,并且它应该能够正常工作。我还没有深入研究过... - MathematicalOrchid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接