Haskell 是一款 纯函数式 的语言,我个人也热衷于 Haskell
下面就来介绍一下如何搭建 Haskell 的环境

GHC

ghc 是 Haskell 的编译器,就像 gcc 和 C 关系一样
接下来就介绍如何在各个平台安装 ghc

Linux

以 Ubuntu(APT) 为例,需要在命令行输入以下代码:

sudo add-apt-repository -y ppa:hvr/ghc
sudo apt-get update
sudo apt-get install -y cabal-install-<version> ghc-<version>

其中,cabal 是用于管理 Haskell 依赖库的程序,而 ghc 则是 Haskell 的编译器

更详细的内容都在这里

Windows

首先从这里下载 GHC,然后安装

Stack

但安装原版 ghc 并不是在我们 haskell 之旅中的一个好的选择
比如原版 ghc 在更新时时常会出现一些问题,而且 cabal 的包管理机制也并不是很完善
这个时候,亲身体验了这些风霜的 haskeller 们站了出来,于是便有了 stack

如何安装 Stack

首先要找到 stack 的官方网站: https://haskellstack.org

Linux/MacOS 用户

只需要在控制台里输入

curl -sSL https://get.haskellstack.org/ | sh

就能够安装 stack

Windows 用户

需要在官网下载安装包(即上图的 Windows 64-bit Installer,这个过程可能会比较缓慢,可以通过虚拟专用网络加速这个过程),随后双击打开

自行选择安装路径,之后点击 Next

下面的两个选项最好勾选,可以自动添加环境变量,少去了自己配置的麻烦
再点 Install 就可以完成安装了

Stack 初始化

安装完 stack 后,打开命令行,输入以下指令

stack setup

用于初始化 stack,可能需要一点时间

为 Stack 修改源

stack 的官方源在中国大陆可能会非常慢,因此我们需要修改 stack 的源
请根据你的系统来找到相应的 stack 目录:

  • Windows: C:\sr
  • Linux/MacOS: ~/.stack

在 stack 目录下找到名为 config.yml (如果没有就自己新建一个) 的配置文件,输入以下内容:

# hackage
package-indices:
  - name: Tsinghua
    download-prefix: http://mirrors.tuna.tsinghua.edu.cn/hackage/package/
    http: http://mirrors.tuna.tsinghua.edu.cn/hackage/00-index.tar.gz

# stackage
setup-info: "http://mirrors.tuna.tsinghua.edu.cn/stackage/stack-setup.yaml"
urls:
  latest-snapshot: http://mirrors.tuna.tsinghua.edu.cn/stackage/snapshots.json
  lts-build-plans: http://mirrors.tuna.tsinghua.edu.cn/stackage/lts-haskell/
  nightly-build-plans: http://mirrors.tuna.tsinghua.edu.cn/stackage/stackage-nightly/

这里使用的是清华大学的镜像
之后,就可以愉快地初始化 stack 了

Stack 安装依赖库

stack 安装依赖库十分简单,只需要:

stack install <libaray name>

之后稍加等待,就能安装依赖库了

GHCi

ghci 是 Haskell 的交互式命令行,具有强大的功能
可以通过 stack ghci 进入 ghci
在 ghci 中输入以下内容

putStrLn "hoshino so cute!"

会输出

hoshino so cute!

当然,你可以通过 Tab 来快速补全

ghci 还提供了一些内置命令,这里列举出常用的

  • :type:t
    输出目标的类型
    例如 :t foldr
    输出 foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
  • :info:i
    输出目标的详细内容
    例如 :t foldr
    输出
      class Foldable (t :: * -> *) where
        ...
        foldr :: (a -> b -> b) -> b -> t a -> b
        ...
              -- Defined in ‘Data.Foldable’
    
  • :kind:k
    输出目标的 kind
    例如 :k Functor
    输出 Functor :: (* -> *) -> Constraint

  • :set
    设置一些内置功能
    :set +t 可以在每个值计算完后输出它的类型
    :set +s 可以在每个值计算完后输出所使用的时间和空间
  • :load:l
    载入目标 Haskell 文件
    例如: :l hello.hs
    之后就可以使用 hello.hs 中提供的函数了

  • :reload:r
    重新载入文件
    可以快速重新载入,就不需要重新 :l

  • :module:m
    导入库
    例如 :m +Data.List 可以导入 Data.List
    :m -Data.List 可以取消导入
    :m Data.List 会取消导入其他库,然后再导入 Data.List

  • :quit:q
    退出 ghci

IDE 环境

编写程序,IDE 当然是能够提高开发效率的一环,接下来就介绍几个常用 IDE 的 Haskell 支持

VSCode

VSCode 有 依赖官方开发的 haskell-ide-enginehaskell language serverdramforever 开发的 Simple GHC Integration
haskell language server 安装麻烦,而且内存占用也比较大,但功能较为强大
Simple GHC Integration 一键式插件,新手友好,功能完善

IntelliJ

IntelliJ IDEAintellij haskell
个人觉得不太好,所以不多加描述

Stack Project

可以通过 stack new <project name> 来新建一个项目
随后可以看到这样的项目结构:

.
├── app                 // 程序代码目录
│   └── Main.hs
├── ChangeLog.md
├── LICENSE
├── my-project.cabal
├── package.yaml        // 包配置文件
├── README.md
├── Setup.hs
├── src                 // 库代码目录
│   └── Lib.hs
├── stack.yaml          // stack 配置文件
└── test                // 测试代码目录
    └── Spec.hs

Lib.hs 中写入以下代码(代码来自我的博客):

module Lib (mergeSort) where

import Data.List

-- 归并排序 :: 数组 -> 排序后的数组
mergeSort :: (Eq a, Ord a) => [a] -> [a]
mergeSort (x:[]) = [x]      -- 如果是 只有一个元素的数组, 就返回这个数组
mergeSort xs = merge (mergeSort $ fst subs) (mergeSort $ snd subs)      -- 把 (被归并排序过的 前半部分的数组) 和 (被归并排序过的 后半部分的数组) 进行排序合并
  where
    mid = length xs `div` 2     -- 中心的下标
    subs = splitAt mid xs       -- 把数组分割成两个接近等份(奇数数量情况)或等份(偶数数量情况)的数组

-- 排序并合并两个数组 :: 数组 -> 另一个数组 -> 合并后的数组
merge :: (Eq a, Ord a) => [a] -> [a] -> [a]
merge [] ys = ys    -- 如果有任意一个数组为空, 则返回非空的那个数组
merge xs [] = xs
merge (x:xs) (y:ys) = if
    x < y then x : merge xs (y:ys)      -- 如果 第一个数组的首元素小于 第二个数组的首元素, 则 排序剩下的两个数组, 并将 第一个数组的首元素添加在排序后的数组之前
    else y : merge (x:xs) ys            -- 这里跟上面差不多啦

然后在 Main.hs 中写入以下代码,来调用 src/Lib.hs 中的函数:

module Main where

import Lib

main :: IO ()
main = print $ mergeSort [5, 2, 1, 4, 3]

最后,在项目根目录下
在命令行中输入以下内容:

stack build

等待之后,就成功编译这个项目了
然后通过

stack exec <project-name>-exe

来运行编译好的程序
会看到输出: [1, 2, 3, 4, 5]

如果在项目目录下运行 stack ghci
还会发现自动编译并导入了所有的文件

结束语

后续我还会带来关于 Haskell 的更多内容
如果期待的话,可以关注我