Skip to content

Commit 9702227

Browse files
committed
Add tabs to Type Classes page
1 parent f23fa7d commit 9702227

File tree

1 file changed

+85
-14
lines changed

1 file changed

+85
-14
lines changed

_overviews/scala3-book/ca-type-classes.md

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
---
22
title: Type Classes
33
type: section
4-
description: This page demonstrates how to create and use type classes in Scala 3.
4+
description: This page demonstrates how to create and use type classes.
55
languages: [zh-cn]
66
num: 63
77
previous-page: ca-given-imports
88
next-page: ca-multiversal-equality
99
redirect_from: /scala3/book/types-type-classes.html
1010
---
1111

12-
1312
A _type class_ is an abstract, parameterized type that lets you add new behavior to any closed data type without using sub-typing.
1413
If you are coming from Java, you can think of type classes as something like [`java.util.Comparator[T]`][comparator].
1514

@@ -21,99 +20,171 @@ A type class is useful in multiple use-cases, for example:
2120
- Expressing how a type you don’t own---from the standard library or a third-party library---conforms to such behavior
2221
- Expressing such a behavior for multiple types without involving sub-typing relationships between those types
2322

24-
In Scala 3, type classes are just traits with one or more parameters whose implementations are provided by `given` instances.
25-
26-
23+
Type classes are traits with one or more parameters whose implementations are provided as `given` instances in Scala 3 or `implicit` values in Scala 2.
2724

2825
## Example
2926

30-
For example, `Show` is a well-known type class in Haskell, and the following code shows one way to implement it in Scala 3.
31-
If you imagine that Scala classes don’t have a `toString` method, you can define a `Show` type class to add this behavior to any class that you want to be able to convert to a custom string.
27+
For example, `Show` is a well-known type class in Haskell, and the following code shows one way to implement it in Scala.
28+
If you imagine that Scala classes don’t have a `toString` method, you can define a `Show` type class to add this behavior to any type that you want to be able to convert to a custom string.
3229

3330
### The type class
3431

3532
The first step in creating a type class is to declare a parameterized trait that has one or more abstract methods.
3633
Because `Showable` only has one method named `show`, it’s written like this:
3734

35+
{% tabs 'definition' class=tabs-scala-version %}
36+
{% tab 'Scala 2' %}
37+
```scala
38+
// a type class
39+
trait Showable[A] {
40+
def show(a: A): String
41+
}
42+
```
43+
{% endtab %}
44+
{% tab 'Scala 3' %}
3845
```scala
3946
// a type class
4047
trait Showable[A]:
4148
extension(a: A) def show: String
4249
```
50+
{% endtab %}
51+
{% endtabs %}
4352

4453
This is the Scala 3 way of saying that any type that implements this trait must define how the `show` method works.
4554
Notice that the syntax is very close to a normal trait:
4655

56+
{% tabs 'trait' class=tabs-scala-version %}
57+
{% tab 'Scala 2' %}
58+
```scala
59+
// a trait
60+
trait Show {
61+
def show: String
62+
}
63+
```
64+
{% endtab %}
65+
{% tab 'Scala 3' %}
4766
```scala
4867
// a trait
4968
trait Show:
5069
def show: String
5170
```
71+
{% endtab %}
72+
{% endtabs %}
5273

5374
There are a few important things to point out:
5475

5576
1. Type-classes like `Showable` take a type parameter `A` to say which type we provide the implementation of `show` for; in contrast, normal traits like `Show` do not.
5677
2. To add the show functionality to a certain type `A`, the normal trait requires that `A extends Show`, while for type-classes we require to have an implementation of `Showable[A]`.
57-
3. To allow the same method calling syntax in both `Showable` that mimics the one of `Show`, we define `Showable.show` as an extension method.
78+
3. In Scala 3, to allow the same method calling syntax in both `Showable` that mimics the one of `Show`, we define `Showable.show` as an extension method.
5879

5980
### Implement concrete instances
6081

6182
The next step is to determine what classes in your application `Showable` should work for, and then implement that behavior for them.
6283
For instance, to implement `Showable` for this `Person` class:
6384

85+
{% tabs 'person' %}
86+
{% tab 'Scala 2 and 3' %}
6487
```scala
6588
case class Person(firstName: String, lastName: String)
6689
```
90+
{% endtab %}
91+
{% endtabs %}
92+
93+
you’ll define a `given` value for `Showable[Person]` in Scala 3, or an `implicit` value for `Showable[Person]` in Scala 2.
6794

68-
you’ll define a `given` value for `Showable[Person]`.
6995
This code provides a concrete instance of `Showable` for the `Person` class:
7096

97+
{% tabs 'instance' class=tabs-scala-version %}
98+
{% tab 'Scala 2' %}
99+
```scala
100+
implicit val showablePerson: Showable[Person] = new Showable[Person] {
101+
def show(p: Person): String =
102+
s"${p.firstName} ${p.lastName}"
103+
}
104+
```
105+
{% endtab %}
106+
{% tab 'Scala 3' %}
71107
```scala
72108
given Showable[Person] with
73109
extension(p: Person) def show: String =
74110
s"${p.firstName} ${p.lastName}"
75111
```
76-
77-
As shown, this is defined as an extension method on the `Person` class, and it uses the reference `p` inside the body of the `show` method.
112+
{% endtab %}
113+
{% endtabs %}
78114

79115
### Using the type class
80116

81117
Now you can use this type class like this:
82118

119+
{% tabs 'usage' class=tabs-scala-version %}
120+
{% tab 'Scala 2' %}
121+
```scala
122+
val person = Person("John", "Doe")
123+
println(showablePerson.show(person))
124+
```
125+
126+
Not that in practice, type classes are typically used with values whose type is unknown, unlike the type `Person`, as shown in the next section.
127+
{% endtab %}
128+
{% tab 'Scala 3' %}
83129
```scala
84130
val person = Person("John", "Doe")
85131
println(person.show)
86132
```
133+
{% endtab %}
134+
{% endtabs %}
87135

88136
Again, if Scala didn’t have a `toString` method available to every class, you could use this technique to add `Showable` behavior to any class that you want to be able to convert to a `String`.
89137

90138
### Writing methods that use the type class
91139

92140
As with inheritance, you can define methods that use `Showable` as a type parameter:
93141

142+
{% tabs 'method' class=tabs-scala-version %}
143+
{% tab 'Scala 2' %}
94144
```scala
95-
def showAll[S: Showable](xs: List[S]): Unit =
96-
xs.foreach(x => println(x.show))
145+
def showAll[A](as: List[A])(implicit showable: Showable[A]): Unit =
146+
as.foreach(a => println(showable.show(a)))
97147

98148
showAll(List(Person("Jane"), Person("Mary")))
99149
```
150+
{% endtab %}
151+
{% tab 'Scala 3' %}
152+
```scala
153+
def showAll[A: Showable](as: List[A]): Unit =
154+
as.foreach(a => println(a.show))
155+
156+
showAll(List(Person("Jane"), Person("Mary")))
157+
```
158+
{% endtab %}
159+
{% endtabs %}
100160

101161
### A type class with multiple methods
102162

103163
Note that if you want to create a type class that has multiple methods, the initial syntax looks like this:
104164

165+
{% tabs 'multiple-methods' class=tabs-scala-version %}
166+
{% tab 'Scala 2' %}
167+
```scala
168+
trait HasLegs[A] {
169+
def walk(a: A): Unit
170+
def run(a: A): Unit
171+
}
172+
```
173+
{% endtab %}
174+
{% tab 'Scala 3' %}
105175
```scala
106176
trait HasLegs[A]:
107177
extension (a: A)
108178
def walk(): Unit
109179
def run(): Unit
110180
```
181+
{% endtab %}
182+
{% endtabs %}
111183

112184
### A real-world example
113185

114186
For a real-world example of how type classes are used in Scala 3, see the `CanEqual` discussion in the [Multiversal Equality section][multiversal].
115187

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

0 commit comments

Comments
 (0)