Skip to content

Commit 8d227be

Browse files
authored
Add code tabs to Contextual Abstractions (#2787)
* Add missing redirection from https://docs.scala-lang.org/scala3/book/types-type-classes.html to https://docs.scala-lang.org/scala3/book/ca-type-classes.html * Follow the standard way of labelling a page to be Scala 3 only * Mark “Extension Methods” as Scala 3 specific, and add cross-references with “Implicit Classes”. * Add Scala 2 tabs and explanations in the Implicit Conversions page Remove the detailed explanation from the Tour page in favor of the Book. * Add code tabs to page 'Context Parameters' * Add tabs to Type Classes page * Update intro * Address feedback from review
1 parent 3d6e5b7 commit 8d227be

28 files changed

+504
-306
lines changed

_overviews/core/implicit-classes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ partof: implicit-classes
77
languages: [zh-cn]
88

99
permalink: /overviews/core/:title.html
10+
versionSpecific: true
11+
scala2: true
12+
---
13+
14+
In Scala 3, implicit classes are still supported for compatibility reasons but the recommended way to achieve the same result is to use [extension methods]({% link _overviews/scala3-book/ca-extension-methods.md %}).
15+
1016
---
1117

1218
**Josh Suereth**

_overviews/scala3-book/ca-context-bounds.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ type: section
44
description: This page demonstrates Context Bounds in Scala.
55
languages: [zh-cn]
66
num: 61
7-
previous-page: ca-given-using-clauses
7+
previous-page: ca-context-parameters
88
next-page: ca-given-imports
99
---
1010

11-
In many situations the name of a [context parameter]({% link _overviews/scala3-book/ca-given-using-clauses.md %}#using-clauses) does not have to be mentioned explicitly, since it is only used by the compiler in synthesized arguments for other context parameters.
11+
In many situations the name of a [context parameter]({% link _overviews/scala3-book/ca-context-parameters.md %}#context-parameters) does not have to be mentioned explicitly, since it is only used by the compiler in synthesized arguments for other context parameters.
1212
In that case you don’t have to define a parameter name, and can just provide the parameter type.
1313

1414

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
---
2+
title: Context Parameters
3+
type: section
4+
description: This page demonstrates how to declare context parameters, and how the compiler infers them at call-site.
5+
languages: [zh-cn]
6+
num: 60
7+
previous-page: ca-extension-methods
8+
next-page: ca-context-bounds
9+
redirect_from: /scala3/book/ca-given-using-clauses.html
10+
---
11+
12+
Scala offers two important features for contextual abstraction:
13+
14+
- **Context Parameters** allow you to specify parameters that, at the call-site, can be omitted by the programmer and should be automatically provided by the context.
15+
- **Given Instances** (in Scala 3) or **Implicit Definitions** (in Scala 2) are terms that can be used by the Scala compiler to fill in the missing arguments.
16+
17+
## Context Parameters
18+
19+
When designing a system, often context information like _configuration_ or settings need to be provided to the different components of your system.
20+
One common way to achieve this is by passing the configuration as additional argument to your methods.
21+
22+
In the following example, we define a case class `Config` to model some website configuration and pass it around in the different methods.
23+
24+
{% tabs example %}
25+
{% tab 'Scala 2 and 3' %}
26+
```scala
27+
case class Config(port: Int, baseUrl: String)
28+
29+
def renderWebsite(path: String, config: Config): String =
30+
"<html>" + renderWidget(List("cart"), config) + "</html>"
31+
32+
def renderWidget(items: List[String], config: Config): String = ???
33+
34+
val config = Config(8080, "docs.scala-lang.org")
35+
renderWebsite("/home", config)
36+
```
37+
{% endtab %}
38+
{% endtabs %}
39+
40+
Let us assume that the configuration does not change throughout most of our code base.
41+
Passing `config` to each and every method call (like `renderWidget`) becomes very tedious and makes our program more difficult to read, since we need to ignore the `config` argument.
42+
43+
### Marking parameters as contextual
44+
45+
We can mark some parameters of our methods as _contextual_.
46+
47+
{% tabs 'contextual-parameters' class=tabs-scala-version %}
48+
{% tab 'Scala 2' %}
49+
```scala
50+
def renderWebsite(path: String)(implicit config: Config): String =
51+
"<html>" + renderWidget(List("cart")) + "</html>"
52+
// ^
53+
// no argument config required anymore
54+
55+
def renderWidget(items: List[String])(implicit config: Config): String = ???
56+
```
57+
{% endtab %}
58+
{% tab 'Scala 3' %}
59+
```scala
60+
def renderWebsite(path: String)(using config: Config): String =
61+
"<html>" + renderWidget(List("cart")) + "</html>"
62+
// ^
63+
// no argument config required anymore
64+
65+
def renderWidget(items: List[String])(using config: Config): String = ???
66+
```
67+
{% endtab %}
68+
{% endtabs %}
69+
70+
By starting a parameter section with the keyword `using` in Scala 3 or `implicit` in Scala 2, we tell the compiler that at the call-site it should automatically find an argument with the correct type.
71+
The Scala compiler thus performs **term inference**.
72+
73+
In our call to `renderWidget(List("cart"))` the Scala compiler will see that there is a term of type `Config` in scope (the `config`) and automatically provide it to `renderWidget`.
74+
So the program is equivalent to the one above.
75+
76+
In fact, since we do not need to refer to `config` in our implementation of `renderWebsite` anymore, we can even omit its name in the signature in Scala 3:
77+
78+
{% tabs 'anonymous' %}
79+
{% tab 'Scala 3 Only' %}
80+
```scala
81+
// no need to come up with a parameter name
82+
// vvvvvvvvvvvvv
83+
def renderWebsite(path: String)(using Config): String =
84+
"<html>" + renderWidget(List("cart")) + "</html>"
85+
```
86+
{% endtab %}
87+
{% endtabs %}
88+
89+
In Scala 2, the name of implicit parameters is still mandatory.
90+
91+
### Explicitly providing contextual arguments
92+
93+
We have seen how to _abstract_ over contextual parameters and that the Scala compiler can provide arguments automatically for us.
94+
But how can we specify which configuration to use for our call to `renderWebsite`?
95+
96+
{% tabs 'explicit' class=tabs-scala-version %}
97+
{% tab 'Scala 2' %}
98+
We explicitly supply the argument value as if it was a regular argument:
99+
```scala
100+
renderWebsite("/home")(config)
101+
```
102+
{% endtab %}
103+
{% tab 'Scala 3' %}
104+
Like we specified our parameter section with `using`, we can also explicitly provide contextual arguments with `using`:
105+
```scala
106+
renderWebsite("/home")(using config)
107+
```
108+
{% endtab %}
109+
{% endtabs %}
110+
111+
Explicitly providing contextual parameters can be useful if we have multiple different values in scope that would make sense, and we want to make sure that the correct one is passed to the function.
112+
113+
For all other cases, as we will see in the next section, there is also another way to bring contextual values into scope.
114+
115+
## Given Instances (Implicit Definitions in Scala 2)
116+
117+
We have seen that we can explicitly pass arguments as contextual parameters.
118+
However, if there is _a single canonical value_ for a particular type, there is another preferred way to make it available to the Scala compiler: by marking it as `given` in Scala 3 or `implicit` in Scala 2.
119+
120+
{% tabs 'instances' class=tabs-scala-version %}
121+
{% tab 'Scala 2' %}
122+
```scala
123+
implicit val config: Config = Config(8080, "docs.scala-lang.org")
124+
// ^^^^^^
125+
// this is the value the Scala compiler will infer
126+
// as argument to contextual parameters of type Config
127+
```
128+
{% endtab %}
129+
{% tab 'Scala 3' %}
130+
```scala
131+
val config = Config(8080, "docs.scala-lang.org")
132+
133+
// this is the type that we want to provide the
134+
// canonical value for
135+
// vvvvvv
136+
given Config = config
137+
// ^^^^^^
138+
// this is the value the Scala compiler will infer
139+
// as argument to contextual parameters of type Config
140+
```
141+
{% endtab %}
142+
{% endtabs %}
143+
144+
In the above example we specify that whenever a contextual parameter of type `Config` is omitted in the current scope, the compiler should infer `config` as an argument.
145+
146+
Having defined a canonical value for the type `Config`, we can call `renderWebsite` as follows:
147+
148+
```scala
149+
renderWebsite("/home")
150+
// ^
151+
// again no argument
152+
```
153+
154+
A detailed guide to where Scala looks for canonical values can be found in [the FAQ]({% link _overviews/FAQ/index.md %}#where-does-scala-look-for-implicits).
155+
156+
[reference]: {{ site.scala3ref }}/overview.html
157+
[blog-post]: /2020/11/06/explicit-term-inference-in-scala-3.html

_overviews/scala3-book/ca-contextual-abstractions-intro.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ next-page: ca-extension-methods
1111

1212
## Background
1313

14-
Implicits in Scala 2 were a major distinguishing design feature.
15-
They are *the* fundamental way to abstract over context.
14+
Contextual abstractions are a way to abstract over context.
1615
They represent a unified paradigm with a great variety of use cases, among them:
1716

1817
- Implementing type classes
@@ -21,36 +20,36 @@ They represent a unified paradigm with a great variety of use cases, among them:
2120
- Expressing capabilities
2221
- Computing new types, and proving relationships between them
2322

24-
Since then, other languages have followed suit, e.g., Rust’s traits or Swift’s protocol extensions.
23+
Other languages have been influenced by Scala in this regard. E.g., Rust’s traits or Swift’s protocol extensions.
2524
Design proposals are also on the table for Kotlin as compile time dependency resolution, for C# as Shapes and Extensions or for F# as Traits.
26-
Implicits are also a common feature of theorem provers such as Coq or Agda.
25+
Contextual abstractions are also a common feature of theorem provers such as Coq or Agda.
2726

28-
Even though these designs use different terminology, they’re all variants of the core idea of *term inference*:
29-
Given a type, the compiler synthesizes a “canonical” term that has that type.
27+
Even though these designs use different terminology, they’re all variants of the core idea of **term inference**: given a type, the compiler synthesizes a “canonical” term that has that type.
3028

29+
## Scala 3 Redesign
3130

32-
## Redesign
31+
In Scala 2, contextual abstractions are supported by marking definitions (methods and values) or parameters as `implicit` (see [Context Parameters]({% link _overviews/scala3-book/ca-context-parameters.md %})).
3332

34-
Scala 3 includes a redesign of contextual abstractions in Scala.
33+
Scala 3 includes a redesign of contextual abstractions.
3534
While these concepts were gradually “discovered” in Scala 2, they’re now well known and understood, and the redesign takes advantage of that knowledge.
3635

3736
The design of Scala 3 focuses on **intent** rather than **mechanism**.
3837
Instead of offering one very powerful feature of implicits, Scala 3 offers several use-case oriented features:
3938

4039
- **Retroactively extending classes**.
41-
In Scala 2, extension methods had to be encoded using implicit conversions or implicit classes.
40+
In Scala 2, extension methods are encoded by using [implicit conversions][implicit-conversions] or [implicit classes]({% link _overviews/core/implicit-classes.md %}).
4241
In contrast, in Scala 3 [extension methods][extension-methods] are now directly built into the language, leading to better error messages and improved type inference.
4342

4443
- **Abstracting over contextual information**.
4544
[Using clauses][givens] allow programmers to abstract over information that is available in the calling context and should be passed implicitly.
4645
As an improvement over Scala 2 implicits, using clauses can be specified by type, freeing function signatures from term variable names that are never explicitly referred to.
4746

4847
- **Providing Type-class instances**.
49-
[Given instances][type-classes] allow programmers to define the _canonical value_ of a certain type.
50-
This makes programming with type-classes more straightforward without leaking implementation details.
48+
[Given instances][givens] allow programmers to define the _canonical value_ of a certain type.
49+
This makes programming with [type-classes][type-classes] more straightforward without leaking implementation details.
5150

5251
- **Viewing one type as another**.
53-
Implicit conversion have been [redesigned][implicit-conversions] from the ground up as instances of a type-class `Conversion`.
52+
Implicit conversions have been [redesigned][implicit-conversions] from the ground up as instances of a type-class `Conversion`.
5453

5554
- **Higher-order contextual abstractions**.
5655
The _all-new_ feature of [context functions][contextual-functions] makes contextual abstractions a first-class citizen.
@@ -78,11 +77,11 @@ Benefits of these changes include:
7877

7978
This chapter introduces many of these new features in the following sections.
8079

81-
[givens]: {% link _overviews/scala3-book/ca-given-using-clauses.md %}
80+
[givens]: {% link _overviews/scala3-book/ca-context-parameters.md %}
8281
[given-imports]: {% link _overviews/scala3-book/ca-given-imports.md %}
8382
[implicit-conversions]: {% link _overviews/scala3-book/ca-implicit-conversions.md %}
8483
[extension-methods]: {% link _overviews/scala3-book/ca-extension-methods.md %}
8584
[context-bounds]: {% link _overviews/scala3-book/ca-context-bounds.md %}
8685
[type-classes]: {% link _overviews/scala3-book/ca-type-classes.md %}
8786
[equality]: {% link _overviews/scala3-book/ca-multiversal-equality.md %}
88-
[contextual-functions]: {% link _overviews/scala3-book/types-dependent-function.md %}
87+
[contextual-functions]: {{ site.scala3ref }}/contextual/context-functions.html

_overviews/scala3-book/ca-extension-methods.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ description: This page demonstrates how Extension Methods work in Scala 3.
55
languages: [zh-cn]
66
num: 59
77
previous-page: ca-contextual-abstractions-intro
8-
next-page: ca-given-using-clauses
8+
next-page: ca-context-parameters
9+
scala3: true
10+
versionSpecific: true
911
---
1012

13+
In Scala 2, a similar result could be achieved with [implicit classes]({% link _overviews/core/implicit-classes.md %}).
14+
15+
---
1116

1217
Extension methods let you add methods to a type after the type is defined, i.e., they let you add new methods to closed classes.
1318
For example, imagine that someone else has created a `Circle` class:

_overviews/scala3-book/ca-given-imports.md

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,14 @@ languages: [zh-cn]
66
num: 62
77
previous-page: ca-context-bounds
88
next-page: ca-type-classes
9+
scala3: true
10+
versionSpecific: true
911
---
10-
<span class="tag tag-inline">Scala 3 only</span>
1112

1213

1314
To make it more clear where givens in the current scope are coming from, a special form of the `import` statement is used to import `given` instances.
1415
The basic form is shown in this example:
1516

16-
{% tabs given-imports-basic-form %}
17-
18-
{% tab 'Scala 3 Only' %}
19-
2017
```scala
2118
object A:
2219
class TC
@@ -28,28 +25,15 @@ object B:
2825
import A.given // import the given instance
2926
```
3027

31-
{% endtab %}
32-
33-
{% endtabs %}
34-
3528
In this code the `import A.*` clause of object `B` imports all members of `A` *except* the `given` instance, `tc`.
3629
Conversely, the second import, `import A.given`, imports *only* that `given` instance.
3730
The two `import` clauses can also be merged into one:
3831

39-
{% tabs given-imports-merged %}
40-
41-
{% tab 'Scala 3 Only' %}
42-
4332
```scala
4433
object B:
4534
import A.{given, *}
4635
```
4736

48-
{% endtab %}
49-
50-
{% endtabs %}
51-
52-
5337
## Discussion
5438

5539
The wildcard selector `*` brings all definitions other than givens or extensions into scope, whereas a `given` selector brings all *givens*---including those resulting from extensions---into scope.

0 commit comments

Comments
 (0)