Skip to content

Add code tabs to Contextual Abstractions #2787

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 8 commits into from
May 8, 2023
Merged
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions _overviews/core/implicit-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ partof: implicit-classes
languages: [zh-cn]

permalink: /overviews/core/:title.html
versionSpecific: true
scala2: true
---

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 %}).

---

**Josh Suereth**
Expand Down
4 changes: 2 additions & 2 deletions _overviews/scala3-book/ca-context-bounds.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ type: section
description: This page demonstrates Context Bounds in Scala.
languages: [zh-cn]
num: 61
previous-page: ca-given-using-clauses
previous-page: ca-context-parameters
next-page: ca-given-imports
---

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.
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.
In that case you don’t have to define a parameter name, and can just provide the parameter type.


Expand Down
157 changes: 157 additions & 0 deletions _overviews/scala3-book/ca-context-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
title: Context Parameters
type: section
description: This page demonstrates how to declare context parameters, and how the compiler infers them at call-site.
languages: [zh-cn]
num: 60
previous-page: ca-extension-methods
next-page: ca-context-bounds
redirect_from: /scala3/book/ca-given-using-clauses.html
---

Scala offers two important features for contextual abstraction:

- **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.
- **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.

## Context Parameters

When designing a system, often context information like _configuration_ or settings need to be provided to the different components of your system.
One common way to achieve this is by passing the configuration as additional argument to your methods.

In the following example, we define a case class `Config` to model some website configuration and pass it around in the different methods.

{% tabs example %}
{% tab 'Scala 2 and 3' %}
```scala
case class Config(port: Int, baseUrl: String)

def renderWebsite(path: String, config: Config): String =
"<html>" + renderWidget(List("cart"), config) + "</html>"

def renderWidget(items: List[String], config: Config): String = ???

val config = Config(8080, "docs.scala-lang.org")
renderWebsite("/home", config)
```
{% endtab %}
{% endtabs %}

Let us assume that the configuration does not change throughout most of our code base.
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.

### Marking parameters as contextual

We can mark some parameters of our methods as _contextual_.

{% tabs 'contextual-parameters' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
def renderWebsite(path: String)(implicit config: Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
// ^
// no argument config required anymore

def renderWidget(items: List[String])(implicit config: Config): String = ???
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
def renderWebsite(path: String)(using config: Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
// ^
// no argument config required anymore

def renderWidget(items: List[String])(using config: Config): String = ???
```
{% endtab %}
{% endtabs %}

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.
The Scala compiler thus performs **term inference**.

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`.
So the program is equivalent to the one above.

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:

{% tabs 'anonymous' %}
{% tab 'Scala 3 Only' %}
```scala
// no need to come up with a parameter name
// vvvvvvvvvvvvv
def renderWebsite(path: String)(using Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
```
{% endtab %}
{% endtabs %}

In Scala 2, the name of implicit parameters is still mandatory.

### Explicitly providing contextual arguments

We have seen how to _abstract_ over contextual parameters and that the Scala compiler can provide arguments automatically for us.
But how can we specify which configuration to use for our call to `renderWebsite`?

{% tabs 'explicit' class=tabs-scala-version %}
{% tab 'Scala 2' %}
We explicitly supply the argument value as if it was a regular argument:
```scala
renderWebsite("/home")(config)
```
{% endtab %}
{% tab 'Scala 3' %}
Like we specified our parameter section with `using`, we can also explicitly provide contextual arguments with `using`:
```scala
renderWebsite("/home")(using config)
```
{% endtab %}
{% endtabs %}

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.

For all other cases, as we will see in the next section, there is also another way to bring contextual values into scope.

## Given Instances (Implicit Definitions in Scala 2)

We have seen that we can explicitly pass arguments as contextual parameters.
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.

{% tabs 'instances' class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
implicit val config: Config = Config(8080, "docs.scala-lang.org")
// ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
val config = Config(8080, "docs.scala-lang.org")

// this is the type that we want to provide the
// canonical value for
// vvvvvv
given Config = config
// ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config
```
{% endtab %}
{% endtabs %}

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.

Having defined a canonical value for the type `Config`, we can call `renderWebsite` as follows:

```scala
renderWebsite("/home")
// ^
// again no argument
```

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).

[reference]: {{ site.scala3ref }}/overview.html
[blog-post]: /2020/11/06/explicit-term-inference-in-scala-3.html
27 changes: 13 additions & 14 deletions _overviews/scala3-book/ca-contextual-abstractions-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ next-page: ca-extension-methods

## Background

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

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

Since then, other languages have followed suit, e.g., Rust’s traits or Swift’s protocol extensions.
Other languages have been influenced by Scala in this regard. E.g., Rust’s traits or Swift’s protocol extensions.
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.
Implicits are also a common feature of theorem provers such as Coq or Agda.
Contextual abstractions are also a common feature of theorem provers such as Coq or Agda.

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.
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.

## Scala 3 Redesign

## Redesign
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 %})).

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

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

- **Retroactively extending classes**.
In Scala 2, extension methods had to be encoded using implicit conversions or implicit classes.
In Scala 2, extension methods are encoded by using [implicit conversions][implicit-conversions] or [implicit classes]({% link _overviews/core/implicit-classes.md %}).
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.

- **Abstracting over contextual information**.
[Using clauses][givens] allow programmers to abstract over information that is available in the calling context and should be passed implicitly.
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.

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

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

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

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

[givens]: {% link _overviews/scala3-book/ca-given-using-clauses.md %}
[givens]: {% link _overviews/scala3-book/ca-context-parameters.md %}
[given-imports]: {% link _overviews/scala3-book/ca-given-imports.md %}
[implicit-conversions]: {% link _overviews/scala3-book/ca-implicit-conversions.md %}
[extension-methods]: {% link _overviews/scala3-book/ca-extension-methods.md %}
[context-bounds]: {% link _overviews/scala3-book/ca-context-bounds.md %}
[type-classes]: {% link _overviews/scala3-book/ca-type-classes.md %}
[equality]: {% link _overviews/scala3-book/ca-multiversal-equality.md %}
[contextual-functions]: {% link _overviews/scala3-book/types-dependent-function.md %}
[contextual-functions]: {{ site.scala3ref }}/contextual/context-functions.html
7 changes: 6 additions & 1 deletion _overviews/scala3-book/ca-extension-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ description: This page demonstrates how Extension Methods work in Scala 3.
languages: [zh-cn]
num: 59
previous-page: ca-contextual-abstractions-intro
next-page: ca-given-using-clauses
next-page: ca-context-parameters
scala3: true
versionSpecific: true
---

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

---

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.
For example, imagine that someone else has created a `Circle` class:
Expand Down
20 changes: 2 additions & 18 deletions _overviews/scala3-book/ca-given-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@ languages: [zh-cn]
num: 62
previous-page: ca-context-bounds
next-page: ca-type-classes
scala3: true
versionSpecific: true
---
<span class="tag tag-inline">Scala 3 only</span>


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.
The basic form is shown in this example:

{% tabs given-imports-basic-form %}

{% tab 'Scala 3 Only' %}

```scala
object A:
class TC
Expand All @@ -28,28 +25,15 @@ object B:
import A.given // import the given instance
```

{% endtab %}

{% endtabs %}

In this code the `import A.*` clause of object `B` imports all members of `A` *except* the `given` instance, `tc`.
Conversely, the second import, `import A.given`, imports *only* that `given` instance.
The two `import` clauses can also be merged into one:

{% tabs given-imports-merged %}

{% tab 'Scala 3 Only' %}

```scala
object B:
import A.{given, *}
```

{% endtab %}

{% endtabs %}


## Discussion

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.
Expand Down
Loading