序章
扉页
另一本 Haskell 教程
(Yet Another Haskell Tutorial)
Hal Daume III
http://www.isi.edu/~hdaume/htut/
版权所有 Hal Daume III, 2002-2004
声明
这个教程的 preprint 版打算让整个 Haskell 社区都可用,所以我们给每个人都授予拷贝和发布的权利,当然你得保证本书的完整性,并且包括这个声明在内。
对本书的任何修改未经过作者同意均不能发表,作者同意後,可以在包含本声明的前提下发表。
本书的作者保留随时修改本书版权和不再长期免费提供本书的权利。
译者注:本教程之所以叫做《另一本 Haskell 教程》,是因为之前已经有一本《Haskell 教程》了。
关于本书
- 本书的目标是向读者提供一个完整的 Haskell 编程手册。读者不需要任何有关Haskell 编程或者函数型语言编程的知识便可以阅读此书。当然了,如果读者熟悉编程有关的一些基础知识(例如算法等等),那么将很有帮助。
- 本书不打算做有关编程的一般知识的介绍,它只讲述 Haskell 语言编程的有关细节。
- 为了便于学习,你必须足够熟悉你的操作系统和一种文本编辑器。另外,本书只讨论在Windows 下和 *Nix 下的 Haskell 编程。其它平台不予考虑,如有问题请参阅你的操作系统的相关文档。
什么是 Haskell?
Haskell 是一种惰性的、纯粹的函数型编程语言。
Hakell 之所以被称作是“惰性的语言”的原因,是因为对于“寻找问题的答案”来讲没有帮助的表达式是不被计算的。 和“惰性的”相反,大多数通用编程语言(C、C++、Java、even ML)的求值是严格的。一个严格的语言是说它的每一个表达式都需要求值,而不论这个表达式是否重要。(这么说也不完全准确,因为严格语言通常会有一个编译器优化的过程,可以消去代码中无用的表达式。)
Haskell 之所以被称作是“纯粹的函数型语言”原因,是因为它不允许副作用(side effect,有些着作上也译作“边际效应”) 的产生。所谓“副作用”是指影响了环境状态,例如一个函数望屏幕上打印了一些字符就被认为是“副作用”,就像是一个函数影响了一个全局变量一样。当然了,一种不产生副作用的语言是没有任何用处的。Haskell 使用一种技术将所有被污染的代码和其馀的程序部分分离出来,用一种安全的方式单独执行。参见第九章“Monads”和第五章“如何在纯函数型编程语言中进行输入输出”。
Haskell 之所以被称作是“函数型编程语言”的原因,是因为一个 Haskell 程序的求值过程看上去和纯粹的数学函数的求值过程是一样的。这和大多数通用编程语言如 C 或者 Java 等不同,後者将一个程序看作是一个语句的序列,并且一条一条执行它。这种语言通常被称作是命令式语言。
Haskell 语言的历史
事情要从头说起。下面是一段引用自《Haskell 98 报告》的文字:
1987年9月,“函数型编程语言和计算机体系(FPCA'87)”研讨会在波兰召开。在俄勒冈,大家针对目前函数型编程语言的窘境进行了讨论:当时世界上已经产生了十多种不严格的、纯粹的函数型编程语言。它们拥有类似的语法基础和表达能力。在这次会议中,与会者一致认为,缺少一种通用的语言规范已经成为大面积推广函数型编程语言的最大障碍。他们决定有必要成立一个委员会去设计这样一种通用的函数型编程语言。如果事情发展得更顺利的话,应该成立一个基金会去支持实际的应用程序开发,包括一种能够促进函数型编程语言的媒介手段。《Haskell 98 报告》发表了委员会的全部努力成功:一种被称作叫Haskell 的纯粹的函数型编程语言。它以着名逻辑学家 Haskell B. Curry 的名字命名。
委员会最初的目的是想要设计出满足下面这些要求的语言:
1. 它必须能够适合教学、研究、应用开发,包括一些大系统的构造。
2. 它必须能够使用形式语言来准确描述。
3. 它必须是自由免费的,任何人只要愿意都可以获取、使用和再次发布它。
4. 它必须建立在大家一致认可的基础上。
5. 它应该能够消除目前的函数型编程语言的差异。
委员会的目标是 Haskell 语言能够成为将来研究语言设计的一个基础,并且希望它的扩展或变体能够适应将来的各种需求。事实上,从 Haskell 第一次发布以来,它的确在演变。到了 1997 年中期,Haskell 规范已经进行了 4 次演变(最後的发布称作是 Haskell 1.4)
1997 年在阿姆斯特丹举行的 Haskell 研讨会决定,现在需要制定一个稳定的 Haskell变体。最後,会议报告将这个稳定的 Haskell 变体命令为“Haskell 98”。Haskell 98 对Haskell 1.4 进行了小小的整理,使之更加单纯,并且舍弃了一些不谨慎的部分。
最初的 Haskell 标准制定的规范後来发展成了 Haskell 标准库,称作“Prelude”。
另外,大多数程序需要访问大量的库函数(特别象输入输出等和操作系统的简单交互),如果这些程序需要移植的话,那么又得制定一套标准库。就在 Haskell 98 确定以後的那个时候,这种需求越来越变得清晰,因此,另一个委员会开始为了完善 Haskell 98 函数库而努力。
为什么要使用 Haskell?
首先有一点得确定,那就是在开始学习本教程之前,确保你是因为对Haskell感兴趣而开始使用 Haskell。我个人使用 Haskell 的原因是,相比其它语言来讲,我可以使用Haskell在更短的时间内写出更多的没有 BUG 的程序。我也认为它的可读性和延展性非常好。
也许更重要的是,我可以在 Haskell 社区中获得令人难以置信的帮助。Haskell 语言不断地演化,以至于我们甚至都不能说它是“稳定的”,许多非常有用的特性正在被添加到各种编译器中,并且每一次扩展都会采纳一些用户建议进去。
为什么不使用 Haskell?
我和大多数我所了解的 Haskell 爱好者共同的两个最大的抱怨是:
- 和 C 等其它语言相比,同样的程序,用 Haskell 写出则运行得更慢一些。
- 和其它语言相比,Haskell 程序几乎无法调试。
第二个问题几乎不是一个问题,因为我写过的 Haskell 程序很少有 BUG,大多数其它语言开发中通常碰到的 BUG 在 Haskell 中根本就不存在。而第一个问题我经常碰到。
然而,和程序员编制程序的时间相比,CPU 的时间更加廉价。我宁愿多等一点儿时间让计算机去执行程序,也不愿意花上好几天去调试我的程序。当然了,这个观点并不适合所有的程序。一些人可能会认为 Haskell 的速度简直无法忍受。不过这也没关系,Haskell 拥有一套机制允许你将其它语言书写的代码链接进来。因此,如果你在 Haskell 里边需要更快的速度的话,你可以将那部分程序用其它语言来书写,然後在 Haskell 里边链接进来。
如果在 Haskell 中链接其它语言代码仍然不能够满足你的要求的话,我建议你去看看O'Caml 语言,它拥有比 C++ 更好的特性,也包括了一些 Haskell 的特点。
什么样的读者适合阅读本书?
事实上存在很多关于 Haskell 的教程。下面这个网址有一个很完整的清单:
http://haskell.org/bookshelf (Haskell 书架)
下面对它们做一个简单的介绍:
- 《A Gentle Introduction to Haskell》是一个为已经熟悉函数型编程语言的读者准备的 Haskell 介绍。
- 《Haskell Companion》是一个对通用概念和定义的简短介绍。
- 《Online Haskell Course》是一篇用德文书写的 Haskell 快速入门。
- 《Two Dozen Short Lessons in Haskell》(Haskell 二十四学时教程)是一本非常好的正规 Haskell 教材。
- 《Haskell Tutorial》是“第三国际暑期学校”(译者注:不知道到底有没有这个学校?)规定的《高级函式编程》课程的教材。
- 《Haskell for Miranda Programmers》假设读者已经拥有 Miranda 语言的知识。
- 《PLEAC-Haskell》是一个模仿《Perl Cookbook》风格的教程。
尽管所有的教程都是非常优秀的,但是他们都不是完整的。《A Gentle Introduction to Haskell》对于初学者来讲过于深奥而其它的书则过于简单。而且,没有一本书有对输入输出和交互式程序设计的足够介绍。对于初学者来讲 Haskell 充满着陷阱,即使是你已经有其它非函数型语言编程的经验也是如此。
为此,我们需要一本教程,可以让那些不了解函数型编程语言,却了解其它类型的编程语言的人能够了解 Haskell。本教材不是为初级程序员准备的:它假设你已经了解一些计算机基础知识和编程知识。(附录中有一些背景信息介绍)
Haskell 语言在经过 10 年的标准化进程後,最後得到 Haskell 98。因此本书中将以Haskell 98 作为标准。任何和标准 Haskell 有偏差的地方都会被注明(例如,许多编译器提供的一些有用的扩展)。
本书的目标是:
- to be practical above all else
- 提供一个 Haskell 的全面介绍
- 提出常见的陷阱和它们的解决方案
- 使读者正确判断“如何在现实世界中使用 Haskell”
原作者序
致谢 Acknowledgements
It would be inappropriate not to give credit also to the original designers of Haskell. Those are: Arvind, Lennart Augustsson, Dave Barton, Brian Boutel, Warren Burton, Jon Fairbairn, Joseph Fasel, Andy Gordon, Maria Guzman, Kevin Hammond, Ralf Hinze, Paul Hudak, John Hughes, Thomas Johnsson, Mark Jones, Dick Kieburtz, John Launchbury, Erik Meijer, Rishiyur Nikhil, John Peterson, Simon Peyton Jones, Mike Reeve, Alastair Reid, Colin Runciman, Philip Wadler, David Wise, Jonathan Young.
Finally, I would like to specifically thank Simon Peyton Jones, Simon Marlow, John Hughes, Alastair Reid, Koen Classen, Manuel Chakravarty, Sigbjorn Finne and Sven Panne, all of whom have made my life learning Haskell all the more enjoyable by always being supportive. There were doubtless others who helped and are not listed, but these are those who come to mind.
- Hal Daum'e III
译者自序
2005.03.05,我有幸得见唐宗汉先生和各位天南海北的朋友。一群志同道合的人为了学习、使用、推广 Perl 而从全国各地来到北京。长达 10 个小时的聚会很快就过去了,给我留下的却是强烈的震撼。所谓井底之蛙,坐井观天,这次聚会,让我得知世界上原来还有这么一群人以这样一种方式生活着。唐先生随身携带电脑以及等电梯之馀、出租车上都要编两行程序给了我很深刻的印象。鲁迅先生曾说过:“时间就象是海绵里的水一样,要挤总是能挤出来的”,以前总觉得自己很忙,没有时间做自己想做的事。现在想来,都不过是借口罢了,lazy 才是真的。
这次聚会中,唐先生讲述了籍由 Pugs 来帮助 Perl6 开发进程的的思路,令人耳目一新。我虽不才,也想一览 Pugs 代码。但是 Pugs 是 Haskell 开发的,因此便开始接触 Haskell。
开始学习 Haskell 的过程中,惊奇地发现 Haskell 有关的中文资料居然非常少。我用 google 仅搜到一篇中文介绍,还算比较全面。无奈,只好寻求唐先生的帮助,于是开始阅读本书:<<Yet Another Haskell Tutorial>>。并且将自己对本书的理解以中文的形式再写出来,也算是一种翻译吧。
在次,我首先要感谢“如飞(rufi)”!他写的《Haskell教程》一文非常的漂亮。正是因为这篇文章,我才得以了解 Haskell 的概貌。并由此喜欢上 Haskell 这种语言。其次,我要感谢唐宗汉先生和各位 PerlChina 社区的朋友们!正是他们的帮助,才让我这次翻译得以顺利地进行。
由于本人英文水平有限,对 Haskell 又是全然不懂,所以肯定有错误之处,还望各位朋友不吝赐教!
岁在乙酉,人在京城
王兴华(flw) 于 2005 年春
概述
本书发布时含有大量的例子代码,如果不幸你的副本中没有,你也可以访问 Haskell 的官方网站 http://haskell.org/ 来获取它们。在本书中,例子程序用以下的样式和其馀的文本区分开来:
print "Hello, world" ( 用粗实线框来表示 )
和操作系统的输入输出交互部分的文字则用这种样式来表达:
% cd /usr/home/flw ( 用粗虚线框来表示 )
在本书中,经常会出现一些提示信息。这些信息或者是表示和其它的语言做比较,或者是提供一些帮助信息。这些提示信息通常以下面的样式出现:
【注意】 提示信息用细实线框包起来
如果出现了一些令人难以理解的部分,我们将会放置一个警告来提醒您的注意:
【警告】 警告信息用细虚线包起来
临了再说明一下,我们有时候会提及到内建函数(也可以叫做前奏函数)。内建函数看上去是这样子的:
map :: (a->b) -> [a] -> [b]
开始学习 Haskell
目前存在有三个 Haskell 系统: Hugs、GHC 和 NHC。Hugs 仅仅只是一个解释器,换句话说,你不能使用 Hugs 编译出可以脱离 Hugs 运行的独立程序,不过你可以用 Hugs 来调试你的程序,Hugs 是一个优秀的解释环境。GHC 可以以解释和编译两种方式工作。GHC 既可以像 Hugs 一样工作,也可以将你的程序编译成可以脱离 GHC 环境执行的独立程序。NHC 则是一个专门的编译器。至于选用哪一个工具是你自己的事情,我只在下面的清单中对它们做一个尽量全面的比较:
Hugs - 速度非常快。实现了几乎所有的 Haskell 98 语法,并且支持许多扩展。内建有模块浏览支持。不能生成独立运行的程序。Hugs 是用 C 语言书写的。可以工作在几乎所有的平台。内建有图形库支持。
GHC - 它的解释器环境要比 Hugs 慢一些,但是允许定义函数(如果是在 Hugs 下,那么你必须修改源文件,然後 reload 一下)。GHC 支持所有的 Haskell 98语法及其扩展。有良好的和其它语言的接口。GHC 可以说是 Haskell “实际上”的标准。
NHC - 很少使用,并且没有交互式环境,只是个编译器而已。不过它生成的可执行文件要比 GHC 生成的更小、更快。支持 Haskell 98,也支持一些扩展。