Real World Haskell 中文版
Real World Haskell 中文版
Real World Haskell 中文版
Warning
《Real World Haskell》中文版现在可以通过域名 cnhaskell.com 快速进行访问,请各位读者使用新
域名访问本文档,原有的域名 rwh.rtfd.org 以及 rwh.readthedocs.org 将在一段时间之后被废弃。
2015 年 8 月 30 宣
本文档是 Real World Haskell 一书的简体中文翻译版本,翻译工作正在进行中,欢迎加入:
https://github.com/huangz1990/real-world-haskell-cn
本文档使用 看云 构建
- 1 -
Real World Haskell 中文版
关于
以下人员参与了本文档的翻译工作:
huangz
Haisheng, Wu
Albert
Guang Yang
labyrlnth
Javran Cheng
bladewang
spectator
tiancaiamao
除了进行翻译之外,本文档还在原书的基础上做了以下改进:
修正原文正文和代码中的错误
更新代码以符合最新的 Hasekll 规范
在一些比较复杂的地方添加注释,帮助理解
关注项目进度 / 反馈意见或建议 / 提交你的翻译贡献,请访问 项目的 github 页面 。
本文档的部分内容参考了 AlbertLee 的译本,在此对他表示感谢。
版权
本文档和原书一样,通过 CC 协议进行署名-非商业性使用 授权。
本文档使用 看云 构建
- 2 -
Real World Haskell 中文版
第一章:入门
第一章:入门
Haskell编程环境
在本书的前面一些章节里,我们有时候会以限制性的、简单的形式来介绍一些概念。由于Haskell是一本比
较深的语言,所以一次性介绍某个主题的所有特性会令人难以接受。当基础巩固后,我们就会进行更加深
入的学习。
在Haskell语言的众多实现中,有两个被广泛应用,Hugs和GHC。其中Hugs是一个解析器,主要用于教
学。而GHC(GlasgowHaskellCompiler)更加注重实践,它编译成本地代码,支持并行执行,并带有更好
的性能分析工具和调试工具。由于这些因素,在本书中我们将采用GHC。
GHC主要有三个部分组成。
ghcghc是生成快速本底代码的优化编译器。
ghcighci是一个交互解析器和调试器。
runghc
runghc是一个以脚本形式(并不要首先编译)运行Haskell代码的程序,
Note
我们如何称呼GHC的各个组件
当我们讨论整个GHC系统时,我们称之为GHC。而如果要引用到某个特定的命令,我们会直接用其名
字标识,比如ghcghc,ghcighci,runghc
runghc。
在本书中,我们假定你在使用最新版6.8.2版本的GHC,这个版本是2007年发布的。大多数例子不要额外
的修改也能在老的版本上运行。然而,我们建议使用最新版本。如果你是Windows或者MacOSX操作系
统,你可以使用预编译的安装包快速上手。你可以从GHC下载页面 找到合适的二进制包或者安装包。
对于大多数的Linux版本,BSD提供版和其他Unix系列,你可以找到自定义的GHC二进制包。由于这些包
要基于特性的环境编译,所以安装和使用显得更加容易。你可以在GHC的二进制发布包页面 找到相关下
载。
我们在[附录A]中提供了更多详细的信息介绍如何在各个流行平台上安装GHC。
初识解释器ghci
ghcighci程序是GHC的交互式解析器。它可以让用户输入Haskell表达式并对其求值,浏览模块以及调试代码。
如果你熟悉Python或是Ruby,那么ghci一定程度上和python,irb很像,这两者分别是Python和Ruby的
交互式解析器。
本文档使用 看云 构建
- 3 -
Real World Haskell 中文版
The ghci command has a narrow focus
We typically can not copy some code out of a haskell source file and paste it into ghci. This does not
have a significant effect on debugging pieces of code, but it can initially be surprising if you are used
to , say, the interactive Python interpreter.
在类Unix系统中,我们在shell视窗下运行ghcighci。而在Windows系统下,你可以通过开始菜单找到它。比
如,如果你在WindowsXP下安装了GHC,你应该从”所有程序”,然后”GHC”下找到ghcighci。(参考附录
A章节Windows 里的截图。)
当我们运行ghcighci时,它会首先显示一个初始banner,然后就显示提示符Prelude>。下载例子展示的是
Linux环境下的6.8.3版本。
$ ghci
GHCi, version 6.8.3: http://www.haskell.org/ghc/ :? for help
Loading package base ... linking ... done.
Prelude>
提示符Prelude标识一个很常用的库Prelude已经被加载并可以使用。同样的,当加载了其他模块或是源文
件时,它们也会在出现在提示符的位子。
Tip
获取帮助信息
在ghci提示符输入 :?,则会显示详细的帮助信息。
模块Prelude有时候被称为“标准序幕”(the standardprelude),因为它的内容是基于Haskell 98标准定
义的。通常简称它为“序幕”(theprelude)。
Note
关于ghci的提示符
提示符经常是随着模块的加载而变化。因此经常会变得很长以至在单行中没有太多可视区域用来输入。
为了简单和一致起见,在本书中我们会用字符串 ‘ghci>' 来替代ghci的默认提示符。
你可以用ghci的 :setprompt 来进行修改。
Prelude> :set prompt "ghci>"
ghci>
prelude模块中的类型,值和函数是默认直接可用的,在使用之前我们不需要额外的操作。然而如果需要其
他模块中的一些定义,则需要使用ghcighci的:module
:module方法预先加载。
ghci> :module + Data.Ratio
本文档使用 看云 构建
- 4 -
Real World Haskell 中文版
现在我们就可以使用Data.Ratio模块中的功能了。这个模块提供了一些操作有理数的功能。
基本交互: 把ghci当作一个计算器 除了能提供测试代码片段的交互功能外,ghci也可以被当作一个桌面计
算器来使用。我们可以很容易的表示基本运算,同时随着对Haskell了解的深入,也可以表示更加复杂的运
算。即使是以如此简单的方式来使用这个解析器,也可以帮助我们了解更多关于Haskell是如何工作的。
基本算术运算 我们可以马上开始输入一些表达式,看看ghci会怎么处理它们。基本的算术表达式类似于像
C或是Python这样的语言:用中缀表达式,即操作符在操作数之间。
ghci> 2 + 2
4
ghci> 31337 * 101
3165037
ghci> 7.0 / 2.0
3.5
用中缀表达式是为了书写方便:我们同样可以用前缀表达式,即操作符在操作数之前。在这种情况下,我
们需要用括号将操作符括起来。
ghci> 2 + 2
4
ghci> (+) 2 2
4
上述的这些表达式暗示了一个概念,Haskell有整数和浮点数类型。整数的大小是随意的。下面例子中的
(^)表示了整数的乘方。
ghci> 313 ^ 15
27112218957718876716220410905036741257
算术奇事(quirk),负数的表示
在如何表示数字方面Haskell提供给我们一个特性:通常需要将负数写在括号内。当我们要表示不是最简单
的表达式时,这个特性就开始发挥影响。
我们先开始表示简单的负数
ghci> -3
-3
上述例子中的-是一元表达式。换句话说,我们并不是写了一个数字“-3”;而是一个数字“3”,然后作
用于操作符-。-是Haskell中唯一的一元操作符,而且我们也不能将它和中缀运算符一起使用。
本文档使用 看云 构建
- 5 -
Real World Haskell 中文版
ghci> 2 + -3
:1:0:
precedence parsing error
cannot mix `(+)' [infixl 6] and prefix `-' [infixl 6] in the same infix expression
如果需要在一个中缀操作符附近使用一元操作符,则需要将一元操作符以及其操作数包含的括号内。
ghci> 2 + (-3)
-1
ghci> 3 + (-(13 * 37))
-478
如此可以避免解析的不确定性。当在Haskell应用(apply)一个函数时,我们先写函数名,然后随之其参数,
比如f3。如果我们不用括号括起一个负数,就会有非常明显的不同的方式理解f-3:它可以是“将函数f应用
(apply)与数字-3”,或者是“把变量f减去3”。
大多数情况下,我们可以省略表达式中的空格(“空”字符比如空格或制表符tab),Haskell也同样能正确的
解析。但并不是所有的情况。
ghci> 2*3
6
下面的例子和上面有问题的负数的例子很像,然而它的错误信息并不一样。
ghci> 2*-3
:1:1: Not in scope: `*-'
这里Haskell把-理解成单个的操作符。Haskell允许用户自定义新的操作符(这个主题我们随后会讲到),
但是我们未曾定义过-。
ghci> 2*(-3)
-6
相比较其他的编程语言,这种对于负数不太一样的行为可能会很些怪异,然后它是一种合理的折中方式。
Haskell允许用户在任何时候自定义新的操作符。这是一个并不深奥的语言特性,我们会在以后的章节中看
到许多用户定义的操作符。语言的设计者们为了拥有这个表达式强项而接受了这个有一点累赘的负数表达
语法。
布尔逻辑,运算符以及值比较
Haskell中表示布尔逻辑的值有这么两个:True和False。名字中的大写很重要。作用于布尔值得操作符类
本文档使用 看云 构建
- 6 -
Real World Haskell 中文版
似于C语言的情况:(&&)表示“逻辑与”,(||)表示“逻辑或”。
ghci> True && False
False
ghci> False || True
True
有些编程语言中会定义数字0和False同义,但是在Haskell中并没有这么定义,同样的,也Haskell也没有
定义非0的值为True。
ghci> True && 1
:1:8:
No instance for (Num Bool)
arising from the literal `1' at :1:8
Possible fix: add an instance declaration for (Num Bool)
In the second argument of `(&&)', namely `1'
In the expression: True && 1
In the definition of `it': it = True && 1
我们再一次的遇到了很有前瞻性的错误。简单来说,错误信息告诉我们布尔类型,Bool,不是数字类
型,Num的一个成员。错误信息有些长,这是因为ghci会定位出错的具体位置,并且给出了也许能解决问
题的修改提示。 错误信息详细分析如下。 “No instance for (Num Bool)” 告诉我们ghci尝试解析数字
1为Bool类型但是失败。 “arising from the literal
1'” 表示是由于使用了数字1而引发了问题。 “In the definition of it'” 引用了一个ghci的快捷方式。
我们会在后面提到。
Tip 遇到错误信息不要胆怯 这里我们提到了很重要的一点,而且在本书的前面一些章节中我们会重复提
到。如果你碰到一些你从来没遇到过的问题和错误信息,别担心(panic)。刚开始的时候,你所要的做的仅
仅是找出足够的信息来帮助解决问题。随着你经验的积累,你会发现错误信息中的一部分其实很容易理
解,并不会像刚开始时那么晦涩难懂。 各种错误信息都有一个目的:通过提前的一些调试,帮助我们在真
正运行程序之前能书写出正确的代码。如果你曾使用过其它更加宽松(permissive)的语言,这种方式可能
会有些震惊(shock).所以,拿出你的耐心来。 Haskell中大多数比较操作符和C语言以及受C语言影响的语
言类似。
ghci> 1 == 1
True
ghci> 2 < 3
True
ghci> 4 >= 3.99
True
有一个操作符和C语言的相应的不一样,“不等于”。C语言中是用!=表示的,而Haskell是用/=表示的,
它看上去很像数学中的≠。
本文档使用 看云 构建
- 7 -