Real World Haskell 前言
前言
做个约定吧!
Haskell是门深刻的语言,学习它将使一个非常有价值的经历。我们集中在三点来解释其原因。首先是新颖:我们请你从一个不同而有价值的视角来看待编程。其次是强大:我们将向你展示如何创建更短更快更安全的软件。最后,可以给你很多乐趣:用美丽的编程技术来解决实际问题的愉悦。
新颖
Haskell很可能与你曾经用过的任何语言都很不同。与程序员脑中通常的概念相比,函数式编程让我们看待软件有一个深刻的不同方式。
在Haskell里,我们不强调修改数据的代码。而是关注于那些取不变值作为输入并输出新值的函数。给出相同的输入,这些函数总是返回相同的输出结果。这是函数式编程的核心思想。
不仅不修改数据,我们的Haskell函数通常不与外界交互;我们称这样的函数为纯函数。我们把纯的代码与那些读写文件的程序严格分开。这更容易组织,验证和测试程序。
我们抛弃了一些可能看上去很基本的想法,如语言中内建 for 循环一类。我们有其他更灵活的方式来做重复的任务。
甚至在Haskell中对表达式求值的方式也是不同的。我们把所有的计算都推迟到其结果真的需要时: Haskell是惰性语言。惰性不只是移动下工作的事:它深刻的影响了我们如何写程序的方式。
强大
在全书各处都会展示Haskell的特性,相对于传统语言是多么强大灵活,他们可以产生更可靠的代码。Haskell确实塞满了如何创建优秀软件的领先思想。
因为纯代码与外部世界没有关系,并且它处理的数据用于不会改变,因此数据被其他程序修改导致隐形冲突的讨厌情况很少见。不管把纯函数用在什么场景中,它的行为总是一致的。
纯代码比处理外部世界的代码更容易测试。当一个函数只对它可见的输入负责时,我们可以很容易断定它行为的属性应该总是一样。我们可以自动用大量随机输入来测试这些属性,测试通过后继续往前走。我们还是用传统的技术来测试必须与文件,网络或者外部硬件进行交互的代码。由于这些非纯的代码比其他传统语言中要少得多,因此对我们的软件的稳固性更有信心。
惰性计算有些令人恐惧的影响。比如说我们想从一个未排序的列表里找出最小的k个元素。在传统语言里明显的方法是,把列表排序,然后取出前
k个元素,但这耗费颇大。为了效率我们不是写个特殊函数来一次取出这些值,而是必须做些适度的簿记工作。在Haskell里,排序并取值的方式实际上工作良好:惰性计算保证了列表只进行足够取出k个最小元素的排序工作。
更好的是,执行如此高效的Haskell代码只有很短,并且使用标准库函数。
-- file: ch00/KMinima.hs
-- lines beginning with "--" are comments.
minima k xs = take k (sort xs)
要建立起何时惰性计算重要的直觉可能要花一点时间,不过当我们使用它时,产生的代码经常更清晰简洁高效。
前面例子显示出,Haskell的强大很重要的一点在于其紧凑的代码上。与流行的传统语言相比,用Haskell开发时经常可以写出更短的代码,所花时间也更少,bug更少。
乐趣
我们相信要掌握Haskell编程的基础很简单,你用几个小时或者几天就可以成功的写些小程序了。
由于在Haskell中高效的编程与其他语言具有很大的不同,因此你要做好准备掌握这门语言本身以及函数式编程技术都将需要大量的思考和实践。
与我们自己开始学习Haskell的早期岁月相比,好消息是现在乐趣来得更早些:钻研一门新的语言并了解如何写出简单的程序只是愉快的挑战,有非常多不同或者缺失的普通概念。
对我门来说,最初的乐趣随着我们经验的增加和理解的深入一直持续着。在其他的语言中很难找到科学与基本编程间的任何联系。在Haskell里我们引入了一些抽象数学的思想并使之实际工作。而且我们发现这些思想不单容易被掌握而且有助于写出更紧凑可复用的代码。
更进一步,我们不会设置任何“高墙”:这本书里并没有特别困难或可怕的技术,不需要掌握他们就可以高效的编程。
有人说Haskell种严密的语言:它让你提前思考更多。在你可以可以运行它之前要对代码花多些时间调试,来处理编译器指出的程序中不合理的地方。一旦修改完编译错误后,我们的Haskell程序经常第一次运行就能正确,即使已经有了多年的经验,我们依然为此感到震惊和喜悦。
对本书的预期
越来越多的人开始用Haskell来解决日常的问题,因此我们开始了这个项目。由于Haskell的学院渊源,现在已经存在的Haskell书籍没有关注我们感兴趣的典型的编程技术的。
这本书里,我们想要向你展示如何使用函数式编程和Haskell来解决实际的问题。我们用手把手的方式:每一章都包含大量的代码例子,很多包含完整的应用。这些是要教你如何开发的库,技术和工具的例子。
* 创建一个软件,从互联网上下载播客段落,把历史记录保存在SQL数据库中。
* 用直觉和强大的方式来测试代码。描述出程序应具有什么属性,然后让 QuickCheck 库自动生成测试案例。
* 把手机摄像头拍下来的条形码转换成标识,可以用它在图书馆或在线书店网站进行查询。
* 编写适用于web的代码。与其他语言写的服务器和客户端用JSON格式交换数据。开发一个并发的链接检查器。
关于读者
读本书之前要先知道什么?我们期望你已经知道如何编程,不过如果你从来没用过函数式语言也没关系。
不管你的水平如何,我们都预先准备好你需要的了;我们尽量深入解释新的微妙的思想,经常配以例子和图片来说明自己的观点。
如果你知道那些库函数或编程技术的话,就可以避免很多手写的代码,不过新的Haskell程序员开始时很难避免。这本书里带了很多信息帮你尽可能的提速。
当然一路上总会有些磕碰。如果动身时做好准备,伴随着乐趣还有偶然的惊奇和困难,你将获得最好的体验。可能遇到的困难都不会持续太久。
当成为更有经验的Haskell程序员时,你写代码的方式会变化。实际上,在本书的课程其间,书里给出的代码也在进化,从语言的基础到强大富有生产力的特性和技术。
对Haskell的预期
Haskell是通用编程语言。被设计的不针对任何特定应用。虽然它对于程序该如何写有很强的立场,不过它并不偏重于某个问题领域。
这门语言的核心鼓励一个纯的惰性的编程风格,这是默认的但并非唯一的选择。Haskell也支持更传统的过程式代码和严格求值模式。另外,虽然这门语言关注于静态类形程序,也是可以(虽然很少见)用动态类型的风格来写Haskell程序。
与传统静态语言的比较
使用简单的静态类型系统的语言已经占据编程世界主导地位几十年了。Haskell是静态类型的,但是它对于类型可以做什么的见解与传统语言相比,要更加灵活强大。类型让Haskell程序更简短,清晰和强大。
尽管很强大,Haskell的类型系统经常可以不引人注目。如果忽略掉显示的类型信息,Haskell编译器将自动推断表达式或函数的类型。传统静态类型语言要喂给编译器非常多的类型信息,与此相比Haskell的类型的能力与推断相结合,显著的减少了代码的凌乱和冗余。
Haskell其他一些特性组合起来,能让代码更加漂亮。这会改善代码的开发时间和敏捷性:我们可以快速的创建可靠的代码,并且在需求变化时能更容易重新掌握代码。
有时候,Haskell程序比用C/C++写的相似的程序运行的慢。对于我们写的大部分代码来说,Haskell的巨大优点在于生产力和可靠性,这些优点超过了任何性能上小的损失。
多核处理器现在很普及了,但是用传统技术非常难以编写多核程序。Haskell为编写更易驾驭的多核程序提供了独特的技术。它支持并行程序,软件事务内存(STM)可以开发可靠的并发程序,可以扩展到成百上千个并发的线程。
与现代动态语言的比较
在过去十年间,动态类型的解释型语言已经变得很流行了。他们提高了开发生产力。虽然这经常会有很大的性能损失的代价,但是对于很多编程任务来说,生产力的重要性要超过性能,或者性能并不是关键因素。
Haskell和动态类型语言在简短性上很相似:解决问题时都比传统语言用更少的代码。动态语言和Haskell的程序经常差不多大小。
当考虑执行型能时,Haskell几乎总是具有巨大的优势。用 Glasgow Haskell编译器(GHC)编译的代码通常比用动态类型语言解释器要快20到60倍。GHC也提供了解释器,因此可以不需要编译,把程序当作脚本执行。
动态类型语言与Haskell对于类型的哲学存在重大不同。动态类型语言流行的一个主要原因是,我们很少需要显示的声明类型。通过自动类型推断,Haskell也提供了相同的优势。
除了表面的相似性,差别更加深刻。在动态类型语言里可以构造出静态类型语言很难表达的结构。然而反之亦然:通过像Haskell那么强大的类型系统,我们可以构造一个动态类型语言难于管理或实现的程序。
两种方式都涉及到交易。简单的说,Haskell的观点强调安全性,而动态类型更偏爱灵活性。如果有人已经发现了一种静态类型总是最好的方式,我们想所有人现在就该了解它。
当然我们这些作者对于哪种交易更有利有自己的观点。我们中的两个作者有很长时间的动态类型编程经验。我们很喜爱用他们工作;现在也在每天使用;不过通常我们更倾向于Haskell。
工业界和开源领域的Haskell
这里是一些用Haskell创建的大型软件系统的例子。有些是开源的,其他是私有产品。
* ASIC 和 FPGA 设计软件 (Lava, Bluespec 公司产品)
* 作曲软件 (Haskore)
* 编译器和编译器相关工具(最著名的 GHC)
* 分布式版本控制 (Darcs)
* Web中间件 (HAppS, Galois公司产品)
下面是截止到2008年底,使用Haskell的公司列表,来源是 Haskell wiki (http://www.haskell.org/haskellwiki/Haskell_in_industry)。
* ABN AMRO
一家跨国银行。在投资业务中使用Haskell衡量金融衍生品组合的风险!(译注:被国有化了)
* Anygma
一家创业公司。使用Haskell开发多媒体内容创作工具。
* Amgen
一家生物科技公司。用Haskell创建数学模型和其他复杂的应用。
* Bluespec
ASIC 和 FPGA设计软件提供商。他们的产品用Haskell开发,他们产品里提供的芯片设计语言受Haskell的影响。
* Eaton
使用Haskell来设计和验证氢气混合动力车辆系统。
编译,调试和性能分析
在实际工作中,一门语言周围的库和工具的生态系统与语言本身同样重要。Haskell在这方面表现很好。
最广翻使用的编译器 GHC 已经活跃的开发了超过15年,并且提供了成熟稳定的特性集。
* 可以编译成现代主要的CPU架构上的本地代码。
* 编译的二进制文件容易部属,没有许可证限制。
* 代码覆盖分析
* 详细的性能和内存使用描述
* 全面的文档
* 对并发和多核扩展性的大量支持
* 交互解释器和调试器
附带的和第三方的库
GHC编译器附带了很多有用的库。这里是满足普通编程需要的库。
* 文件 I/O,以及文件系统遍历和操作。
* 网络客户端和服务器编程
* 正则表达式和解析
* 并发编程
* 自动测试
* 声音和图像
Haskell社区把开源库和软件收集在 Hackage 软件包数据库里。Hackage里发布的大部分库都是自由协议的,允许商用或者开源使用。开源库涵盖的一些领域包括下面这些。
* 与全部主要的开源和商业数据库的接口
* XML, HTML 和 XQuery 处理
* 网络及web的客户端和服务器开发。
* 桌面GUI,包括跨平台库。
* 支持Unicode和其他文本编码
Haskell简史
Haskell开发的起源根植于数学和计算机科学的研究。
史前时代
在现代计算机发明的几十年前,数学家 Alonzo Church (阿隆索.丘奇)发展了一种称为lambda演算(朗姆达演算)的语言。他意图把这种语言当作研究数学基础的工具。第一个意识到编程与lambda演算之间实际联系的人是 John McCarthy,他在1958年发明了Lisp。(译注:John McCarthy 约翰麦卡锡,1971年图灵奖得主)
在20世纪60年代,计算机科学家开始意识到并研究lambda演算的重要性。Peter Landin 和 Christopher Strachey 发展了编程语言的基础思想:如何验证他们的实际行为(操作语义),以及如何理解他们的含义(指称语义)。
在20实际70年代早期,Robin Milner创建了一个名为 ML 的更加严格的函数式编程语言。开发ML是为了帮助自动证明数学定理,后面被用在更平常的计算任务上。
在 70年代开始显露出一种新的惰性计算策略。 David Turner 开发了 SASL 和 KRC, Rod Burstall 和 John Darlington 开发了 NPL 和 Hope。 NPL, KRC 和 ML 影响了80年代一些语言的开发,包括 Lazy ML, Clean和Miranda。
古代
到八十年代晚期,惰性函数式语言的研究者们的努力分散在超过一打的语言中。因为担心力量的分散,一些研究者决定成立一个委员会来设计一个共同的语言。经过三年工作,委员会在1990年发布了Haskell 1.0 规范。以非常具有影响力的逻辑学家Haskell Curry的名字命名。
很多人都怀疑“委员会设计”方式,但是Haskell委员会的工作是委员会方式成功的优美实例。他们拿出了一个优雅的经过深思熟虑的语言设计,并且弥合了研究社区的分歧。90年代纷乱的惰性函数式语言中,只有Haskell至今依然实际使用。
自从1990年发表,Haskell语言标准经过了5次修订,最近一次在1998年。已经开发了一些Haskell的实现,其中一些依然活跃的发展中。
在90年代,Haskell有两个主要目的。一方面,他给语言研究者提供了一种稳定的语言,拿来来试验如何让惰性函数式程序更高效。其他研究者研究如何用惰性函数式技术构造程序。还有些用作教学语言。
现代
随着90年代这些基础研究的进行,Haskell依然还是学术事件。社区里的非正式标语是“拼命避免成功!”。外部的人根本没听说过这门语言。实际上,函数式语言这个领域本身就很非著名。
在此其间,主流的编程世界也做了些相对较小的试验:从C编程到C++再到Java。同时程序员们开始用新的更加动态的语言来进行修补。Guido van Rossum 设计了Python;Larry Wall 创建了 Perl; Yukihiro Matsumoto 开发了 Ruby。
随着这些语言开始更广泛的使用,他们传播了一些重要的思想。首先程序员并非不能用富有表达能力的语言工作,实际上他们以此炫耀。其次是快速增长的计算能力的副产品:牺牲一些执行性能来换取程序员生产力的大量提高是明智的。最后,这些语言从函数式编程中借鉴了很多。
在过去的五年里,Haskell成功的从学院的象牙塔中逃离,像Python,Ruby 甚至Javascript那样有名了。现在在开源以及商业领域,这门语言有了生气勃勃快速增长的用户,而研究者们依然继续推动他的性能和表达能力的扩展。
有用的资源
在用Haskell工作时,肯定会遇到一些问题,想找更多资料。这里时一些互联网资源,可以找到很多信息,并与其他Haskell程序员交流。
参考资料
* The Haskell Hierarchical Libraries reference (Haskell库参考)
提供了编译器附带标准库的文档。这是Haskell程序员最宝贵的在线资源。
* Haskell 98 Report
描述了Haskell98语言标准。
* GHC Users's Guide
包含GHC支持扩展的详细文档,以及一些GHC的特性。
* Hoogle 和 Hayoo
Haskell API搜索引擎。可以用名字或者类型搜索函数。
应用程序和库
如果你想为特定任务找一个Haskell库,或者找一个Haskell写的应用程序,从下面的资源中找。
* Haskell 社区
社区维护了一个开源库和应用的中心数据库。名为 Hackage (http://hackage.haskell.org/)。可以让你搜索软件并下载,或者按类别进行浏览。
* Haskell Wiki (http://haskell.org/haskellwiki/Applications_and_libraries)
Haskell Wiki中专门有一节用来描述Haskell库的信息。
Haskell社区
有很多种方式可以接触到其他的Haskell程序员,问问题,看看别人都在讨论什么,或者与别人简单的建立社会网络。
* 搜索社区资源的第一站应该是Haskell的官方网站(http://www.haskell.org)。这个页面上有最近的社区和信息的链接,还有一个很大的活跃维护的wiki。
*
Haskell用户们用了好几个邮件列表,来分主题讨论(http://haskell.org/haskellwiki/Mailing_lists)。这里面最有趣的是 haskell-cafe。那里的气氛轻松友好,专家和大学老师们与黑客和初学者们随便交流。
* 要进行实时聊天,有一个名为 #haskell 的IRC频道,很大很活跃。像 haskell-cafe 一样,虽然有很大量的用户同时在线,那里气氛依然保持友善有益。
* 有很多本地的用户组,聚会,学术工作组等等;这里有个知名的用户组和工作组列表(http://haskell.org/haskellwiki/User_groups)。
* Haskell Weekly News (http://sequence.complete.org)是每周Haskell社区活动简报。可以找到感兴趣的邮件列表话题,新软件的发布等等。
* Haskell社区和活动报告 (http://haskell.org/communities/)收集了人们如何使用Haskell的信息。它已经运行了很多年了,因此提供了了解Haskell过去历史的好方法。
致谢
没有Haskell社区的话,这本书不会存在:无政府主义者们,满怀希望的艺术家们,理论家和工程师们,他们用了20年时间致力于创造一个更好的,没有bug的程序世界。Haskell社区的人在友善与知识深度的接合上是独一无二的。
感谢我们的编辑 Mike Loukides 和 O'Reilly 的产品团队,感谢他们的建议与协助。
[其他省略]
各位大虾,原文的chm文档在这里下载。
Real World Haskell 中文翻译目录:
第十一章 代码测试与品质保证
第十二章 应用:条码ISBN扫描
第十三章 数据结构
第十四章 Monads
第十五章 Monads 编程
第十六章 使用PARSEC
第十七章 与C的接口:FFI
第十八章 Monads 变形
第十九章 错误、异常处理
第二十章 应用:系统编程
第二十一章 使用Databases
第二十二章 扩展示例:Web客户端
第二十三章 使用PARSEC
第二十四章并发和多核编程
第二十五章 分析与优化
第二十六章高级库设计:建立一个Bloom过滤器
第二十七章 Sockets网络与系统日志
第二十八章 内存管理
附录A 安装GHC 和 Haskell 库
附录B 字符,字符串和转义字符