编辑推荐
适读人群:针对希望学习FP并将它应用于日常编码中的程序员
√Scala的入门好书有《快学Scala》,但讲高阶特性的仅此一本。
√自成体系已显大师风范,亲手设计习题更是罕见,《Scala函数式编程》习题可谓王冠之明珠。
√经典名著,岂敢怠慢!《Scala函数式编程》译者是Scala社区翘楚,更有旗帜人物作序力荐。
√好书不仅给你知识,更带来挑战。《Scala函数式编程》实属典范,语言|函数式|智慧全面挑战。
内容简介
函数式编程(FP)是一种软件开发风格,它注重不依赖于编程状态的函数。函数式代码易于测试和复用,容易实现并发,且不容易受到bug的攻击。Scala是一种能很好支持函数式编程的新兴JVM语言。《Scala函数式编程》是针对希望学习FP并将它应用于日常编码中的程序员而写的,内容包括:函数式编程的概念;函数式编程相关的各种“为什么”和“怎么做”;如何编写多核程序;练习和检测。
作者简介
王宏江,现任挖财技术部资深架构师,从事软件开发有十多年。曾在阿里巴巴担任架构师,参与过1688、来往等网站的基础建设,以及淘宝类目等基础业务重构,并在淘宝中间件负责过应用容器与分布式框架团队。Tomcat方面的专家,善于诊断问题以及性能调优。有丰富的企业架构和大型互联网技术架构经验。同时也是函数式编程爱好者,和Scala布道者。
精彩书评
这《Scala函数式编程》绝不轻易放过每个知识点,《Scala函数式编程》包含有大量习题,要求你自己实现Scala标准库或者Scalaz中的既有功能。所以,当你读完《Scala函数式编程》,做完习题后,虽然你的应用开发能力并不会直接提升,但你会体会到构建函数式语言和框架时的难点和取舍,从而增进你的框架开发和语言设计的能力。
——ThoughtWorksLeadConsultant杨博
这《Scala函数式编程》所讲授的,正是基于Scala的函数式编程基础。基于Scheme、Haskell等老牌函数式语言的传统教材的问题在于,相关语言的语法和思维方式与读者现有的知识体系迥异,容易造成较为陡峭的入门门槛。此外,由于这些语言本身的实际应用机会不多,初学者也难以在实战中获得宝贵的直觉和经验。而在Scala的帮助下,这《Scala函数式编程》并不要求你抛开现有的思维方式另起炉灶,它所做的更像是为你现有的思维方式添砖加瓦,从而令你如虎添翼。
——SparkcommitterfromDatabricks连城
尽管函数式编程在近十多年用得越来越多,但市面上介绍其高阶特性的书却并不多。这《Scala函数式编程》在这方面是个重要的补充,它不仅仅面向Scala程序员,同样面向用任何编程语言开发的程序员,只要你充满好奇心。
——挖财网首席架构师王宏江
“让你洞察计算的本质。”
——MartinOdersky,Scala的作者
“Scala和Java8开发者的函数式编程指南!”
——WilliamE.Wheeler,TekSystems
“《Scala函数式编程》向你展示了提升Scala技能的方法和理念,它已超过‘更好的Java’。”
——FernandoDobladez,Code54
“里面的练习有些挑战,很有趣,对你在真实世界中使用它很有益。”
——ChrisNauroth,Hortonworks
“边干边学,而非只是阅读。”
——DouglasAlan、Eli和EdytheL.Broad,哈佛和麻省理工学院
目录
原推荐序
序言
致谢
关于《Scala函数式编程》
函数式编程介绍
1什么是函数式编程
1.1函数式编程的好处:一个简单的例子
1.1.1一段带有副作用的程序
1.1.2函数式的解法:去除副作用
1.2(纯)函数究竟是什么
1.3引用透明、纯粹度以及替代模型
1.4小结
2在Scala中使用函数式编程
2.1Scala语言介绍:
2.2运行程序
2.3模块、对象和命名空间
2.4高阶函数:把函数传给函数
2.4.1迂回做法:使用循环方式
2.4.2第一个高阶函数
2.5多态函数:基于类型的抽象
2.5.1一个多态函数的例子
2.5.2对高阶函数传入匿名函数
2.6通过类型来实现多态
2.7小结
3函数式数据结构
3.1定义函数式数据结构
3.2模式匹配
3.3函数式数据结构中的数据共享
3.3.1数据共享的效率
3.3.2改进高阶函数的类型推导
3.4基于list的递归并泛化为高阶函数
3.4.1更多与列表相关的函数
3.4.2用简单组件组合list函数时的效率损失
3.5树
3.6小结
4不是用异常来处理错误
4.1异常的优点与劣势
4.2异常的其他选择
4.3Option数据类型
4.3.1Option的使用模式
4.3.2Option的组合、提升及对面向异常的API的包装
4.4Either数据类型
4.5小结
5严格求值和惰性求值
5.1严格和非严格函数
5.2一个扩展例子:惰性列表
5.2.1对Stream保持记忆,避免重复运算
5.2.2用于检测Stream的helper函数
5.3把函数的描述与求值分离
5.4无限流与共递归
5.5小结
6纯函数式状态
6.1以副作用方式生成随机数
6.2纯函数式随机数生成器
6.3用纯函数式实现带状态的API
6.4状态行为的更好的API
6.4.1组合状态行为
6.4.2嵌套状态行为
6.5更通用的状态行为数据类型
6.6纯函数式命令编程
6.7小结
功能设计和组合子库
7纯函数式的并行计算
7.1选择数据类型和函数
7.1.1一种用于并行计算的数据类型
7.1.2组合并行计算
7.1.3显性分流
7.2确定表现形式
7.3完善API
7.4API与代数
7.4.1映射法则
7.4.2分流法则
7.4.3打破法则:一个微妙的bug
7.4.4用Actor实现一个完全无阻塞的Par
7.5完善组合子为更通用的形式
7.6小结
8基于性质的测试
8.1基于性质测试概览
8.2选择数据类型和函数
8.2.1API的初始代码片段
8.2.2性质的含义与API
8.2.3生成器的意义和API
8.2.4生成值决定生成器
8.2.5精炼Prop的数据类型
8.3最小化测试用例
8.4使用库并改进其易用性
8.4.1一些简单的例子
8.4.2为并行计算编写测试套件
8.5测试高阶函数及展望未来
8.6生成器法则
8.7小结
9语法分析器组合子
9.1代数设计,走起
9.2一种可能的代数
9.2.1切片和非空重复
9.3处理上下文的相关性
9.4写一个JSON分析器
9.4.1JSON格式
9.4.2JSON分析器
9.5错误提示
9.5.1一种可行的设计
9.5.2错误嵌套
9.5.3控制分支和回溯轨迹
9.6实现代数
9.6.1一种可能的实现
9.6.2串化分析器
9.6.3标记分析器
9.6.4故障转移和回溯
9.6.5上下文相关的分析
9.7小结
函数设计的通用结构
10Monoid
10.1什么是monoid
10.2使用monoid折叠列表
10.3结合律和并行化
10.4例子:并行解析
10.5可折叠数据结构
10.6组合monoid
10.6.1组装更加复杂的monoid
10.6.2使用组合的monoid融合多个遍历
10.7小结151
11Monad
11.1函子:对map函数的泛化
11.1.1函子法则
11.2Monad:对flatMap和unit函数的泛化
11.3Monadic组合子
11.4单子定律
11.4.1结合法则
11.4.2为指定的monad证明结合法则
11.4.3单位元法则
11.5什么是monad
11.5.1identitymonad
11.5.2状态monad和partialtypeapplication
11.6小结
12可应用和可遍历函子
12.1泛化单子
12.2Applicativetrait
12.3单子与可应用函子的区别
12.3.1对比Optionapplicative与Optionmonad
12.3.2对比Parserapplicative与Parsermonad
12.4可应用函子的优势
12.4.1不是所有的可应用函子都是Monad
12.5可应用法则
12.5.1Leftandrightidentity
12.5.2结合律
12.5.3Naturalityofproduct
12.6可遍历函子
12.7使用Traverse
12.7.1从monoid到可应用函子
12.7.2带状态的遍历
12.7.3组合可遍历结构
12.7.4遍历融合
12.7.5嵌套遍历
12.7.6Monad组合
12.8小结
作用与I/O
13外部作用和I/O
13.1分解作用13.2一个简单的IO类型
13.2.1处理输入效果
13.2.2简单IO类型的优缺点
13.3避免栈溢出
13.3.1将一个控制流转化为数据构造子
13.3.2Trampolining:栈溢出的通用解决方法
13.4一个更微妙的IO类型
13.4.1合理的monad
13.4.2一个支持控制台I/O的monad
13.4.3纯解释器
13.5非阻塞和异步I/O
13.6一个通用的IO类型
13.6.1最终的main程序
13.7为什么IO类型不足以支撑流式I/O
13.8小结
14本地影响和可变状态
14.1纯函数式的可变状态
14.2一种限制副作用范围的数据类型
14.2.1受限可变性的语言表达
14.2.2一种可变引用的代数表达
14.2.3执行修改状态的行为
14.2.4可变数组
14.2.5一个纯函数的in-place快排实现
14.3纯粹是相对于上下文的
14.3.1副作用是什么?
14.4小结
15流式处理与增量I/O
15.1命令式I/O的问题示例
15.2一个简单的流转换器
15.2.1创建Process
15.2.2组合和追加处理
15.2.3处理文件
15.3可扩展的处理类型
15.3.1来源
15.3.2保证资源安全
15.3.3单一输入过程
15.3.4多个输入流
15.3.5去向
15.3.6Effectful通道
15.3.7动态资源分配
15.4应用场景
15.5小结
前言/序言
编写好的软件很难。在各种方法论中纠结多年,我们俩发现并爱上了函数式编程(FP)。尽管它与众不同,但它就是能引领我们编写出一致连贯、灵活组合、美丽优雅的程序。我们俩都是波士顿地区Scala爱好者群(BostonAreaScalaEnthusiasts)的成员,这个群会定期在剑桥聚会。起初,群里主要是一些Java程序员,他们一直寻求一些更好的东西。
后来大部分的人都表示,没有一个好的方法去学习如何用Scala进行函数式编程。我们学习的过程几乎都很随意,写一些函数式的代码,向其他Scala和Haskell程序员请教学习,阅读一些文章、博客和书籍。我们始终觉得应该有比这更简单的学习方法,直到2010年4月,群的组织者之一Nerminerifovi,建议我们写一本关于Scala函数式编程的书。本以为基于我们学习的经验,写一本期望中思路清晰的书是一件又快又容易的事情,没想到我们花了4年多才完成。要是我们当初学习函数式编程时,有这样一《Scala函数式编程》该多好啊。
希望这《Scala函数式编程》能够带给你一种兴奋刺激的感觉,犹如我们第一次遇到函数式编程那样。
原推荐序
函数式编程作为书题出现在Scala中是个有趣的现象。毕竟,通常Scala被称为函数式编程语言,而且在市场上有非常多的Scala相关书籍。是不是这些书都缺失了对语言函数式方面内容的描述?为了回答这个问题,我们需要有指导性地进行深挖。什么是函数式编程?
对我来说,它是“使用函数编程”的别名,换句话说,是一种聚焦在函数上的编程方式。那么什么是函数?再来探寻更大范围的定义。当一种定义承认函数可能有副作用并返回结果时,纯函数式编程限制函数就像数学里定义的那样:用一种二元关系去映射参数到结果。
Scala是不纯粹的函数式编程语言,它同时承认非纯粹函数和纯函数,而且没有使用不同的语法或给予不同的类型去区分这两种函数种类。其他函数式语言也有同样的属性。在Scala里如果能区分纯函数和非纯函数将是很好的,但我认为我们没有找到轻量级的和无需迟疑的灵活方式来这么做。
可以确信的是,Scala程序员是被鼓励使用纯函数编程的。副作用也有,比如易变、I/O或者异常的使用没有被禁止,事实上这些副作用有的时候使用起来十分方便,使用它们的原因有互用性、高效、方便等。但是专家的建议是过度地使用副作用普遍来说不是一种好的方式。然而,因为在Scala中非纯函数编程是可能的甚至是方便的,对命令式编程背景的程序员来说,保持他们的风格和不努力采用函数式思维的诱惑就非常大了。事实是,非常有可能将Scala编写成没有封号结尾的Java程序。
那么要学习Scala中的函数式编程,是不是需要先学习纯函数式编程,比如Haskell?任何关于方法的争论都在《Scala函数式编程》的出现后被极大地削弱了。Paul和Rúnar所做的是简单地将Scala作为纯函数式编程语言。可变变量、异常、经典的输入输出和所有其他的非纯函数被消除了。假如你想知道在没有这些便捷方式下如何编写有用的代码,你需要阅读此书。从第一个原理扩展到增量的输入输出,《Scala函数式编程》展示了如何使用纯函数表达每一个概念。而且不仅仅是展示了可能性,也同样引导你去编写优美的代码和深入探索计算的本质。
《Scala函数式编程》是充满挑战的,不仅仅是因为它需要对细节的注意,同样是对你编程思想的挑战。通过阅读《Scala函数式编程》和完成推荐的练习,你将更好地认识纯函数式编程是什么,能表达什么,优点是什么。
《Scala函数式编程》让我特别喜欢的是它的自成体系。它开始于最简单的表达式,然后从细节解释每个抽象,再在其基础上进一步抽象。在某种程度上,《Scala函数式编程》开发了另一个Scala“宇宙”,这里可变状态是不存在的,所有函数是纯的。普遍使用的Scala库的实现和这有些偏离,通常它们是部分按照命令式实现的,(大多数)外层是函数式接口。Scala容许在函数式接口中封装可变状态,我认为这是一个优点。但是这种能力通常也被滥用。假如发现自己过多地使用它,那么《Scala函数式编程》是一种强力的解药。
——MARTINODERSKY
Scala的创造者