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,所有您从源代码安装的工具都会崩溃,在每次更新 ghc、ghc-libs 或 haskell-* 软件包时。当运行此类损坏的二进制文件时,您将看到如下常见消息
加载共享库时出错:libHS...so:无法打开共享对象文件:没有此类文件或目录
要解决此问题,只需重新构建并重新安装损坏的工具,以便将其重新链接到较新的库。
另一方面,静态链接通常更易于维护,不会强制您在每次更新依赖项后都从源代码重新构建所有工具,并且目前 GHC 对静态链接的支持更好。由于这些原因,静态链接是本地开发的首选和推荐选项。否则,如果您想要动态链接,为了成功链接,必须为此配置 GHC、Cabal 和 Stack,因为默认设置为使用静态链接。
如果您希望使用静态链接的 ghc(来自 官方仓库),这是可能的,但设置起来更复杂,请参阅 #静态链接。
原生安装
要使用 Haskell,安装 以下软件包
- ghc — Haskell 编译器。有几种 实现 可用,但最常用的(现在实际上是参考实现)是 GHC (Glasgow Haskell Compiler)(格拉斯哥 Haskell 编译器)。
- cabal-install 或 stack — 依赖 GHC 编译 Haskell 源代码的构建工具。Cabal 是经典的构建工具,专注于依赖项解析和来自 Hackage(Haskell 社区的开源软件包中央归档)的源代码包。Stack 是另一种构建工具,专注于来自 Stackage(Hackage 的稳定子集,提供已知可以良好协同工作的软件包的精选集(快照))的精选快照和源代码包。
配置
直接调用 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。
下表总结了不同软件包管理方式的优点和缺点。
方法 | 优点 | 缺点 |
---|---|---|
官方仓库 |
|
|
Cabal |
|
|
Stack |
|
|
AUR |
|
|
Cabal
- 描述 Haskell 软件包及其依赖项的 Cabal 文件格式;
- 与 Cabal 文件格式一起使用的 Cabal 库;
cabal
命令行工具(由 cabal-install 或 cabal-install-binAUR 软件包提供),它使用 Cabal 库来构建 Haskell 软件包。
cabal
命令行工具,除非另有说明。Cabal 是 Haskell 的“原始”构建系统。您可以在 Hackage 上找到的大多数库和工具都可以通过 Cabal 安装。
安装软件包
要运行 Cabal 安装的用户范围的可执行文件,必须将 ~/.cabal/bin
添加到 $PATH
环境变量 中。
PATH="$HOME/.cabal/bin:$PATH"
运行以下命令以一步安装 Hackage 软件包及其所有依赖项
$ cabal install package
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 package
或 ghc-pkg hide package
/ghc-pkg expose package
— 这会使 GHC “忘记”已安装的软件包(或假装它不存在)。然而,这些操作都不会删除任何文件。
Stack
Stack 是另一个用于管理 Haskell 软件包的工具。它与 Cabal 的目标略有不同,哲学也略有不同。它在底层使用 Cabal 库并与 Hackage 集成 — 但在 Stackage 上维护自己的软件包(快照)仓库,并承诺快照是经过精选的,并且包含可以良好协同工作的软件包。
安装软件包
在其默认配置中,Stack 将编译后的可执行文件安装到 ~/.local/bin
。将此目录添加到您的 shell 配置文件中的 $PATH
环境变量中,例如 bash 的 ~/.bashrc
或 zsh 的 ~/.zshrc
export PATH="$HOME/.local/bin:$PATH"
运行以下命令以下载、构建和安装 Stackage 软件包
stack install package
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 版本的静态链接二进制文件。或者,可以通过 ghcup 或 Visual Studio Code 的 Haskell 扩展 安装 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 的分支,专注于灵活性和可配置性。
- Fourmolu — Ormolu 的分支,增加了配置各种格式化参数的能力。
- hindent — 可扩展的 Haskell 代码美化器。
- Ormolu — Haskell 源代码的格式化工具,实现了一种“真实”的格式化风格,不允许任何配置。
- stylish-haskell — 简单的 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-server 与 LSP 客户端 插件之一(例如 coc.nvim、ALE、LanguageClient-neovim)一起使用。
Emacs
Emacs 对 Haskell 的基本支持由官方 haskell-mode 提供。为了获得更高级的功能,还可以将 lsp-haskell 与 haskell-language-server 一起使用。
备选安装
本节中描述的方法最适合 Haskell 开发设置。可以将它们与来自 官方仓库 的软件包一起使用,如果是这种情况,请确保您知道您正在使用的 Haskell 软件包的版本,是 pacman 安装的版本还是以下方法之一安装的版本。
ghcup
这是在任何 Linux 发行版中安装 Haskell 的推荐方法。GHCup 将 GHC、工具和库安装在您的主目录中,并允许并行拥有多个版本并相对轻松地处理它们。它的范围类似于 rustup、pyenv 和 jenv。
安装 ghcup-hs-binAUR 软件包。或者,您可以按照官方 安装说明 操作,或手动下载 ghcup 二进制文件 并将其放置在 $PATH
中的某个位置。
默认情况下,ghcup 会将可执行文件安装到 ~/.ghcup/bin
。您需要将此目录添加到您的 shell 配置文件中的 $PATH
环境变量中,例如 bash 的 ~/.bashrc
或 zsh 的 ~/.zshrc
。如果您想运行 Cabal 安装的可执行文件,也请将 ~/.cabal/bin
添加到 $PATH
中
export PATH="$HOME/.cabal/bin:$HOME/.ghcup/bin:$PATH"
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
安装推荐版本的 Cabal
$ ghcup install cabal
GHCup 也可以安装 haskell-language-server,请使用以下命令
$ ghcup install hls
与原生安装一起使用
如果您决定将 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
安装 Haskell 的完全不同的方法是 Nix 包管理器。Nix 具有更陡峭的学习曲线,但在可靠且可重现的方式管理 Haskell 和非 Haskell 包方面提供了更大的灵活性。
技巧和窍门
静态链接
- 本节介绍如何在 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 包是 ghc、ghc-libs 和 ghc-static(而不是 cabal-install、stack 以及官方存储库中提供的任何 haskell-*
包)。
stack-static
安装 stack-staticAUR 包。与 cabal-static
方法类似,请确保您从官方存储库安装的唯一其他 Haskell 包是 ghc、ghc-libs 和 ghc-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 依赖项。