Skip to content

Latest commit

 

History

History
353 lines (230 loc) · 25.8 KB

0.序言.md

File metadata and controls

353 lines (230 loc) · 25.8 KB

0. 序言

这是一本用Scala 3去解决实际问题的工具书,Scala则是我用过的最有趣的编程语言。本书中使用上千个示例去解释两百五十多个常见的Scala编程问题。

相较于其它Scala 3的学习资源而言,本书的独到之处在于:

  • 作为一本cookbook,本书旨在通过提供最常见问题的解决方案以节省你的时间。
  • 本书不仅涵盖了Scala语言,还包括Scala工具和库的介绍,包括SBT、Spark、Scala.js、Akka actors,以及用Play Framework处理JSON。
  • 本书对Scala集合类进行了深入研究,用5章的篇幅展示了它们的用途。
  • 几乎所有的例子都在Scala解释器中演示。这样一来,不论你是在电脑旁、飞机上,或者是在最喜爱的摇椅中阅读,都可以看到他们确切的结果。(好处是你经常会发现,“啊,原来是这么工作的。”)

Scala 3语言

Scala编程实战 第一版中,我将Scala 2描述为感觉像Ruby和Java的组合。当时我写道:“它像Ruby一样轻量、简洁、可读,但它又能编译成class文件打包成Jar文件,在Java虚拟机(JVM)上运行。 它使用特质(traits)和混入(mixins),感觉是动态的,但它是静态类型的。”

从那时起,Scala语言的特性就被重新思考,并在一个公开的公共过程中进行辩论。而随着2021年Scala 3的发布,这门语言感觉更加轻量了。现在,它似乎是四种优秀语言的结合:Ruby和Java,与Python和Haskell的轻量级和简洁的语法结合在一起。

这种更轻量的感觉部分归功于新的可选大括号语法,这也被称为显著缩进方式。有了这一变化,以前的for loops 看起来像这样:

    for (i <- 1 to 5) { println(i) }

现在看起来是这样的:

    for i <- 1 to 5 do println(i)

同样地,if表达式和许多其他表达式也使用了较少的模板语法,并且更容易阅读:

    val y = if (x == 1) { true } else { false } // Scala 2
    val y = if x == 1 then true else false      // Scala 3

虽然这种新的语法被认为是可选的,但它已经成为事实上的标准,并在本书中使用。包括我为Scala文档网站共同撰写的 Scala 3书籍https://oreil.ly/sUTXM ) 、Coursera上的Scala 3官方培训课程、Martin Odersky等人(Artima出版社)的 Scala编程(Programming in Scala) 书籍和Dean Wampler(O’Reilly)的 Scala编程(Programming Scala) 书籍,以及更多学习资源。

新的语法并不是唯一的变化。Scala 3有许多新的功能,包括:

  • 枚举类型
  • 并集类型和交集类型
  • 顶层定义(因此你的代码不再需要包含在类、特质和对象中)
  • 用新的 givenusing 语法简化了 隐式(implicits) 的使用
  • 大大简化了扩展方法和类型族的语法

甚至特质和类的语法也被简化,变得更容易阅读:

    trait Animal:
       def speak(): Unit
    
    trait HasTail:
       def wagTail(): Unit
    
    class Dog extends Animal, HasTail:
       def speak() = println("Woof")
       def wagTail() = println("⎞⎜⎛ ⎞⎜⎛")

在新的语法中,每一个在你的代码中产生不必要“噪音”的结构都已被删除。

Scala功能

除了刚才所说的一切之外,Scala还提供了大量的功能,使其成为一种独特的、真正的现代编程语言:

  • 它是由Martin Odersky(javac之父)创建的 —— 并受到Java、Ruby、Smalltalk、ML、Haskell、Python、Erlang和其他语言的影响。
  • 它是一种高级编程语言。
  • 它有一个简明、可读的语法 —— 我们称之为 表达式
  • 它是静态类型的 —— 因此您可以享受静态类型安全的所有好处 —— 但它感觉像一种动态脚本语言。
  • 它是一种纯粹的面向对象的编程(OOP)语言;每个变量都是一个对象,每个操作符都是一个方法。
  • 它同时也是一种函数式编程(FP)语言,所以你可以将函数作为变量。
  • 事实上,Scala( https://oreil.ly/MOunk )的本质,正如Odersky先生所说的那样。它是FP和OOP在类型化环境中的融合,其中:
    • 函数用于逻辑
    • 对象用于模块化
  • 它在JVM上运行,并且由于Scala.js项目( https://www.scala-js.org )。 它也是一个类型安全的JavaScript替代品。
  • 它能与Java和其他JVM库无缝互动。
  • 感谢GraalVM和Scala Native,你现在可以从Scala代码中创建快速启动的native可执行文件。
  • 创新的Scala集合库有几十种预先构建的函数方法,可以节省您的时间,并大大减少编写自定义循环和算法的需要。
  • Scala中内置了编程的最佳实践,它倾向于不可更改性、匿名函数、高阶函数、模式匹配、默认情况下不能扩展的类等等。
  • Scala生态系统提供了世界上最现代化的函数式编程库。

我喜欢Scala的一点是,如果你熟悉Java,你可以在第一天就能熟练掌握Scala。但这门语言很深奥,所以随着时间的推移,你会不断学习并找到更新、更好的方法来编写代码。Scala将改变你思考编程的方式 —— 这是件好事。

在Scala的所有优点中,我最喜欢的是它能让你写出简洁、可读的代码。据说,程序员花在阅读代码上的时间与花在编写代码上的时间相比,至少有10:1的比例,因此,编写简明可读的代码是一件大事。

Scala轻量且动态

Scala不仅仅是表现力强,它给人的感觉是一种轻量、动态的脚本语言。例如,Scala的类型推断系统消除了对显示声明的需求。你不必总是指定类型,而是简单地将你的变量分配给它们的数据:

    val hello = "Hello, world"  // a String
    val i = 1                   // an Int
    val x = 1.0                 // a Double

注意,我们不需要显示声明变量是 StringIntDouble 类型的。这是Scala的类型推断系统在发挥作用。

创建你自己的自定义类型的工作方式完全相同。给定一个 Person 类:

    class Person(val name: String)

你可以创建一个person:

    val p = Person("Martin Odersky")

或一个列表中的多人,没有不必要的模板代码:

    val scalaCenterFolks = List(
        Person("Darja Jovanovic"),
        Person("Julien Richard-Foy"),
        Person("Sébastien Doeraene")
    )

尽管我还没有引入表达式,但我怀疑任何有一点经验的开发人员都能理解这段代码:

    for
        person <- scalaCenterFolks
        if person.name.startsWith("D")
    do
        println(person.name)

而且,尽管我还没有引入枚举,但同一个开发者很可能知道这段代码的含义:

    enum Topping:
        case Cheese, Pepperoni, Mushrooms, Olives

请再次注意,这里没有不必要的模板代码;代码尽可能的“小马拉大车”,但仍然易于阅读。我们非常注意延续Scala作为一种声明式语言的传统。

在所有这些例子中,你可以看到Scala的轻量级语法,以及它给人的感觉是一种动态脚本语言。

读者对象

这本书旨在为使用Scala的程序员提供参考,在他们使用Scala和它的类库、工具遇到问题时,通过阅读本书快速找到答案。同时,我也希望本书成为想学习Scala的程序员的一个好工具。我个人是“通过实例学习”的信徒,且本书满满的都是例子。

我假设读者对另一种编程语言有一定的经验,诸如C、C++、Java、Ruby、C#、PHP、Python、Haskell等等。我本人用使用这些语言的经验,所以我的写作也受到这个背景的影响。

另一种描述本书读者对象的方式是通过软件开发者等级。在Martin Odersky的这篇文章( https://oreil.ly/QMV5U )中定义了如下不同等级的计算机程序员:

  • Level A1 初级应用程序员
  • Level A2 中级应用程序员
  • Level A3 专家级应用程序员
  • Level L1 初级类库设计师
  • Level L2 中级类库设计师
  • Level L3 专家级类库设计师

本书的主要目标读者为A1、A2、A3的应用开发者和L1类库设计者。帮助这些开发者是我的主要目标,同时我希望L2和L3的开发者也可以从本书众多例子中获益 —— 尤其如果他们没有函数式编程的经验,或者他们希望跟上Scala及其他相关工具和类库的更新的速度。

内容简介

本书是关于 解决方案 的,第1章 命令行任务 包含了一系列在命令行上使用Scala的例子。首先展示了如何使用Scala REPL的贴士以及功能丰富的Ammonite REPL,然后,介绍如何使用 scalacscala 等命令行工具来编译和运行你的代码,以及使用 javap 命令来反汇编你的Scala class文件。最后,展示了如何运行Scala生成的JAR文件。

第2章,字符串,介绍字符串使用的技巧。Scala String 的基本功能从Java继承而来,在Scala强大的隐式转换(implicit conversions)帮助下,Scala为字符串增加了很多功能,所以你也可以把它们当作一连串的字符值( Char values)。

第3章,数值和日期,介绍Scala的数值类型,以及Java 8引入的日期类。你将会看到,在Scala语言中,数值类型不支持++或者--操作符。本章解释了为什么,并演示了其他可以使用的方法。它还展示了如何处理大数字、货币以及如何比较浮点数。日期例子中使用了Java8日期类,还展示了如何使用遗留的日期类型。

第4章,控制结构,介绍Scala内建的控制结构,首先解释if/then声明、基础的 for 循环,然后提供如何使用for/yield(for comprehensions)循环以及 for 循环内嵌if声明的办法。鉴于表达式 匹配(match) 和模式匹配对于Scala的重要性,在此将展示用以上方法解决一系列问题的技巧。

第5章,,提供Scala类、参数和字段相关的例子。由于Scala的构造函数与Java的构造函数有很大的不同,因此我将用几个例子来展示实现主构造函数和辅助构造函数的来龙去脉。本章还会用几个例子介绍样例类( case class)以及如何使用他们。

第6章,特质和枚举,提供所有重要的Scala特质的例子,以及全新的枚举类型。首先展示如何像Java接口一样使用特质,然后深入到更高级的主题,比如如何用混入的方式使用特质,以及使用各种方法限制特质可以混合到哪些成员中。最后两个例子演示了如何在领域建模中使用枚举,包括创建代数数据类型(ADTs)。

第7章,对象,提供对象有关的例子,包括 对象 作为一个类的实例的含义,以及与 object 关键字有关的一切。

第8章,方法,介绍如何定义方法去接受参数、返回值,用指定参数名的方式调用方法,为方法参数设置默认值,创建可变的参数字段(varargs),以及编写支持流畅风格的方法。本章最后一个例子演示了全新的Scala 3扩展方法。

第9章,包和导入,提供Scala的 packageimport 声明的例子,由此可见其比Java中的这两关键字更加强大。具体包括如何使用大括号去引入包,在导入依赖时如何隐藏和重命名成员等。

尽管本书的大部分内容都展示了函数式编程技术,但第10章 函数式编程 将重点介绍函数式编程的技巧。本章中展示了如何定义匿名函数(函数字面量 function literals)并在各种场合下使用它们的方案。其中的例子包括如何定义一个接受函数参数的方法、部分应用的函数(partially applied functions),以及如何从函数中返回一个函数。

Scala集合库的内容丰富且深入,所以第11章到第15章提供了数百个与集合相关的例子。

第11章,集合:介绍,相关的例子将帮助你在不同的需求下选择适当的集合类以及方法去解决具体的问题,如不同集合间的转换,集合的过滤,创建集合的子集。

第12章,集合:常见序列类,展示最常见的集合类,包括 VectorListArrayBufferArrayLazyList。同时展示了如何创建每种类型,以及添加、更新和删除元素。

第13章,集合:常见序列方法,展示如何使用Scala序列类中最常见的方法。同时展示了如何对序列进行迭代、转换、过滤、排序等等。

与前一章展示常见的序列方法一样,第14章,集合:Map的使用,展示了许多用于Scala Map 类的相同技术。

最后,第15章,集合:Tuple、Range、Set、Stack和Queue,提供了其他Scala集合类的内容,包括元组(tuples)、范围(ranges)、集合(sets)、栈(stacks)和队列(queues)。

第16章,文件和进程,然后展示了如何处理文件和进程。首先提供用Scala读写文件,包括获得目录列表,以及如何使用序列化。然后我将用几个例子来展示如何以独立于平台的方式与外部进程一起工作。

第17章,简单构建工具(SBT),SBT(Simple Build Tool)是公认的Scala应用构建工具,本章是SBT的全面指南。首先将展示创建SBT工程结构的几种方式,其次介绍如何引入托管和非托管的依赖、构建你的工程、为工程生成Scala文档(Scaladoc)、部署工程等。 项目等等。

第18章,Scala Futures和Akka Actors的并发,本章为利用Futures和Akka actors库构建并发应用程序(以及使用那些多核CPU!)的奇妙世界提供了解决方案。本章futures的例子将展示如何构建一次性的、短暂的并发区,而actors的例子则展示了如何创建长期存在的actor,这些actor在其生命周期内可能会响应数十亿的请求。

第19章,Play框架和Web服务,介绍如何在Web服务的客户端和服务器端都使用Scala。在服务器端,将使用 Play框架 去开发RESTful Web服务。客户端和服务器端实例代码都会展示如何序列化和反序列化JSON,以及如何使用HTTP报头。

第20章,Apache Spark,介绍Apache Spark框架。Spark是使Scala出名的应用之一,本章实例将展示如何将大型数据集作为 弹性分布式数据集(RDD) 来处理,还展示了如何使用行业标准的SQL查询。

第21章,Scala.js、GraalVM和jpackage,提供了几个Scala和JVM领域的库和工具的实例。前面几个例子展示如何使用Scala.js作为类型安全的JavaScript替代物。最后的例子展示如何使用GraalVM将Scala代码转换为本地可执行文件,然后如何使用Java的 jpackage 工具将你的Scala应用打包成一个本地应用。

第22章,Scala与Java的交互,展示如何解决Scala与Java代码交互时可能遇到的少数问题。虽然Scala代码在与Java交互时往往能正常工作,但也有一些罕见的“陷阱”。本章将介绍如何去解决Scala和Java因集合库的差异引发的问题,以及在Java代码中调用Scala代码时可能遇到的问题。

第23章,类型,提供了使用Scala强大的类型系统的技巧。从介绍类型(type)开始,展示了诸如类型差异(variance)、边界(bounds)和约束(constraints)等的例子。例子将展示如何在类和方法定义中声明泛型,实现 “duck typing” ,以及控制你的特质可以混入哪些类型。本章还介绍了几个全新的Scala 3概念,包括不透明类型(opaque types),使用givenusing 来替代Scala2中的隐式值(implicit),并集交集和交集类型,和比较对象时的 相等性(equality) 有关的两个例子。

第24章,最佳实践,对于一本cookbook书来说是独一无二的,但由于这是一本介绍解决办法的书,我认为用一个章节专门介绍最佳实践(即如何以“Scala的方式”编写代码)是非常重要的。本章将展示如何创建没有副作用的函数,如何使用不可变的对象和集合类型,如何用表达式(而不是语句)来思考,如何使用模式匹配,以及如何消除代码中的空值(null)。

安装Scala

你可以通过几种不同的方式安装Scala 3,包括Homebrew(在MacOS上)、Coursier、SDKMAN,以及下载并手动安装Scala。Coursier被认为是“Scala安装程序”,它的使用在这个“Scala 3入门”页面( https://oreil.ly/Czdpa )中有所介绍。

如果你还不想安装Scala,你也可以使用这些在线工具在浏览器中进行实验:

代码风格约定

关于我在本书中使用的惯例,有几个要点需要了解。首先,如前所述,我使用了可选的大括号(显著缩进)编程风格,这减少了大部分的小括号和大括号使用:

    for i <- 1 to 5 do println(i)     // 使用这种风格
    for (i <- 1 to 5) { println(i) }  // 不使用这种风格

除了这种风格外,我还用四个空格缩进我的代码。目前还没有缩进的标准,开发人员似乎更喜欢两到四个空格。

其次,当我展示例子时,我经常在例子后面的注释中展示例子的结果,我的例子看起来像这样:

    (1 to 10 by 2).toList       // List(1, 3, 5, 7, 9)
    (1 until 10 by 2).toList    // List(1, 3, 5, 7, 9)
    ('d' to 'h').toList         // List(d, e, f, g, h)
    ('d' until 'h').toList      // List(d, e, f, g)

使用这种风格有助于我在本书中包括更多的例子,而这些例子是我在第一版中无法容纳的。

本书中使用的其他编码约定有:

  • 我总是把变量定义为 val 字段(就像Java中的 final 一样),除非有理由让它们成为 var
  • 当一个方法不需要参数并且有副作用时(比如打印到控制台),我会使用 def method() ,而不是 def method
  • 虽然在许多情况下,没有必要定义数据类型,但我总是声明公共方法的返回类型。

作为最后一个约定的一个例子,你可以定义一个方法而不声明其返回类型,就像这样:

    def double(i: Int) = i * 2

然而,大多数开发者更喜欢显示方法的返回类型:

    def double(i: Int): Int = i * 2

现在只需多打几个字符,就能使你的代码以后更容易阅读。

支持服务

本书中展示的许多源代码实例都可以在这个GitHub仓库中找到,其中包括许多完整的SBT项目。

  • github.com/alvinj/ScalaCookbook2Examples
  • github.com/bitlap/ScalaCookbook2Examples 这是本书中文版本fork的仓库

Scala Gitter频道( https://gitter.im/lampep/dotty )是一个很好的帮助来源。你偶尔会在那里看到我的问题。

如果你对Scala功能的提案和辩论感兴趣,“Scala贡献者”网站( https://contributors.scala-lang.org )也是一个非常好的资源。

最后,你可以在 alvinalexander.com 上找到我最新的博客文章,我经常在 twitter.com/alvinalexander 上发布关于Scala的话题。

排版约定

本书在印刷的字体上采取以下惯例:

斜体( Italic
       用于术语、链接、邮件地址、文件名、文件拓展名。

等宽字体(Constant width)
       用于代码列表,以及段落内程序元素如变量或者函数名、数据库、数据类型、环境变量、语句和关键字。

等宽粗体( Constant width bold
        显示命令或其他必须由用户输入的文本。

等宽斜体( Constant width italic
       显示应该用用户提供的值或根据上下文确定的值替换的文本。

使用示例代码

补充材料(代码示例、练习等)可以在 https://github.com/alvinj/ScalaCookbook2Examples 下载。

如果你有技术问题或在使用代码示例时遇到问题,请发送电子邮件至 [email protected]

本书是为了帮助你完成工作。一般来说,如果本书提供了示例代码,你可以在你的程序和文档中使用它。你不需要与我们联系以获得许可,除非你要复制大部分的代码。比如,用本书中的几段代码编写一个程序不需要许可。销售或分发O'Reilly出版书籍的光盘中的例子则需要获得许可。引用本书的内容和示例代码来回答问题并不需要许可。将本书中大量的示例代码加入到你的产品文档则需要获得许可。

如果引用本书则的内容,我们感谢你能标明出处,但是并不要求一定这么做。引用的出处通常以固定格式给出,包括标题、作者、出版商和ISBN。比如说:“Scala Cookbook by Alvin 亚历山大(O'Reilly)。Copyright 2021 Alvin Alexander, 978-1-492-05154-1.”

如果你觉得你对本书的示例代码的使用超出了合理使用或上述给出的免许可范畴,请通过[email protected]邮箱与我们联系。

O’Reilly在线学习

40多年来,O'Reilly Media 提供技术和商业培训、知识和洞察力,以帮助公司取得成功。

我们独特的专家和创新者网络通过书籍、文章和我们的在线学习平台分享他们的知识和专业技能。O'Reilly的在线学习平台让你可以按需访问现场培训课程、深入学习路径、互动编码环境以及来自O'Reilly和其他200多家出版商的大量文本和视频。欲了解更多信息,请访问 http://oreilly.com

如何联系我们

有关本书的意见和问题请向出版商提出:

O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international or local)
707-829-0104 (fax)

我们为这本书建立了一个网页,在那里我们列出了勘误表、例子和任何附加信息。你可以访问这个页面:https://oreil.ly/scala-cookbook-2e

如果期望对本书进行评论或提出技术问题,可以发送电子邮件至 [email protected]

如果期望了解更多关于书籍和课程的新闻和信息,请访问 https://oreilly.com 。同时还可以在以下社交平台找到我们:

在Facebook上找到我们:https://facebook.com/oreilly

在Twitter上关注我们:https://twitter.com/oreillymedia

在YouTube上观看我们:https://youtube.com/oreillymedia

致谢

写这么厚的一本书需要耗费很多时间,我要感谢责任编辑,Jeff Bleiel,一直在写作的过程中鼓励我,在遇到困难时让我保持清醒的头脑。我们从2018年12月开始合作编写这本书,虽然Scala 3在社区过程中不断变化,但我们一直在一起工作,直到2021年该书完成。

当我完成各章的初稿时,Jeff就如何改进它们提出了数百条建议。这个过程一直持续到COVID-19大流行期间,随着书中的内容越来越清晰,Jeff(正确地)建议重新组织几个章节的内容。他的工作非常彻底,我可以告诉你,当你看到一本由Jeff Bleiel编辑的书时,你可以确信它是经过精心编辑和思考的。

对于这版书,所有的审稿人都以不同的方式提供了帮助。Jason Swartz( https://twitter.com/swartzrock )是本书第一版的 “最有价值审稿人”候选人,他在这一版中又做了出色的工作,提出了许多可靠的建议。

Philip Schwarz( https://twitter.com/philip_schwarz )加入了我们这个版本,并提供了许多很好的见解,特别是对该书的早期章节。

但是对于这个版本,我应该特别感谢Hermann Hueck( https://twitter.com/hermannhueck ),他是这个版本最有价值的审稿人。Hermann提供了数百条大大小小的建议,涵盖了从最小的一行代码到本书的整体组织的各个方面。

我对Jeff和Hermann的评价是不言而喻的。但也许最好的说法是,没有他们,这本书就不一样了 —— 谢谢你们俩!

我还要感谢本书的制作编辑Christopher Faucher。在Jeff和我同意我们完成了最初的写作和编辑过程后,Chris进来了,帮助我们把书送到了终点,这期间我们处理了数百条意见和问题。如果你知道将一个大型软件应用到生活中是什么感觉,那么让这样一本大书通过终点线也是完全一样的。谢谢你,Chris!

最后,我要感谢Martin Odersky( https://twitter.com/odersky )和他的团队,感谢他们创造了如此有趣的编程语言。2010年,我在Anchorage,Alaska的一家书店发现他的 Scala编程 一书时,从此爱上了Scala,并一直持续到2021年的Scala 3及以后。

一切都好,

Alvin Alexander

译者注

  • 译者注1:Scala 3的大括号可以通过编译插件统一增加或者删除,甚至可以像Scala 2一样全部写大括号,所以称之为可选的。通常可以使用格式化工具统一代码风格。
  • 译者注2:因为Scala 2的 implicit 一个关键词代表的含义较多,如拓展方法、隐式参数/值、隐式类/转换,且涉及到多个作用域的优先级问题。所以Scala 3将 implicit 不同功能划分出来,这样初学者不再需要纠结这里的implicit是何种用法,以免造成困惑。
  • 译者注3:整体上遵从OOP对宏观系统进行建模,微观上使用FP实现对具体模块内逻辑进行设计和实现 —— 通常Scala项目是多范式的,不会仅使用一种,毕竟每个思想都有其优劣或局限。
  • 译者注4:for/yield循环准确来说并不是循环,而是for推断式/表达式,其是基于编译器的语法糖,主要目的是简化 flatMapmap等操作嵌套太深,使用for推断的代码更易阅读,与循环没有任何关系。
  • 译者注5:官方开始迁移到 https://discord.com/channels/632150470000902164/632150470000902166 频道。