Skip to content

add code tab and modified translation. #2817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 94 additions & 15 deletions _zh-cn/overviews/scala3-book/ca-type-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,101 +14,180 @@ permalink: "/zh-cn/scala3/book/:title.html"
---


_类型类_是一种抽象的参数化类型,它允许您在不使用子类型的情况下向任何封闭数据类型添加新行为。
_类型类_ 是一种抽象的参数化类型,它允许您在不使用子类型的情况下向任何封闭数据类型添加新行为。
如果你从 Java 那边过来,你可以将类型类视为类似于 [`java.util.Comparator[T]`][comparator] 的东西。

> Oliveira 等人写的论文 [“Type Classes as Objects and Implicits”][typeclasses-paper] (2010) 讨论了在 Scala 中类型类背后的基本观点。
> 虽然论文用了旧的 Scala 版本,但其中的观点至今依然有用。

这在多用例中很有用,例如:

- 表达你不拥有的类型——来自标准库或第三方库——如何符合这种行为
- 为多种类型表达这种行为,而不涉及这些类型之间的子类型关系

Scala 3 中,类型类只是具有一个或多个参数的 traits,其实现由 `given` 实例提供。
类型类只是具有一个或多个参数的 traits,其实现在 Scala 3 中,`given` 实例提供,在 Scala 2 中用 `implicit` 值

## 例子

例如,`Show` 是 Haskell 中众所周知的类型类,下面的代码显示了在 Scala 3 中实现它的一种方法。
例如,`Show` 是 Haskell 中众所周知的类型类,下面的代码显示了在 Scala 中实现它的一种方法。
如果您认为 Scala 类没有 `toString` 方法,您可以定义一个 `Show` 类型类,然后把此行为添加到任意的类,这个类是能够转换为自定义字符串。

### 类型类

创建类型类的第一步是声明具有一个或多个抽象方法的参数化 trait。
因为 `Showable` 只有一个名为 `show` 的方法,所以写成这样:

{% tabs 'definition' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
// a type class
trait Showable[A] {
def show(a: A): String
}
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
// a type class
trait Showable[A]:
extension(a: A) def show: String
```
{% endtab %}
{% endtabs %}

这是 Scala 3 的说法,任何实现此 trait 的类型都必须定义 `show` 方法的工作方式。
请注意,语法非常接近普通的 trait:
请注意,通常当你要定义 `Show` trait时,下面这样的办法接近普通的面向对象的办法:

{% tabs 'trait' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
// a trait
trait Show {
def show: String
}
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
// a trait
trait Show:
def show: String
```
{% endtab %}
{% endtabs %}

有几件重要的事情需要指出:

1. 像 `Showable` 这样的类型类有一个类型参数 `A` 来说明我们为哪种类型提供了 `show` 的实现;相反,像 `Show` 这样的正常特征不会
2. 要将 show 功能添加到特定类型 `A`,正常 trait 需要 `A extends Show`,而对于类型类,我们需要实现 `Showable[A]`。
3. 为了在两个 `Showable` 中允许相同的方法调用语法来模仿 `Show`,我们将 `Showable.show` 定义为扩展方法。
1. 像 `Showable` 这样的类型类有一个类型参数 `A` 来说明我们为哪种类型提供了 `show` 的实现;相反,像 `Show` 这样的传统的 trait 不会
2. 要将 show 功能添加到特定类型 `A`,传统的 trait 需要 `A extends Show`,而对于类型类,我们需要实现 `Showable[A]`。
3. 在 Scala 3 中,为了在两个 `Showable` 中允许相同的方法调用语法来模仿 `Show`,我们将 `Showable.show` 定义为扩展方法。

### 实现具体实例

下一步是确定在应用程序中,`Showable` 适用于哪些类,然后为它们实现该行为。
例如,为这个 `Person` 类实现 `Showable`:

{% tabs 'person' %}
{% tab 'Scala 2 and 3' %}
```scala
case class Person(firstName: String, lastName: String)
```
{% endtab %}
{% endtabs %}

你将为 `Showable[Person]` 定义一个 `given` 值。
这段代码为 `Person` 类提供了一个 `Showable` 的具体实例:
你将为 `Showable[Person]` 定义一个 _规范值_ ,例如下面的代码为 `Person` 类提供了一个 `Showable` 的实例:

{% tabs 'instance' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
implicit val showablePerson: Showable[Person] = new Showable[Person] {
def show(p: Person): String =
s"${p.firstName} ${p.lastName}"
}
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
given Showable[Person] with
extension(p: Person) def show: String =
s"${p.firstName} ${p.lastName}"
```

如图所示,这被定义为 `Person` 类的扩展方法,它使用 `show` 方法主体内的引用 `p`。
{% endtab %}
{% endtabs %}

### 使用类型类

现在你可以像这样使用这个类型类:

{% tabs 'usage' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
val person = Person("John", "Doe")
println(showablePerson.show(person))
```

注意,在实践中,类型类一般与类型未知的值一起使用,而不像下面章节展示的 `Person` 类。
{% endtab %}
{% tab 'Scala 3' %}
```scala
val person = Person("John", "Doe")
println(person.show)
```
{% endtab %}
{% endtabs %}

同样,如果 Scala 没有可用于每个类的 `toString` 方法,您可以使用此技术将 `Showable` 行为添加到您希望能够转换为 `String` 的任何类。

### 编写使用类型类的方法

与继承一样,您可以定义使用 `Showable` 作为类型参数的方法:

{% tabs 'method' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
def showAll[A](as: List[A])(implicit showable: Showable[A]): Unit =
as.foreach(a => println(showable.show(a)))

showAll(List(Person("Jane"), Person("Mary")))
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
def showAll[S: Showable](xs: List[S]): Unit =
xs.foreach(x => println(x.show))
def showAll[A: Showable](as: List[A]): Unit =
as.foreach(x => println(a.show))

showAll(List(Person("Jane"), Person("Mary")))
```
{% endtab %}
{% endtabs %}

### 具有多种方法的类型类

请注意,如果要创建具有多个方法的类型类,则初始语法如下所示:

{% tabs 'multiple-methods' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
trait HasLegs[A] {
def walk(a: A): Unit
def run(a: A): Unit
}
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
trait HasLegs[A]:
extension (a: A)
def walk(): Unit
def run(): Unit
```
{% endtab %}
{% endtabs %}

### 一个真实的例子

有关如何在 Scala 3 中使用类型类的真实示例,请参阅[多元相等性部分][multiversal]中的 `CanEqual` 讨论。


[typeclasses-paper]: https://infoscience.epfl.ch/record/150280/files/TypeClasses.pdf
[typeclasses-chapter]: {% link _overviews/scala3-book/ca-type-classes.md %}
[comparator]: https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html
[multiversal]: {% link _zh-cn/overviews/scala3-book/ca-multiversal-equality.md %}