前言
本书不同于目前已有的许多其他编程书籍。它没有仅专注于某一种特定的编程语言或程序库,而是使用现有的语言和程序库来解释一种思维方式,这种思维方式的重要性日益增加,而且已经对许多新兴技术产生了影响。
读者可能已经了解了本书介绍的一些概念,因为函数式思想在许多技术中都有所体现。.NET领域的例子包括C# 3.0和LINQ项目、Microsoft Parallel Extensions to .NET以及Windows Presentation Foundation (WPF)中所使用的声明性编程模型。在本书中,我们将在读者已有的.NET和C#经验基础上,解释函数式编程范例。我们将介绍微软新推出的函数式编程语言F#,并用它将抽象思想变得更具体一些。如果可能,我们还会给出C#示例,这是因为在设计C#应用程序时,函数式思想也可以提供一些帮助。
如果我们要编写一本专门介绍F#的书,可能会完全根据它的各个语言特征来组织篇章结构,并对这些特征进行逐一解释。但本书整体上是关于函数式编程的,其结构基础比较松散,也就是那些构成函数式范例的思想。这种组织方式要更困难一些,因为思想之间没有清晰界限,经常是相互重叠的。
我们尝试选出了一些思想,我们认为这些思想对于函数式编程入门者来说是最为重要的,并围绕着这些思想来编写本书。这一点在第Ⅱ部分尤其重要,这一部分系统地研究了函数式、高阶函数和函数式程序的体系结构。这就意味着,有一些可用于快速演示函数式编程的例子(如处理数据集合的例子),不会仅在书中出现一次,而是在介绍各个新思想之后,在若干章节中逐渐开发这些例子。我们之所以决定采用这一方法,是因为它表明了函数式编程是如何从一小组简单概念中优雅地发展而来的,就像函数式程序本身一样。
致谢
“蝴蝶效应”,这个由Edward Lorenz创造的词语是以混沌理论为基础的:一个非常微小的事件,例如在亚洲某个地方的蝴蝶扇动了一下翅膀,可能会引起一个非常巨大的事件,如南美洲发生一次飓风。在概念上,蝴蝶扇动翅膀是固定的,而位置(亚洲或巴西)和结果(飓风或龙卷风)是变化的。其实说一句话就足够了:有太多我无法在此一一列举的人(和蝴蝶),没有他们,就不可能有本书的存在。而且,即使我不相信混沌理论,我希望在此提到的人数也是非常庞大的。
如果不是遇到Don Syme,我可能永远都不会对F#和函数式编程发生兴趣。Don是我在微软研究院两个实习期内的导师,和他一起工作是一件非常愉快的事情,特别是在长时间地讨论F#(还有生活、宇宙和其他各种话题)时。我还要感谢微软研究院的James Margetson,他教给我许多很酷的函数式编程技巧。但是,如果我没有成为微软的MVP(最有价值专家),没有遇到Luke Hoban,那我就不可能遇到Don和James,是Luke Hoban后来把我介绍给Don的。如果像这样继续下去,我还要提到Michal Bláha、Jan Stoklasa、Bo?ena Mannová,他们是CodeProject.com的作者,当然还要提到其他许多人。
如果没有来自Manning的Mike Stephens,没有Harry Pierson,这本书也不会存在,Mike Stephens是最早与我联系的,没有Harry Pierson的最初参与,我们可能永远都不会着手编写本书。尽管我们一起合作的时间很短暂,但Harry的参与非常重要,而且使我备受鼓舞。
没有前面提到的这些人,就不可能开始编写本书,以下要提到的这些人,没有他们就不可能完成本书。
我非常感谢我的合著者Jon,感谢他帮助我度过了将初稿和草图转变为真正书籍的漫长过程。如果读者感受到这本书就是为你而写的,那也应当归功于Jon,因为他从读者的角度发出,将所有内容都调整为正确形式。最后还要说一声,Jon是一个非常出色的合作者,无论是通过网络,还是当面和他讨论本书,都是一件非常愉快的事情。
现在,我要感谢Manning为本书做出贡献的每一个人。我已经提到了Mike Stephens,他总是能够雪中送炭。Nermina Miller和Tara McGoldrick Walsh对我在日常编写过程中遇到的问题提供总体的指导性意见,而Mary Piergies和Liz Welch、Elizabeth Martin一起,帮助我找到具体解决方法。我还和Manning的其他许多优秀员工一起短暂合作过,他们是Gabriel Dobrescu、Steven Hong、Dottie Marsico、Christina Rudloff、Gordan Salinovic、Maureen Spencer和Karen Tegtmeyer。我还要感谢出版商Marjan Bace,他提供了无数非常有用的真知灼见。
Manning的人们还有一双幸运之手,他们在编写过程中的不同阶段,挑选出正确的人选进行评论。我们收到大量的评论、建议和提示,还有相当数量的正面反馈,这些反馈意见鼓励我尽可能多地考虑这些建议。除了我们的匿名评论者之外,我还要感谢我们的两位技术评论者——Matthew Podwysocki和Michael Giagnocavo。我过去也曾经担任过技术评论者,所以我非常了解他们的艰苦工作!还要特别感谢为本书作序的Mads Torgersen。
另外一群对本书提供宝贵建议的人们是早期草稿的读者们。首先是我的同事Jan Stoklasa和René Stein,还有那些通过“Manning提前获取计划”(Manning Early Access Program)购买本书并在论坛中共享反馈的人们(如Dave Novick、Peer Reynders、Vladimir Kelman和Michiel Borkent等人)。其他一些对本书成文有所帮助的评价者包括:Marius Bancila、Freedom Dumlao、Eric Swanson、Walter Myers、Keith J. Farmer、Adam Tacy、Marc Gravell、Jim Wooley、Alessandro Gallo、Lester Lobo、Massimo Perga、Andrew Siemer、 Austin Ziegler、Dave McMahon、Jason Jung、Joshua Gan、Keith Hill、Mark Needham、Mark Ryall、Mark Seemann、Paul King和Stuart Caborn。
当然,我还要感谢我的朋友和家庭。他们可能并不知道,虽然他们的问题“你的书什么时候完成啊?”可能听起来不像在鼓励,但我非常了解他们,我衷心地感谢他们别样的鼓励。最后但绝非最不重要的一个,非常感谢我最亲爱的Evelina,她不仅提供了非常宝贵的精神支持,还如此耐心地阅读和评论了大部分手稿。
Tomas Petricek
我首先要感谢Tomas和Manning的每一个人,使我有机会参与本书的撰写。能够亲身参与,尽绵薄之力,是非常有趣的,一方面从一本书中学习函数式编程,同时又将自己学到的东西融入一本书中,这一切让我乐在其中。我只希望我所做的微薄贡献能够有所帮助——我主要是充当一个富有热情但又无知的读者(当然还是一个C#狂热者),所以在某些方面,您现在读到的这本书经过了一些调整,是用来教我学习函数式编程的。对我而言,它是一份值得感恩的礼物。Tomas已经向Manning的所有编辑和其他员工表示了感谢,我希望再一次向他们表示谢意。
我的孩子们还太小,不能开始编程,而我的妻子则太……怎么说呢?大体来说就是太正常了吧——但当我疯狂工作时,我的孩子和妻子总是伴我左右,给我支持。我总是努力使自己保持头脑清醒,能够同时处理一本以上的书,而我的妻子(她撰写童话)似乎总是在为各种不同书目和出版社忙碌着计划书、详细目录、初稿、审稿、校稿和已提交的手稿。而且,她还嫁给了我——所有人都奇怪她是如何保持头脑清醒的。但是,我非常高兴她做到了,我要感谢她成为现在这样一个人。Tom、Robin和William都很喜欢技术,在这方面大有前途,但当我从办公室回到家中时,最喜欢的还是他们的微笑和拥抱。
最后,我还要感谢我的所有英语老师,特别是Simon Howells。我对编程语言的了解越多,我就越相信软件工程师应当最为关注的语言就是他用来与人们进行交流的语言,而不是计算机语言。就像我对计算充满激情一样,Simon Howells对语言和文学也充满着激情,这种激情给他的学生们留下了深刻印象。他很可能从来都不会阅读我著作中的一个单词,但他的教导将陪伴我一生。
Jon Skeet
关于本书
如果您是一位.NET开发人员,熟悉面向对象的技术,希望了解这场新的“函数式编程”运动都是关于什么内容的,还想知道如何从中获益,那这本书绝对是为您而写的。
本书是专门为那些拥有面向对象编程和C# 2.0工作经验的.NET开发人员定制的。当然,一般来说,读者不需要了解函数式编程,甚至不需要了解F#。事实上,如果已经习惯采用面向对象的方式来思考问题,那么在学习函数式编程时的难度会更大一些,因为许多函数式思想会显得非常陌生。我们在编写本书时已经考虑到这一点,所以在解释特定主题时,我们经常会从读者角度思考,并对OOP和函数式编程进行对比。
如果您是一位使用其他语言和工具(例如Java、Python或者Ruby)的面向对象程序员,能够快速理解语言,那么也可以从本书中获益。虽然我们给出的例子是采用C#编写的,但其中有许多例子在其他面向对象式语言中都是非常相似的。对于其他语言中所不存在的C# 3.0功能,本书给出了简要解释,所以不用担心会摸不到头脑。
本书的核心不是函数式编程的学术问题,但如果您是一位计算机科学系的学生,正在学习相关课程,也可以阅读本书,从中了解函数式概念的现实应用。
本书包含哪些主题
如果您还在犹豫本书是否适合自己,可以先看看本书包含的内容。
* 函数式编程概念。在阅读本书时,您将学习一种新的问题思考方式,将会看到复杂的面向对象设计模式如何在函数式编程中变为一个简单的概念。在使用任意一种语言进行编程时,都可以采用函数式思维方式,所以无论您使用哪种特定技术,它都是有用的。
* 实践中使用的基本函数式结构。现在,在C# 3.0中已经可以使用这些结构中的一部分,所以在纯粹的函数式F#实施方式旁边,还会给出许多其他示例:用我们熟悉的C#代码编写而成。我们还将解释C# 3.0中新增加的一些功能,以及它们与函数式思想的联系。深入理解这些概念,可以帮助您从这些新功能中获取最大利益。
* 编写实用F#代码。尽管本书并不是专门介绍F#的,但仍然会教给您足够的知识,供您开始应用F#。我们还将研究一些能够让F#真正发挥实力的领域,包括异步编程和创建可组合库等。
我们并不会宣称本书在所有方面都是完美的:计算领域不存在“一体适万用”的良方。应当知道,本书并不奢望让所有读者都满意。
本书不涉及哪些内容
本书的编写目标不是作为一本参考书。如果您还不熟悉函数式编程,阅读本书的最佳方式就是从头开始,然后按各章顺序阅读。我们假定读者会这样阅读本书,所以后面的章节经常引用前面已经解释过的概念。如果从本书中间开始阅读,可能会发现理解起来比较困难。
本书也不是F#编程的快速指南。要想成为一位优秀的F#程序员,唯一的方法就是理解函数式编程思想。您可以学习所有F#关键字,并用F#重写C#代码,但这样不会有太多好处。如果希望编写出合乎惯例的F#代码,那就需要学习函数式编程中所使用的一种不同思考方式。而这正是可以通过阅读本书学到的东西。在习惯了函数式思维方式之后,可以阅读其他书籍来帮助您更深入地学习F#。
我们的主要目的是编写一本书,可以帮助专业程序员编写一些解决方案,以解决现实世界的业务问题。但是,这并不意味着我们会为您提供一种现成的解决方案,用来解决您眼前需要解决的问题,而是主要介绍相关概念和技术。我们用许多例子来说明这些原理,但不可能涵盖F#和函数式编程的全部适用领域。
路线图
本书采用了一种重复式结构。在第Ⅰ部分(第1至4章),我们将解释最重要主题中的一些内容,使您可以体会到学习的动力,并理解是什么使函数式编程不同于其他编程方式。第Ⅱ部分(第5至8章)系统地讨论了函数式编程的基础。在第Ⅲ部分(第9至12章),我们将在此基础上,讨论函数式编程、优化的最佳实践,还要讨论大多数函数式程序员偶尔会用到的高级技巧。第Ⅳ部分(第13至16章)给出了一些更为复杂的例子,展示如何使用函数式编程来开发更大的现实世界项目。
第1章讨论了是什么原因使函数式概念变得日益重要。它给出一些现有技术的例子,您对这些技术可能已经有所了解,而且这些技术已经从函数式编程的某些方面获益。这一章还给出了用F#编写的第一个示例应用程序。
第2章介绍了函数式编程背后的概念。它没有给出任何细节,而且主要使用C#,从而可以帮助读者理解这些概念之间的相互关系,以及它们对于程序结构意味着什么。
第3章终于给出了一些实际函数代码。它介绍了F#中用到的一些数据类型,例如元组和列表。我们还会看到如何处理F#中的类型,但也会用C#来实现它们,以解释它们是如何工作的。这一章介绍了将函数作为值来使用的思想,它对于函数式编程是非常重要的。
第4章给出了第一个用F#实现的现实世界应用程序。我们将使用各种.NET和F#库来实现一个用于绘制饼图的程序。在该章还将看到如何在开发过程中有效使用F#所提供的工具。
第5章对值进行了讨论。函数式程序被编写为一些计算式,它们取得参数值,返回结果值,因此,很容易就可以看出,为什么在开始系统地评论函数式功能时,必须首先研究各种各样的值。
第6章描述了处理值的最常用方式,也就是使用高阶函数。直接对值进行处理,经常需要大量的重复代码,所以本章介绍了如何设计和实施可重复使用的操作。
第7章将注意力转移到体系结构方面。函数式应用程序的结构由其要处理的数据确定。我们将使用一个管理和绘制简单文档的应用程序来说明这一重要原理。
第8章主要关注一些应用程序的体系结构,这些应用程序需要在运行时动态改变其行为。这一功能可以使用函数来实现,所以我们将详细讨论它们,还将解释诸如闭包之类的相关主题。
第9章说明如何在F#中混合使用面向对象式风格和函数式风格。它说明在编写函数式.NET库时,如何将函数式功能(例如不变性)与面向对象概念(如封装)结合使用。
第10章主要介绍正确性和效率。我们将看到如何编写能够处理任意大小数据集的函数,以及如何高效率地编写这些函数。您还会学习如何使用诸如数组之类的命令式结构来优化代码。
第11章讨论重构、测试和延迟。采用函数式编程时,对已有代码的理解和改进将变得更为容易,在本章将解释是如何做到的。我们将研究单元测试,了解如何因为具有了可组合性和严格性而不再需要进行某些种类的测试。
第12章开始展示如何处理数据集合。我们将介绍F#序列表达式,它是专门为这一目的设计的。您还会看到它不是一个内置功能,这一点不同于C#中与它最接近的对应内容,序列表达式是一种可以改变代码含义的更通用功能。
第13章展示了一种在F#中处理数据时的常见场景。它首先使用一种公共Web服务下载数据,然后将其解析为一种结构化格式。最后将会看到如何使用Excel对其中有意义的内容进行可视化。
第14章介绍如何利用函数式概念来生成易于并行处理的代码。该章使用一个图像处理应用程序和一个模拟来进行展示,在这个模拟中存在一些动物和猎取这些动物的捕食者。
第15章介绍如何构建声明性函数库。该章表明,可以优美地组合那些经过精心设计的库。在示例中,我们将看到如何创建一个生成动画的库和一个表示经济合同的库。
第16章介绍如何生成GUI应用程序,笼统而言是如何生成由外部事务驱动的程序。在其他语言中,要实现诸如此类的控制流是非常困难的,我们将学习一些技巧,利用这些技巧,可以非常容易地在F#中实现它。
印刷约定
本书包含大量代码示例,它们采用等宽字体排版。较长示例都展示在带有标题的代码清单中。由于本书混合采用了C#和F#,所以,在代码清单中,其标题还指出了所使用的语言。在展示用F#编写的代码时,我们在两种代码清单形式之间进行了区分。标有“F#”的代码是无格式源代码,可以作为整体进行编译。而标有“F# Interactive”的代码清单给出了以F#编写、输入到交互式外壳(shell)的代码段。由该外壳生成的输出使用斜体。在所有代码清单中,所有C#和F#关键字都使用等宽字体的粗体突出显示。
命名约定
在本书中,我们不仅混合使用了两种语言,还将函数式编程惯例与面向对象式惯例混合在一起。由于我们希望使用两种语言的自然风格,所以必须遵循F#和C#中的不同命名约定。
在C#中,我们遵循通常的.NET风格。在F#中,我们在开发类或者编写可以从其他.NET语言访问的组件时,也毫无例外地使用这种符号体系。而在展示仅作为私有实现的F#代码时,我们将遵循函数式命名风格。更特别的是,对于变量和函数名称均采用camelCase风格。这是F#中的标准风格,因为函数声明基本上与变量声明相同。
偶尔,我们会使用较短的名称和缩写,其中有两个原因。第一,函数式程序员经常使用这种风格。有了更好的IDE,使用这一风格的理由就比较少了,所以我们尽量减少它的使用。在某些情况下,较短名称是函数式编程的常见术语,所以我们还是保留了它。第二,有时我们会并排使用两个示例,这就意味着我们必须使用一种更紧凑的编码风格。在其他情况下,大多数命令遵循.NET风格,也有几种例外,在正文中将对其进行讨论。
StyleCop和FxCop
如果读者熟悉诸如StyleCop和FxCop之类的代码分析工具,可能了解本书中的代码是否符合这些工具所要求的规则。我们遵循了大多数(但并非全部)通常的.NET约定。如果通过这些工具来运行代码,会产生大量警告信息,其中同样有两个原因。
* 这些代码分析工具是为面向对象式语言开发的,而这些语言使用的命名规则与风格是符合面向对象惯例的。在本书中将会了解到,函数式世界在许多方面是有所不同的,我们不能遵循所有的面向对象式原则。F#是非常成功的,就是因为它非常不用于C#和Visual Basic。从语言关键字中看不出什么,但在它所采用的整体编程风格方面可以明显表现出来,在命名约定方面也有所体现,这种命名约定使代码更为紧凑。
* 本书的篇幅有限。我们使用源代码示例来演示重要的思想,所以我们不希望包含一些对于这些讨论并不重要而只是为了使代码符合约定而产生的干扰性内容。
源代码下载
本书示例的源代码可从出版商的网站上联机下载:http://www.manning.com/Real-WorldFunctionalProgramming,也可以从作者创建的代码库中下载:http://code.msdn.microsoft.com/realworldfp。
作者在线
购买本书之后,还可以免费访问由Manning运转的一个专用Web论坛,在这里可以对本书进行评论,询问技术问题,并接受来自作者及其他用户的帮助。要访问该论坛并订阅它,可在Web浏览器中打开http://www.manning.com/Real-WorldFunctionalProgramming。该网页将提供一些信息,告诉您在注册之后如果进入该论坛,可以获得哪些方面的帮助,以及该论坛中的一些行为规则。
Manning对读者的承诺是提供一种场合,供各位读者之间、读者与作者之间进行有意义的对话。关于作者的参与程度问题,并不做出承诺,作者们对本书论坛的贡献是自愿的(也是没有报酬的)。我们建议读者询问一些富有挑战性的问题,以免作者失去留在这里的兴趣!
在本书付印时,“作者在线”论坛及前面讨论的归档文件,都可以从出版商的网站上下载。
其他在线资源
除了Manning的网站(http://www.manning.com/Real-WorldFunctionalProgramming)之外,我们还为本书创建了一个配套网站:http://functional-programming.net。其中包含了一些可能对读者有用的信息、各章的源代码以及没有收入本书中的内容。这个网页还链接到最近发表的一些与函数式编程有关的文章,读者可以阅读这些内容,了解有关这一主题的更多内容。
如果您对F#感兴趣,可能还希望查看微软开发人员中心的官方网站:http://msdn.microsoft.com/fsharp。其中包含有关该语言的最新信息,以及指向文章、视频或其他F#资源的链接。如果希望询问有关F#的问题,并确保会得到满意的回答,请访问F#社区论坛:http://cs.hubfs.net。
封面介绍
本书封面上的画像题为“雇员”,它显示了一个办公室职员或文职人员,衣着优雅,头戴礼帽,还携带着一把雨伞。这幅图取自19世纪法国出版的Sylvain Maréchal四卷本地区衣着风俗纲要。每一幅图都是手工精心绘制和着色的。Maréchal作品集的多样性生动地提醒了我们,仅仅在200多年以前,世界上的村镇、地区在文化上有多大差异。由于相互隔离,人们说着不同的方言。在街道上或者在乡村中,只需要看看穿着,就可以很轻松地判断出人们生活在哪里,做什么生意,从事什么职业或者是什么身份。
从那以后,穿着代码发生了变化,当时非常明显的区域差异已经不复存在了。现在很难区分不同大陆的居民,更不要说不同村镇或地区了。也许我们已经用文化的多样性换取了一种更加多样化的个人生活,当然是换了一种更多样化、速度更快的技术性生活。
在这样一个很难将一本计算机书籍同其他书籍区分开来的时代,Manning独创性地为计算机书籍选用表现两个世纪以前地区生活多样性的图片作为封面,用Maréchal的图片让我们回归生活。