Haskell 标准:无法加载模块,隐藏包

Haskell Criterion: Could not load module, hidden package

当我完全按照 this Criterion tutorial 所说的开始操作时,出现错误。我究竟做错了什么?是教程错了吗?如果是这样,有什么地方可以让我学习正确使用 Criterion 的方法吗?

具体来说,正如教程所说,我 运行 在命令行中输入以下内容:

cabal update
cabal install -j --disable-tests criterion

这个运行没有错误。然后我把教程里的例子程序一模一样复制过来:

import Criterion.Main

-- The function we're benchmarking.
fib m | m < 0     = error "negative!"
      | otherwise = go m
  where
    go 0 = 0
    go 1 = 1
    go n = go (n-1) + go (n-2)

-- Our benchmark harness.
main = defaultMain [
  bgroup "fib" [ bench "1"  $ whnf fib 1
               , bench "5"  $ whnf fib 5
               , bench "9"  $ whnf fib 9
               , bench "11" $ whnf fib 11
               ]
  ]

我将其放入一个名为 benchTest.hs 的文件中,然后我使用命令行完全按照教程中的说明编译程序,但使用 benchTest 代替了他们所谓的 Fibber它。具体来说,我在命令行中运行如下:

ghc -O --make benchTest

这导致了这个错误:

benchTest.hs:1:1: error:
    Could not load module `Criterion.Main'
    It is a member of the hidden package `criterion-1.5.13.0'.
    You can run `:set -package criterion' to expose it.
    (Note: this unloads all the modules in the current scope.)
    It is a member of the hidden package `criterion-1.5.13.0'.
    You can run `:set -package criterion' to expose it.
    (Note: this unloads all the modules in the current scope.)
    Use -v (or `:set -v` in ghci) to see a list of the files searched for.
  |
1 | import Criterion.Main
  | ^^^^^^^^^^^^^^^^^^^^^

阴谋集团进化简史

纵观其历史,Cabal 经历了一次巨大的变革 有效,通常表示为 v1- 命令与 v2- 命令;例如 从 Cabal 2 开始,你可以说 cabal v1-installcabal v2-install。发生什么了 当你说 cabal install 取决于 Cabal 版本时:Cabal 2 将使用 默认情况下 v1-install 而 Cabal 3 将使用 v2-install。的变化 默认值反映了首选的操作模式。如此一来,v1 就变成了 基本上没有维护。我不希望它很快被删除,因为有一个 一群顽固的旧方法支持者。但我个人认为,首先, 新方法(有趣的是你可以使用 cabal new-install 作为同义词)在技术上是 更好,其次,新手应该使用它,因为它更好 记录在案,您可以获得更多帮助(在许多情况下, 由于 above-mentioned 的优势,更容易提供帮助)

为什么 v1 被包含在 v2 中(简而言之)

运行 在 v1 中遇到的主要问题是不兼容的依赖项 几个项目。想象一下,您从事的项目 A 依赖于包 X 版本 42,同时您从项目 B 开始 也取决于 X 但版本 43。你猜怎么着:你不能 v1-build 同一台机器上的两个项目,而不会清除 cabal 之间的缓存。 这就是它在黑暗时代(从 2000 年代中期到每年 2010 年代)。

在那之后,cabal 沙箱 到来了。他们允许你建立我们想象中的 项目 AB 不那么麻烦,但界面不是很好,而且更多 重要的是,每个沙盒都是独立的,因此包含了很大一部分 重复的二进制文件;例如AB 也可能依赖于相同的 Y 版本 13,所以理论上不需要构建和存储 Y 两次, 但这正是 cabal 沙箱会做的。

Cabal v2 于 2010 年代后期到来并带来了: 通过名为 environment files 和共享的(也是最近的)GHC 功能进行项目 通过 Cabal store 构建工件(这样你就不会存储同一事物的许多副本)。

环境和v2-install

您可以为 每个项目(AB 等)通过

cabal v2-install --lib X-42 --package-env=.

在各自项目的目录中。关于语法的几个注释:

  1. v2- 在 Cabal 3 中可以省略,因为它是默认值;

  2. 只要install正确,标志的顺序并不重要 在 cabal 之后;

  3. --lib 很重要,因为默认情况下你只会得到可执行文件(那是 criterion 会发生什么:该包包含一个可执行文件);

  4. --package-env=.表示:在当前创建GHC环境文件 目录(因此 .)。如果命令成功,您会注意到一个新的 (Linux 上的“隐藏”)文件在当前目录中,名称类似于 .ghc.environment.x86_64-linux-9.0.2。这是告诉所有的文件 随后调用此目录中的 GHC 以在其中搜索 Cabal 编译的库 并存储在... Cabal store(在 Linux 上是 ~/.cabal/store 目录 默认)。原则上,您可以使用 . 以外的环境值, 如果该值不对应于路径,它将是一个命名的 环境。 More details in Cabal reference manual… 在实践中,我发现 99.99% 的案例由 --package-env=..

    完美服务
  5. X-42表示版本42的包X应该添加到新的 创造的环境。你可以省略版本(你会得到“some compatible version"), 你可以列出多个包。

如果没有指定环境,cabal v2-install --lib 是什么意思

表示default环境。有一个名为 default。它与 v1 有同样的问题(见上文)。所以,在 练习它可以工作,但它会非常脆弱,特别是如果你进入 上面描述的“project A and project B”情况。哪怕你只 现在处理一个项目,我建议使用 --package-env 因为它是未来的 证明。

为什么初始错误

如您所说,您使用的是 Cabal 2,因此 v1-install 最初看到 可怕的“隐藏包”错误——这是什么原因?老实说,我 不知道。而且我怀疑如果不回滚到那个旧版本就很容易计算出来 阴谋集团版本和更多实验。正如我上面所说, v1 并没有真正维护 不再,即使它看起来像 Cabal 中的一个错误(这完全有可能 特别是在 Cabal 2 系列的早期版本中),没有人可能会 打扰一下。

老教程不行了是不是很难过

是的。不幸的是,软件技术必须发展才能使世界成为 更好的地方live(再看上面v2的原因)。有时候这个 开发必须打破向后兼容性。理想情况下,我们会去更新所有 反映变化的教育材料和手册,但这很难 可能的。叹。 Haskell 的新用户必须小心谨慎并尊重创意 到 v1v2 的转变,并尝试尽早对 v2 有一个基本的了解 并尝试将它应用到仍然存在的优秀但古老的教程中。

环境是最好的方法吗?

v2 的一些设计者和支持者认为环境文件是 太微妙的功能。作为(“适当的”)替代方案,他们建议创建一个 full-fledged 您开始的每个项目的 cabal 包。这相当于打电话 cabal init,这将在当前创建一个 <project-name>.cabal 文件 目录,并维护 .cabal 文件,包括包列表 那里的依赖;您还将使用 cabal v2-build 构建项目(而不是直接调用 GHC)。

虽然更强大,但毫不奇怪,这个想法不会沉睡 很多人使用 Haskell 来尝试很多独立的小东西: 每次都创建一个完整的“包”感觉很蹩脚。好吧,这只是一个额外的 在实践中归档,如果你将它与 environments-based 我上面描述的方法,它还维护了一个额外的 文件,但在那种情况下你永远不需要手动编辑它(不像 .cabal 文件)。总而言之,在“尝试一件小事”的场景中,我发现 environments-based 方法对我来说效果更好。但它确实有其 与 package-based 方法相比的局限性,特别是 hard to figure out 如何获取环境中依赖项的分析版本。但那是一个 改天的故事……

您可以在 Cabal issue 中找到有关如何改进 cabal v2-install --lib 的更多讨论。

如果您想遵循官方认可的做事方式(即通过包),请花一点时间阅读 the Getting Started section of the Cabal manual — 它非常清楚,并且准确地展示了一个简单应用程序的示例对外部包的依赖。