昨天我了解了一个新的 Haskell 工具 Stack。乍一看,它的功能和 Cabal 差不多。那么,他们之间有什么区别呢?斯塔克是卡巴尔的替代品吗?在哪些情况下我应该使用 Stack 而不是 Cabal?Stack 能做什么 Cabal 做不到的?
从我能从 FAQ 中收集到的信息来看,Stack 似乎使用 Cabal 库,而不是 cabal.exe二进制文件(更准确地说是 Cabal-install)。看起来这个项目的目标是自动沙箱和避免依赖地狱。
cabal.exe
换句话说,它使用相同的 Cabal 包结构,它只是为管理这些东西提供了不同的前端。(我想!)
斯塔克是卡巴尔的替代品吗?
是也不是。
在什么情况下我应该使用 Stack 而不是 Cabal? Stack 能做什么 Cabal 不能做的事情?
Stack 通过 违约使用策划好的堆栈包。因此,我们知道任何依赖关系都是一起构建的,这样可以避免版本冲突问题(在 Haskell 经验中这种问题很常见,过去被称为“ Cabal hell”)。最近版本的秘社也有防止冲突的措施。尽管如此,使用 Stack 设置一个可重复的构建配置,使您确切地知道将从存储库中提取什么内容更加简单。请注意,还有使用非堆栈包的规定,所以即使堆栈快照中没有包,您也可以使用它。
就个人而言,我喜欢 Stack,并推荐每个哈斯克尔开发人员使用它。他们的发展是 很快。它有一个 很多更好的用户体验。有些事情 Stack 做到了 Cabal 还没有提供:
stack build --fast --file-watch
--pedantic
有一篇很好的博客文章解释了这种差异: 为什么 Stack 不是 Cabal?虽然 Cabal 在那篇文章之后的几年里已经发展到克服了那里讨论的一些问题,但是关于 Stack 背后的设计目标和哲学的讨论仍然是相关的。
在接下来的内容中,我将把这两个工具作为 阴谋安装和 栈进行比较。特别是,我将使用 阴谋安装来避免与 秘社库混淆,秘社库是两种工具都使用的公共基础设施。
广义地说,我们可以说 阴谋安装和 栈是 秘社的前锋。这两个工具都可以构建 Haskell 项目,它们的依赖集可能在单个系统的范围内相互冲突。它们之间的关键区别在于如何实现这一目标:
默认情况下,当被要求构建项目时,阴谋安装将查看其 .cabal文件中指定的依赖项,并使用依赖求解器计算出一组满足它的包和包版本。这个集合是从 黑客入侵作为一个整体——过去和现在的所有包和所有版本中抽取的。一旦找到可行的构建计划,将在 ~/.cabal中的某个数据库中安装所选择的依赖项版本并建立索引。依赖项之间的版本冲突可以通过根据版本(以及其他相关配置选项)对安装的包进行索引来避免,这样不同的项目就可以检索它们需要的依赖项版本,而不会彼此干扰。这种安排就是 阴谋安装文档中 “ Nix 风格的本地建筑”的含义。
.cabal
~/.cabal
当被要求建立一个项目时,栈将会考虑到 stack.yaml的 resolver领域,而不是去 Hackage。在默认工作流中,该字段指定 堆叠 快照,它是具有已知相互兼容的 修好了版本的 Hackage 包的子集。然后,栈将尝试仅使用快照提供的内容来满足 .cabal文件(或者可能是 stack.yaml0——不同格式,同一角色)中指定的依赖项。从每个快照安装的包都在单独的数据库中注册,这些数据库不会相互干扰。
stack.yaml
resolver
我们可以说,在指定构建配置时,栈方法用一些设置灵活性换取了一些直观性。特别是,如果您知道您的项目使用了,比如说,LTS 15.3快照,您可以访问 它的堆栈页面,一眼就可以知道从 Stackage 获取的任何依赖关系 栈的版本。也就是说,这两种工具都提供了超越基本工作流的特性,因此,总的来说,它们都可以完成对方所做的一切(尽管可能是以一种不那么方便的方式)。例如,使用 阴谋安装可以实现 冻结已知的良好构建配置的精确版本和 用旧的 Hackage 状态解决依赖关系,使用 栈实现 需要非堆栈依赖项或覆盖快照包版本也是可能的。
最后,阴谋安装和 栈之间的另一个不同之处是,栈旨在提供一个完整的构建环境,具有诸如 自动 GHC 安装管理和 码头整合这样的特性。相比之下,阴谋安装意味着与生态系统的其他部分是正交的,所以它不会尝试提供这种特性(特别是,GHC 版本必须单独安装和管理,例如通过 打孔器)。