Haskell

来自 ArchWiki
(重定向自 Stack (Haskell))

来自 Wikipedia

Haskell 是一种通用的、静态类型的、纯函数式编程语言,具有类型推断和惰性求值特性。Haskell 的开发旨在适用于教学、研究和工业应用,它率先推出了许多先进的编程语言特性,例如类型类,这使得类型安全的运算符重载成为可能。Haskell 的主要实现是 Glasgow Haskell Compiler (GHC)。它以逻辑学家 Haskell Curry 的名字命名。

安装

Haskell 的安装方式有几种选择。其中一种受 Arch Linux 支持,而其他方式由 Haskell 官方支持,适用于任何 Linux 发行版。

8.0.2-1 版本起,Arch ghc 软件包和 extra 仓库中的所有 haskell-* 软件包仅提供动态链接库。

使用动态链接通常会带来更快的构建速度,更小的磁盘和 RAM 使用量(通过在多个运行中的 Haskell 程序之间共享页面),并使您免于解决跨 GHC 混合错误的问题。因此,对于管理最终用户应用程序,首选使用 pacman官方仓库。但它也有自身的缺点:由于使用 GHC 编译的库不提供稳定的 ABI所有您从源代码安装的工具都会崩溃,在每次更新 ghcghc-libshaskell-* 软件包时。当运行此类损坏的二进制文件时,您将看到如下常见消息

加载共享库时出错:libHS...so:无法打开共享对象文件:没有此类文件或目录

要解决此问题,只需重新构建并重新安装损坏的工具,以便将其重新链接到较新的库。

注意: 有关 Arch 为何将 Haskell 软件包迁移到动态链接的详细说明,请参阅此 维护者的回复

另一方面,静态链接通常更易于维护,不会强制您在每次更新依赖项后都从源代码重新构建所有工具,并且目前 GHC 对静态链接的支持更好。由于这些原因,静态链接是本地开发的首选和推荐选项。否则,如果您想要动态链接,为了成功链接,必须为此配置 GHC、Cabal 和 Stack,因为默认设置为使用静态链接。

如果您希望使用静态链接的 ghc(来自 官方仓库),这是可能的,但设置起来更复杂,请参阅 #静态链接

原生安装

要使用 Haskell,安装 以下软件包

  1. ghc — Haskell 编译器。有几种 实现 可用,但最常用的(现在实际上是参考实现)是 GHC (Glasgow Haskell Compiler)(格拉斯哥 Haskell 编译器)。
  2. cabal-installstack — 依赖 GHC 编译 Haskell 源代码的构建工具。Cabal 是经典的构建工具,专注于依赖项解析和来自 Hackage(Haskell 社区的开源软件包中央归档)的源代码包。Stack 是另一种构建工具,专注于来自 Stackage(Hackage 的稳定子集,提供已知可以良好协同工作的软件包的精选集(快照))的精选快照和源代码包。
提示: Cabal 和 Stack 都是稳定且成熟的构建工具。许多 Haskell 开发者使用 Cabal,而许多其他人则更喜欢 Stack。如果您不确定选择哪个工具,请尝试同时安装两者,看看哪个最适合您。

配置

注意: 如果您不打算从源代码编译 Haskell,而只是使用仓库中分发的 Haskell 应用程序,则可以跳过此部分。

直接调用 GHC

为了成功链接,必须将 -dynamic 标志传递给 GHC。您可以使用以下文件尝试一下

Main.hs
main = putStrLn "Hello, World"

使用以下命令编译并运行它

$ ghc -dynamic Main.hs
$ ./Main
Hello, World

配置 Cabal 以进行动态链接

首先,运行以下命令从 Hackage 下载最新的软件包列表,并创建全局配置文件 ~/.cabal/config(或 $CABAL_CONFIG 指向的文件)

$ cabal update
提示: 建议定期运行 cabal update,以将您的本地软件包和依赖项列表与 Hackage 上的最新版本同步。

要配置 Cabal 以进行动态链接,请取消注释并编辑 ~/.cabal/config 中的以下选项

~/.cabal/config
library-vanilla: False
shared: True
executable-dynamic: True
program-default-options
  ghc-options: -dynamic
  • library-vanilla: False 抑制静态库的创建(如果您的项目包含库)。
  • shared: True 启用共享库的创建(如果您的项目包含库)。
  • executable-dynamic: True 使可执行文件使用动态链接(如果您的项目包含可执行文件)。
  • ghc-options: -dynamic-dynamic 标志添加到每次 GHC 调用中(例如,如果软件包具有非平凡的 Setup.hs)。

配置 Stack 以进行动态链接

您可以使用 stack setup 命令初始化 Stack 并创建全局配置文件 ~/.stack/config.yaml。默认情况下,Stack 会在首次调用时自动下载其自身版本的 GHC 到隔离位置。要强制 Stack 改用系统 GHC 安装,请使用 --system-ghc--resolver 标志运行 stack setup

$ stack setup --system-ghc --resolver resolver

请注意,您需要指定与您的系统 GHC 兼容的 resolver。否则,Stack 会很高兴地忽略 --system-ghc 标志并下载其自身的 GHC 副本。您可以使用 ghc --version 命令确定系统 GHC 的版本

$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.10.2

然后访问 Stackage 网站,并选择与您的系统 GHC 版本匹配的合适的长期支持 (LTS) 或 nightly 快照。在命令行上将选定的快照用于 --resolver 标志,例如 --resolver lts-16.15--resolver nightly-2020-09-01

Stackage 通常滞后于新的 GHC 版本发布。可能发生 Stackage 尚未发布适用于您的系统 GHC 的快照的情况。在这种情况下,您可能需要选择适用于早期 GHC 小版本的快照,或者暂时降级您的 Haskell 安装,并等待对较新 GHC 版本的支持最终在 Stackage 上发布。

要配置 Stack 以进行动态链接,请将以下代码片段添加到 ~/.stack/config.yaml

~/.stack/config.yaml
# Stop downloading GHCs into isolated locations under ~/.stack.
install-ghc: false

# Allow Stack to pick the system GHC (false by default).
system-ghc: true

# Allow to use, say, Stackage snapshot for GHC 8.8.2 with system GHC 8.8.3.
compiler-check: newer-minor

# Add the -dynamic flag to every invocation of GHC.
ghc-options:
  "$everything": -dynamic

软件包管理

大多数 Haskell 库和可执行文件都以源代码包的形式分发,这些源代码包可从 Hackage 和 Stackage 获得。它们分别是 Cabal 和 Stack 使用的仓库。

GHC 是 Haskell 的标准编译器,它生成的机器代码可以在 Linux 上原生运行。与其他编译语言一样,许多流行的 Haskell 软件包可以从 Arch 官方仓库以预构建形式获得。

一些额外的软件包可以从 AUR 安装。这些软件包可能需要从源代码构建,构建 AUR 软件包或开发软件需要安装编译器和构建工具。不建议为此用例使用 #原生安装 方法,而是参阅 #备选安装

因此,Haskell 软件包有四个主要来源:Hackage (Cabal)、Stackage (Stack)、官方仓库AUR

下表总结了不同软件包管理方式的优点和缺点。

方法 优点 缺点
官方仓库
  • 使用 pacman 更容易进行软件包管理
  • 已经编译为动态链接
  • 并非所有软件包都可用
  • 使从源代码编译变得困难
Cabal
  • 所有软件包都可用
  • 不需要 root 权限
  • 更好地支持开发
  • 安装在主目录中
  • 难以移除特定的软件包
Stack
  • 所有软件包都可用(倾向于 Stackage)
  • 不需要 root 权限
  • 更好地支持开发
  • 安装在主目录中
  • 版本固定到快照
  • 难以移除特定的软件包
AUR
  • 额外的软件包可用
  • 存在未维护或孤立软件包的风险
  • 可能存在不兼容版本的软件包

Cabal

注意: 在 Haskell 中,术语 Cabal 可以指代
  • 描述 Haskell 软件包及其依赖项的 Cabal 文件格式;
  • 与 Cabal 文件格式一起使用的 Cabal 库;
  • cabal 命令行工具(由 cabal-installcabal-install-binAUR 软件包提供),它使用 Cabal 库来构建 Haskell 软件包。
在本文的上下文中,Cabal 通常指 cabal 命令行工具,除非另有说明。

Cabal 是 Haskell 的“原始”构建系统。您可以在 Hackage 上找到的大多数库和工具都可以通过 Cabal 安装。

安装软件包

要运行 Cabal 安装的用户范围的可执行文件,必须将 ~/.cabal/bin 添加到 $PATH 环境变量 中。

PATH="$HOME/.cabal/bin:$PATH"

运行以下命令以一步安装 Hackage 软件包及其所有依赖项

$ cabal install package
警告: 如果您使用的是来自 官方仓库cabal[broken link: package not found],截至 2024 年 12 月,Cabal 在构建 build-type: Custom 的软件包时 忽略 ~/.cabal/config 中的 ghc-options。因此,必须在命令行上指定 --ghc-options=-dynamic 标志,否则您可能会遇到构建错误。

您还可以从源代码构建和安装 Haskell 软件包。为此,请在包含源代码的目录中运行 cabal install,而无需指定任何软件包。

每个 Cabal 软件包都应根据 软件包版本策略 (PVP) 在 .cabal 文件中指定其依赖项列表及其版本约束。在软件包安装期间,Cabal 尝试找到一组满足所有约束的依赖项。此过程称为依赖项解析

Stack 的存在是有原因的;Cabal 以给初学者带来很多摩擦而闻名,尽管近年来情况有所好转。大多数情况下,依赖项解析效果良好,但有时会失败。在这种情况下,您需要找出问题的原因,并为 Cabal 提供一些关于如何解决有问题的依赖项的提示。例如,有时需要添加 --allow-newer 以允许 Cabal 忽略软件包的 PVP 规定的依赖项版本上限,从而有效地安装具有比软件包作者允许的更新依赖项的软件包。对于维护不善的软件包,情况会变得更加复杂;有关另一个示例,请参阅关于安装 Idris(另一种用 Haskell 编写的编程语言)的 此线程,其中必须同时使用 --allow-newer--constraint='haskeline < 0.8.0.0' 命令行标志才能成功编译。

移除软件包

没有简单的方法可以做到这一点。Cabal 不支持此功能,但有一些外部工具,例如 cabal-store-gc

要重新安装整个用户范围的 Haskell 软件包系统,请删除 ~/.cabal~/.ghc 并从头开始。这在 GHC 升级时通常是必要的。

为了更精确地控制,可以直接在 用户软件包数据库 上使用 ghc-pkg unregister packageghc-pkg hide package/ghc-pkg expose package — 这会使 GHC “忘记”已安装的软件包(或假装它不存在)。然而,这些操作都不会删除任何文件。

Stack

Stack 是另一个用于管理 Haskell 软件包的工具。它与 Cabal 的目标略有不同,哲学也略有不同。它在底层使用 Cabal 库并与 Hackage 集成 — 但在 Stackage 上维护自己的软件包(快照)仓库,并承诺快照是经过精选的,并且包含可以良好协同工作的软件包。

安装软件包

在其默认配置中,Stack 将编译后的可执行文件安装到 ~/.local/bin。将此目录添加到您的 shell 配置文件中的 $PATH 环境变量中,例如 bash~/.bashrczsh~/.zshrc

export PATH="$HOME/.local/bin:$PATH"

运行以下命令以下载、构建和安装 Stackage 软件包

stack install package
警告: 截至 2024 年 12 月,Stack 在构建 Setup.hs忽略 ~/.stack/config.yaml 中的 ghc-options。如果您在 Setup.hs 中遇到构建错误,例如 Could not find module ‘Prelude’ There are files missing in the ‘base-...’ package,请尝试安装 ghc-static 软件包作为解决方法。

您还可以通过从软件包目录运行以下命令,从源代码构建和安装 Haskell 软件包

stack install --resolver resolver

请注意,您应该指定与 stack setup 命令使用的 resolver 相同的 resolver。

移除软件包

Stack 不支持“卸载”操作。

如果您想重新安装整个用户范围的 Haskell 软件包系统,请删除 ~/.stack 目录并从头开始。这在 GHC 升级时通常是必要的。

开发工具

工具

haskell-language-server

haskell-language-server 是 Haskell 的 Language Server Protocol (LSP) 实现。它为任何与 LSP 集成的编辑器提供类似 IDE 的功能,例如代码完成、“转到定义”、悬停文档、代码检查、格式化或重构。

如果您正在使用来自 pacman 的动态链接 Haskell 软件包,请安装 haskell-language-server。否则,如果您更喜欢静态链接,请安装 haskell-language-server-staticAUR。此软件包包含每个受支持的 GHC 版本的静态链接二进制文件。或者,可以通过 ghcupVisual Studio CodeHaskell 扩展 安装 haskell-language-server。

当您打开项目时,haskell-language-server 将尝试自动确定构建配置。如果自动检测失败,您可能需要使用项目根目录中的 hie.yaml 文件 手动配置它

ghcid

ghcid 是一个基于 GHCi 的 Haskell 开发工具,它提供了一种简单而强大的方法,可以在每次源代码更改时显示编译器错误和警告。它可以通过 ghcidAUR 软件包安装。

hoogle

hoogle 允许您通过函数名称或近似类型签名搜索 Haskell 库。它可以通过 hoogle 软件包安装。

hoogle 的在线版本可在 https://hoogle.haskell.org 获取。

代码检查器

hlint

hlint 建议 Haskell 代码的可能改进,例如使用替代函数、简化代码和发现冗余。它可以通过 hlint 软件包获得。

stan

stan 是一个 Haskell 静态分析器,是 hlint 的补充。截至 2021 年 6 月,它仍处于 beta 阶段。

weeder

weeder 是一个用于执行全程序死代码分析的应用程序。

格式化工具

  • Floskell — hindent 的分支,专注于灵活性和可配置性。
https://github.com/ennocramer/floskell || haskell-floskell
  • Fourmolu — Ormolu 的分支,增加了配置各种格式化参数的能力。
https://github.com/parsonsmatt/fourmolu || haskell-fourmolu
  • hindent — 可扩展的 Haskell 代码美化器。
https://github.com/mihaimaruseac/hindent || hindent
  • Ormolu — Haskell 源代码的格式化工具,实现了一种“真实”的格式化风格,不允许任何配置。
https://github.com/tweag/ormolu || haskell-ormolu
  • stylish-haskell — 简单的 Haskell 代码美化器。
https://github.com/jaspervdj/stylish-haskell || stylish-haskell

编辑器

Visual Studio Code

Visual Studio Code 有一个由 haskell-language-server 驱动的 Haskell 扩展。如果您没有安装 haskell-language-server,Haskell 扩展将自动为您下载并安装静态链接的 Linux 二进制文件。

IntelliJ IDEA

IntelliJ IDEA 对 Haskell 的支持由 Haskell 插件 提供[死链 2024-12-15 ⓘ]。它适用于任何版本的 IntelliJ IDEA,包括 intellij-idea-community-edition

您需要安装 Stack 才能在 IntelliJ IDEA 中创建新项目或导入现有项目。截至 2021 年 6 月,不支持仅 Cabal 的项目。

Vim

Vim 的基本语法高亮和缩进可以通过 haskell-vim 插件获得。为了获得更好的类似 IDE 的体验,请将 haskell-language-serverLSP 客户端 插件之一(例如 coc.nvimALELanguageClient-neovim)一起使用。

Emacs

Emacs 对 Haskell 的基本支持由官方 haskell-mode 提供。为了获得更高级的功能,还可以将 lsp-haskellhaskell-language-server 一起使用。

备选安装

本节中描述的方法最适合 Haskell 开发设置。可以将它们与来自 官方仓库 的软件包一起使用,如果是这种情况,请确保您知道您正在使用的 Haskell 软件包的版本,是 pacman 安装的版本还是以下方法之一安装的版本。

ghcup

这是在任何 Linux 发行版中安装 Haskell 的推荐方法。GHCup 将 GHC、工具和库安装在您的主目录中,并允许并行拥有多个版本并相对轻松地处理它们。它的范围类似于 rustuppyenvjenv

安装 ghcup-hs-binAUR 软件包。或者,您可以按照官方 安装说明 操作,或手动下载 ghcup 二进制文件 并将其放置在 $PATH 中的某个位置。

默认情况下,ghcup 会将可执行文件安装到 ~/.ghcup/bin。您需要将此目录添加到您的 shell 配置文件中的 $PATH 环境变量中,例如 bash~/.bashrczsh~/.zshrc。如果您想运行 Cabal 安装的可执行文件,也请将 ~/.cabal/bin 添加到 $PATH

export PATH="$HOME/.cabal/bin:$HOME/.ghcup/bin:$PATH"
提示: 如果您想使用 XDG 样式目录,请定义 GHCUP_USE_XDG_DIRS 环境变量 (https://ghcup.readthedocs.io/en/latest/guide/#xdg-support)。

GHCup 提供了一个方便的 TUI,支持其大部分功能

$ ghcup tui

或者,您可以使用以下 CLI 命令

列出可用的 GHC 和 Cabal 版本

$ ghcup list

安装推荐版本的 GHC

$ ghcup install ghc

您也可以安装特定版本的 GHC,例如

$ ghcup install ghc 8.10.2

以上命令不会自动将 GHC 添加到 $PATH 中。您需要选择要默认使用的 GHC 版本

$ ghcup set ghc 8.10.2
注意: GHCup 通常与 Cabal 结合使用以管理其他 Haskell 包,您可以使用它来安装 Stack,请参阅 #Stack

安装推荐版本的 Cabal

$ ghcup install cabal

GHCup 也可以安装 haskell-language-server,请使用以下命令

$ ghcup install hls

更多信息,请参阅官方 ghcupCabal 文档。

与原生安装一起使用

如果您决定将 GHCup 和 Cabal 与原生安装一起使用,您需要在 Cabal 中指定要使用的 GHC,方法是在 $HOME/.config/cabal/config 中指定要使用的 GHC 版本的路径,搜索行 with-compiler: 并取消注释

 with-compiler: path_of_your_ghc

请记住,如果您使用 GHCup,您的 GHC 路径将位于 $HOME/.ghcup/bin 下。此外,一旦您设置了希望使用的 GHC 版本,GHCup 会将该版本链接到 $HOME/.ghcup/bin/ghc。如果您在 Cabal 配置文件中设置了该路径,您可以使用 GHCup 更改 Cabal 使用的 GHC 版本。

Stack

您可以使用 stack-staticAUR 包或使用 GHCup(请参阅 #ghcup)安装 Stack。或者,您可以按照官方 安装说明 进行操作,或者手动下载 Stack 二进制文件 并将其放置到您的 $PATH 中的某个位置。

如果您想运行 Stack 安装的可执行文件,请将 ~/.local/bin 目录添加到 $PATH 中,有关如何执行此操作的更多信息,请参阅 环境变量

Stack 将使用隔离版本的 GHC,因此不需要任何额外的配置。运行 stack setup 以自动从最新的 Stackage LTS 快照安装 GHC

$ stack setup

现在您可以使用 Stack 构建和安装静态链接的 Haskell 包,而无需任何特殊的配置或命令行标志

$ stack install package

更多信息,请参阅官方 Stack 文档

Nix

本文或章节需要扩充。

原因: 由于没有相关经验,我无法提供足够好的概述(在 Talk:Haskell 中讨论)

安装 Haskell 的完全不同的方法是 Nix 包管理器。Nix 具有更陡峭的学习曲线,但在可靠且可重现的方式管理 Haskell 和非 Haskell 包方面提供了更大的灵活性。

技巧和窍门

静态链接

警告: 不保证它能正确工作,因为存储库中的 GHC 版本非常过时。您可能会遇到一些错误,除了更新 GHC [1] 之外,目前没有其他解决方案。
注意
  • 本节介绍如何在 Arch 上构建静态链接的 Haskell 包,但仍然使用从官方存储库安装的 GHC。在继续之前,请确保删除 ~/.cabal~/.stack 目录(如果它们存在)。
  • 在本文的上下文中,静态链接并不意味着生成完全静态的 ELF 二进制文件。只有 Haskell 代码将被静态链接到单个 ELF 二进制文件中,该二进制文件可能动态链接到其他系统库,例如 glibc

要使用静态链接,至少必须通过 ghc-static 包安装静态引导库。这将允许您构建仅依赖于引导库以及任何其他未通过官方存储库中的 haskell-* 包安装的库的项目。

不幸的是,如果您的项目依赖于您已安装的任何动态链接的 haskell-* 包,Cabal 在依赖项解析期间不会考虑缺少静态库的情况。因此,它会尝试使用现有的 haskell-* 包,然后在发现缺少静态库时,出现 链接器错误 而失败

Could not find module ‘SomePackage.SomeModule’
There are files missing in the ‘somepackage-0.1.0.0’ package,
try running 'ghc-pkg check'.
Use -v (or `:set -v` in ghci) to see a list of the files searched for.

ghc-static 不同,没有可用于链接的“haskell-*-static”包。但是,还有其他方法可以解决此问题,如下面的每个部分所述。

静态全局包数据库

官方 ghc-static 包提供了一种直接的 方法,它在 /usr/lib/ghc-version/static-package.conf.d 中公开了一个替代的“静态”全局包数据库。静态数据库仅限于静态可链接的引导包,因此,如果重新配置 Cabal 以使用静态数据库而不是默认数据库,它的行为就像动态的 haskell-* 包不存在一样。

可以使用如下命令在构建时确定静态数据库的精确路径

$ ghc --print-global-package-db | sed 's/\(package\.conf\.d\)$/static-\1/'
/usr/lib/ghc-version/static-package.conf.d

以下是如何启用静态数据库以供使用

  • 当使用 Cabal 构建包时,可以传递以下标志来限制全局包的选择仅限于引导包
$ cabal configure --ghc-pkg-option="--global-package-db=$(ghc --print-global-package-db | sed 's/\(package\.conf\.d\)$/static-\1/')"
  • 当直接使用 GHC 而不是 Cabal 构建时,可以传递以下标志来覆盖全局包数据库
$ ghc -clear-package-db -package-db "$(ghc --print-global-package-db | sed 's/\(package\.conf\.d\)$/static-\1/')" -user-package-db ...

ghc-pristine

安装 ghc-pristineAUR 包,该包封装了现有的 GHC 安装,以在 /usr/share/ghc-pristine 中创建一个单独的 GHC 发行版,其包数据库仅包含引导库。这有效地创建了一个半隔离的环境,其中不包含动态链接的 haskell-* 包,但仍然使用了来自官方存储库的 GHC 编译器。然后,要使用静态链接构建软件,只需调用封装的编译器 /usr/share/ghc-pristine/bin/ghc 即可。对于 Cabal,这相当于 ~/.cabal/config 中的以下配置

~/.cabal/config
with-compiler: /usr/share/ghc-pristine/bin/ghc

您还可以通过从项目目录运行以下命令,在每个项目的基础上指定编译器的路径

$ cabal configure --with-compiler=/usr/share/ghc-pristine/bin/ghc

cabal-static

在 Arch 上重新获得静态链接的另一种方法是安装 cabal-install-binAUR 包。与官方 cabal-install 不同,这个包不会从官方存储库中拉取动态链接的 haskell-* 依赖项,并避免混合安装在同一系统上的静态和共享 Haskell 库。然后您可以像往常一样使用 Cabal,但有以下限制:您必须确保 您安装的唯一其他 Haskell 包是 ghcghc-libsghc-static(而不是 cabal-installstack 以及官方存储库中提供的任何 haskell-* 包)。

stack-static

安装 stack-staticAUR 包。与 cabal-static 方法类似,请确保您从官方存储库安装的唯一其他 Haskell 包是 ghcghc-libsghc-static。然后按照 #Configuring Stack for dynamic linking 中所述设置 Stack 以使用系统 GHC

$ stack setup --system-ghc --resolver resolver

要使这些选项永久生效,请将以下代码段粘贴到 ~/.stack/config.yaml

~/.stack/config.yaml
# Stop downloading GHCs into isolated locations under ~/.stack.
install-ghc: false

# Allow Stack to pick the system GHC (false by default).
system-ghc: true

# Allow to use, say, Stackage snapshot for GHC 8.8.2 with system GHC 8.8.3.
compiler-check: newer-minor

此配置将允许您像往常一样构建静态链接的包,但使用系统 GHC 安装而不是 Stack 提供的 GHC。

hpack-static-bin

hpack-static-binAUR 提供了一个静态链接的(意味着没有 haskell-* 依赖项)替代 haskell-hpack。它是预编译的,因此不需要 make 依赖项。

参见