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、ghc-libs 或 haskell-* 软件包时失效,因为使用 GHC 编译的库不提供稳定的 ABI。当运行此类损坏的二进制文件时,您将看到常见的消息
error while loading shared libraries: libHS...so: cannot open shared object file: No such file or directory
要解决此问题,只需重新构建并重新安装损坏的工具,以便将其重新链接到较新的库。
另一方面,静态链接通常更易于维护,不会强制您在每次更新其依赖项后都从源代码重新构建所有工具,并且目前 GHC 对静态链接的支持更好。由于这些原因,静态链接是本地开发的首选和推荐选项。否则,如果您想要动态链接,则必须为此配置 GHC、Cabal 和 Stack 才能成功链接,因为默认设置为使用静态链接。
- 要使用 pacman 从 Arch 官方软件仓库 安装 Haskell,请继续阅读#原生安装。请注意,这可能会在您的系统中安装数百个
haskell-*
软件包。 - 否则,请跳至 #其他安装方式 部分。
如果您希望将 ghc(来自官方软件仓库)与静态链接一起使用,这是可能的,但设置起来更复杂,请参阅 #静态链接。
原生安装
要使用 Haskell,安装 以下软件包
- ghc — Haskell 编译器。有几种 实现 可用,但最常用的(现在 事实上 是参考)是 GHC (Glasgow Haskell Compiler)。
- 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 兼容的解析器。否则,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 library that works with Cabal file format;
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
命令相同的解析器。
移除软件包
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 或 Haskell 扩展 为 Visual Studio Code 安装 haskell-language-server。
当您打开项目时,haskell-language-server 将尝试自动确定构建配置。如果自动检测失败,您可能需要使用项目根目录中的 hie.yaml
文件手动配置它。
ghcid
ghcid 是一个基于 GHCi 的 Haskell 开发工具,它提供了一种简单而可靠的方法来显示每次源代码更改时的编译器错误和警告。它可以通过 ghcidAUR 软件包安装。
hoogle
hoogle 允许您通过函数名称或近似类型签名搜索 Haskell 库。它可以通过 hoogle 软件包安装。
hoogle 的在线版本可在 https://hoogle.haskell.org 上找到。
代码检查器 (Linters)
hlint
hlint 建议 Haskell 代码的可能改进,例如使用替代函数、简化代码和发现冗余。它可以通过 hlint 软件包获得。
stan
stan 是一个 Haskell 静态分析器,是对 hlint 的补充。截至 2021 年 6 月,它仍处于 beta 阶段。
weeder
weeder 是一个用于执行全程序死代码分析的应用程序。
代码格式化工具 (Formatters)
- 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
可以通过 haskell-vim 插件获得 Vim 的基本语法高亮和缩进。为了获得更好的类似 IDE 的体验,请将 haskell-language-server 与 LSP 客户端 插件之一(例如 coc.nvim、ALE、LanguageClient-neovim)一起使用。
Emacs
官方 haskell-mode 提供了 Emacs 对 Haskell 的基本支持。为了获得更高级的功能,还可以将 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 文档。
与原生安装一起使用
如果您决定使用 GHCup 和 Cabal 以及原生安装,您需要指定 Cabal 使用哪个 GHC,方法是在 $HOME/.config/cabal/config
中指定要使用的 GHC 版本的路径,搜索 with-compiler:
行并取消注释。
with-compiler: path_of_your_ghc
请记住,如果您使用 GHCup,您的 GHC 路径将位于 $HOME/.ghcup/bin
下。此外,GHCup 一旦您设置了您希望使用的 GHC 版本,就会将该版本链接到 $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 依赖项。