使用 Scala 有很多好处,特别是 Scala 3。 很难列出 Scala 的每一个好处,但“前十名”列表可能看起来像这样:
- Scala 融合了函数式编程(FP)和面向对象编程(OOP)
- Scala 是静态类型的语言,但通常感觉像一种动态类型语言。
- Scala 的语法简洁,但仍然可读;它通常被称为 易于表达
- Scala 2 中的 Implicits 是一个定义特性,它们在 Scala 3 中得到了改进和简化。
- Scala 与 Java 无缝集成,因此您可以创建混合了 Scala 和 Java 代码的项目,Scala 代码可以轻松使用成千上万个现有的 Java 库
- Scala 可以在服务器上使用,通过 Scala.js, Scala 也可以在浏览器中使用
- Scala 标准库具有数十种预构建的函数式方法,可节省您的时间,并大大减少编写自定义
for
循环和算法的需要 - Scala 内置了“最佳实践”,它支持不可变性,匿名函数,高阶函数,模式匹配,默认情况下无法扩展的类等
- Scala 生态系统提供世界上最现代化的 FP 库
- 强类型式系统
1) FP/OOP 融合
Scala 比任何其他语言都更支持 FP 和 OOP 范式的融合。 正如 Martin Odersky 所说,Scala 的本质是在类型化环境中融合了函数式和面向对象编程,具有:
- 函数用于编写逻辑 (局部)
- 对象用于构建模块化 (整体)
模块化的一些最佳示例可能是标准库中的类。
例如,List
被定义为一个类—从技术上讲,它是一个抽象类—并且像这样创建了一个新实例:
val x = List(1, 2, 3)
但是,在程序员看来是一个简单的 List
实际上是由几种特殊类型的组合构建的,包括名为Iterable
, Seq
, 和 LinearSeq
的 traits。
这些类型同样由其他小型的模块化代码单元组成。
除了从一系列模块化 traits 构建/cases像 List
这样的类型之外,List
API还包含数十种其他方法,其中许多是高阶函数:
val xs = List(1, 2, 3, 4, 5)
xs.map(_ + 1) // List(2, 3, 4, 5, 6)
xs.filter(_ < 3) // List(1, 2)
xs.find(_ > 3) // Some(4)
xs.takeWhile(_ < 3) // List(1, 2)
在这些示例中,无法修改列表中的值。
List
类是不可变的,因此所有这些方法都返回新值,如每个注释中的数据所示。
2) 动态的感觉
Scala的 类型推断 经常使语言感觉是动态类型的,即使它是静态类型的。 对于变量声明,情况确实如此:
val a = 1
val b = "Hello, world"
val c = List(1,2,3,4,5)
val stuff = ("fish", 42, 1_234.5)
当把匿名函数传递给高阶函数时,情况也是如此:
list.filter(_ < 4)
list.map(_ * 2)
list.filter(_ < 4)
.map(_ * 2)
还有定义方法的时候:
def add(a: Int, b: Int) = a + b
这在Scala 3中比以往任何时候都更加真实,例如在使用union types 时:
// union type parameter
def help(id: Username | Password) =
val user = id match
case Username(name) => lookupName(name)
case Password(hash) => lookupPassword(hash)
// more code here ...
// union type value
val b: Password | Username = if (true) name else password
3) 简洁的语法
Scala是一种 low ceremony,“简洁但仍然可读”的语言。例如,变量声明是简洁的:
val a = 1
val b = "Hello, world"
val c = List(1,2,3)
创建类型如traits, 类和枚举都很简洁:
trait Tail:
def wagTail(): Unit
def stopTail(): Unit
enum Topping:
case Cheese, Pepperoni, Sausage, Mushrooms, Onions
class Dog extends Animal, Tail, Legs, RubberyNose
case class Person(
firstName: String,
lastName: String,
age: Int
)
简洁的高阶函数:
list.filter(_ < 4)
list.map(_ * 2)
所有这些表达方式以及更多表达方式都很简洁,并且仍然非常易读:我们称之为 富有表现力。
4) 隐式,简化
Scala 2 中的隐式是一个主要明显的设计特征。 它们代表了抽象上下文的基本方式,具有服务于各种用例的统一范式,其中包括:
- 实现 type classes
- 建立背景
- 依赖注入
- 表达能力
从那以后,其他语言也采用了类似的概念,所有这些都是 术语推断 核心思想的变体:给定一个类型,编译器合成一个具有该类型的“规范”术语。
虽然隐式是 Scala 2 中的一个定义特性,但它们的设计在 Scala 3 中得到了极大的改进:
- 定义“given”值的方法只有一种
- 只有一种方法可以引入隐式参数和参数
- 有一种单独的方式来导入 givens,不允许它们隐藏在正常导入的海洋中
- 只有一种定义隐式转换的方法,它被清楚地标记为这样,并且不需要特殊的语法
这些变化的好处包括:
- 新设计避免了特性交叉,使语言更加一致
- 它使隐式更容易学习和不容易滥用
- 它极大地提高了 95% 使用隐式的 Scala 程序的清晰度
- 它有可能以一种易于理解和友好的原则方式进行术语推断
这些功能在其他部分有详细描述,因此请参阅 [上下文抽象介绍][context] 和 given
和 using
子句 部分了解更多详细信息。
5) 与 Java 无缝集成
Scala/Java 交互在许多方面都是无缝的。 例如:
- 您可以使用 Scala 项目中可用的所有数千个 Java 库
- Scala
String
本质上是 JavaString
,添加了附加功能 - Scala 无缝使用 Java 中 java.time._ 包中的日期/时间类
您还可以在 Scala 中使用 Java 集合类,并为它们提供更多功能,Scala 包含方法,因此您可以将它们转换为 Scala 集合。
虽然几乎所有交互都是无缝的,但“与 Java 交互”一章 演示了如何更好地结合使用某些功能,包括如何使用:
- Scala 中的 Java 集合
- Scala 中的 Java
Optional
- Scala 中的 Java 接口
- Java 中的 Scala 集合
- Java 中的 Scala
Option
- Java 中的 Scala traits
- 在 Java 代码中引发异常的 Scala 方法
- Java 中的 Scala 可变参数
有关这些功能的更多详细信息,请参见该章。
6) 客户 &服务器
Scala 可以通过非常棒的框架在服务器端使用:
- Play Framework 可让您构建高度可扩展的服务器端应用程序和微服务
- Akka Actors 让你使用actor模型大大简化分布式和并发软件应用程序
Scala 也可以通过 Scala.js 项目 在浏览器中使用,它是 JavaScript 的类型安全替代品。 Scala.js 生态系统 有几十个库 让您可以在浏览器中使用 React、Angular、jQuery 和许多其他 JavaScript 和 Scala 库。
除了这些工具之外,Scala Native 项目“是一个优化的提前编译器和专为 Scala 设计的轻量级托管运行时”。它允许您使用纯 Scala 代码构建“系统”风格的二进制可执行应用程序,还允许您使用较低级别的原语。
7) 标准库方法
您将很少需要再次编写自定义的 for
循环,因为 Scala 标准库中的数十种预构建函数方法既可以节省您的时间,又有助于使代码在不同应用程序之间更加一致。
下面的例子展示了一些内置的集合方法,除此之外还有很多。
虽然这些都使用 List
类,但相同的方法适用于其他集合类,例如 Seq
、Vector
、LazyList
、Set
、Map
、Array
和 ArrayBuffer
。
这里有些例子:
List.range(1, 3) // List(1, 2)
List.range(start = 1, end = 6, step = 2) // List(1, 3, 5)
List.fill(3)("foo") // List(foo, foo, foo)
List.tabulate(3)(n => n * n) // List(0, 1, 4)
List.tabulate(4)(n => n * n) // List(0, 1, 4, 9)
val a = List(10, 20, 30, 40, 10) // List(10, 20, 30, 40, 10)
a.distinct // List(10, 20, 30, 40)
a.drop(2) // List(30, 40, 10)
a.dropRight(2) // List(10, 20, 30)
a.dropWhile(_ < 25) // List(30, 40, 10)
a.filter(_ < 25) // List(10, 20, 10)
a.filter(_ > 100) // List()
a.find(_ > 20) // Some(30)
a.head // 10
a.headOption // Some(10)
a.init // List(10, 20, 30, 40)
a.intersect(List(19,20,21)) // List(20)
a.last // 10
a.lastOption // Some(10)
a.map(_ * 2) // List(20, 40, 60, 80, 20)
a.slice(2, 4) // List(30, 40)
a.tail // List(20, 30, 40, 10)
a.take(3) // List(10, 20, 30)
a.takeRight(2) // List(40, 10)
a.takeWhile(_ < 30) // List(10, 20)
a.filter(_ < 30).map(_ * 10) // List(100, 200, 100)
val fruits = List("apple", "pear")
fruits.map(_.toUpperCase) // List(APPLE, PEAR)
fruits.flatMap(_.toUpperCase) // List(A, P, P, L, E, P, E, A, R)
val nums = List(10, 5, 8, 1, 7)
nums.sorted // List(1, 5, 7, 8, 10)
nums.sortWith(_ < _) // List(1, 5, 7, 8, 10)
nums.sortWith(_ > _) // List(10, 8, 7, 5, 1)
8) 内置最佳实践
Scala 习语以多种方式鼓励最佳实践。
对于不可变性,我们鼓励您创建不可变的 val
声明:
val a = 1 // 不可变变量
还鼓励您使用不可变集合类,例如 List
和 Map
:
val b = List(1,2,3) // List 是不可变的
val c = Map(1 -> "one") // Map 是不可变的
样例类主要用于 领域建模,它们的参数是不可变的:
case class Person(name: String)
val p = Person("Michael Scott")
p.name // Michael Scott
p.name = "Joe" // 编译器错误(重新分配给 val 名称)
如上一节所示,Scala 集合类支持高阶函数,您可以将方法(未显示)和匿名函数传递给它们:
a.dropWhile(_ < 25)
a.filter(_ < 25)
a.takeWhile(_ < 30)
a.filter(_ < 30).map(_ * 10)
nums.sortWith(_ < _)
nums.sortWith(_ > _)
match
表达式让您可以使用模式匹配,它们确实是返回值的 表达式:
val numAsString = i match {
case 1 | 3 | 5 | 7 | 9 => "odd"
case 2 | 4 | 6 | 8 | 10 => "even"
case _ => "too big"
}
val numAsString = i match
case 1 | 3 | 5 | 7 | 9 => "odd"
case 2 | 4 | 6 | 8 | 10 => "even"
case _ => "too big"
因为它们可以返回值,所以它们经常被用作方法的主体:
def isTruthy(a: Matchable) = a match {
case 0 | "" => false
case _ => true
}
def isTruthy(a: Matchable) = a match
case 0 | "" => false
case _ => true
9) 生态系统库
用于函数式编程的 Scala 库,如 Cats 和 Zio 是 FP 社区中的前沿库。 所有流行语,如高性能、类型安全、并发、异步、资源安全、可测试、函数式、模块化、二进制兼容、高效、副作用/有副作用等,都可以用于这些库。
我们可以在这里列出数百个库,但幸运的是它们都列在另一个位置:有关这些详细信息,请参阅 “Awesome Scala” 列表。
10) 强类型系统
Scala 有一个强大的类型系统,它在 Scala 3 中得到了更多的改进。 Scala 3 的目标很早就定义了,与类型系统相关的目标包括:
- 简化
- 消除不一致
- 安全
- 人体工程学
- 性能
简化 来自数十个更改和删除的特性。
例如,从 Scala 2 中重载的 implicit
关键字到 Scala 3 中的术语 given
和 using
的变化使语言更加清晰,尤其是对于初学者来说。
消除不一致 与Scala 3中的几十个删除的特性、改变的特性和[增加的特性][add]有关。 此类别中一些最重要的功能是:
- 交集类型
- 并集类型
- 隐式函数类型
- 依赖函数类型
- trait 参数
- 通用元组
安全 与几个新的和改变的特性有关:
- Multiversal equality
- Restricting implicit conversions
- Null safety
- Safe initialization
人体工程学 的好例子是枚举和扩展方法,它们以非常易读的方式添加到 Scala 3 中:
// 枚举
enum Color:
case Red, Green, Blue
// 扩展方法
extension (c: Circle)
def circumference: Double = c.radius * math.Pi * 2
def diameter: Double = c.radius * 2
def area: Double = math.Pi * c.radius * c.radius
性能 涉及几个方面。 其中之一是 不透明类型。 在 Scala 2 中,有几次尝试创建解决方案以与域驱动设计 (DDD) 实践相一致,即赋予值更有意义的类型。 这些尝试包括:
- 类型别名
- 值类
- 样例类
不幸的是,所有这些方法都有弱点,如 Opaque Types SIP 中所述。 相反,如 SIP 中所述,不透明类型的目标是“对这些包装器类型的操作不得在运行时产生任何额外开销,同时在编译时仍提供类型安全使用。”
有关更多类型系统的详细信息,请参阅 参考文档。
其他很棒的功能
Scala 有许多很棒的特性,选择十大列表可能是主观的。 多项调查表明,不同的开发人员群体喜欢不同的特性。
Contributors to this page:
Contents
- 导言
- Scala 3 特性
- 为什么是 Scala 3 ?
- Scala 的味道
- Hello, World!
- The REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 头等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 领域建模
- 函数式领域建模
- 方法
- 方法特性
- main 方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 扩展
- 高阶函数
- 自定义 map 函数
- 创建可以返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数是值
- 函数式错误处理
- 总结
- 类型和类型系统
- 类型推断
- 泛型
- 相交类型
- 联合类型
- 代数数据类型
- 型变
- 不透明类型
- 结构化类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- Given 实例和 Using 语句
- 上下文绑定
- Given 导入
- 实现类型类
- 多元相等性
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- worksheet
- 与 Java 交互
- 向 Java 开发者介绍Scala
- Scala for JavaScript Developers
- Scala for Python Developers
- 下一步去哪