From 9cfb9e76c70f088f97e1d629dea649e3878f87a7 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 25 Apr 2023 12:00:08 +0200 Subject: [PATCH 1/8] 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 --- _overviews/scala3-book/ca-type-classes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_overviews/scala3-book/ca-type-classes.md b/_overviews/scala3-book/ca-type-classes.md index e0e81c8d50..e7f196fa0a 100644 --- a/_overviews/scala3-book/ca-type-classes.md +++ b/_overviews/scala3-book/ca-type-classes.md @@ -6,6 +6,7 @@ languages: [zh-cn] num: 63 previous-page: ca-given-imports next-page: ca-multiversal-equality +redirect_from: /scala3/book/types-type-classes.html --- From a6fd020cc9eb198ea9692e6296149d9f186746d1 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 25 Apr 2023 12:03:25 +0200 Subject: [PATCH 2/8] Follow the standard way of labelling a page to be Scala 3 only --- _overviews/scala3-book/ca-given-imports.md | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/_overviews/scala3-book/ca-given-imports.md b/_overviews/scala3-book/ca-given-imports.md index b60e21f75d..37a5a9e626 100644 --- a/_overviews/scala3-book/ca-given-imports.md +++ b/_overviews/scala3-book/ca-given-imports.md @@ -6,17 +6,14 @@ languages: [zh-cn] num: 62 previous-page: ca-context-bounds next-page: ca-type-classes +scala3: true +versionSpecific: true --- -Scala 3 only 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 @@ -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. From da3b3447ce361c7c432af8d5ca29ece02f3f239c Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 25 Apr 2023 14:10:37 +0200 Subject: [PATCH 3/8] =?UTF-8?q?Mark=20=E2=80=9CExtension=20Methods?= =?UTF-8?q?=E2=80=9D=20as=20Scala=203=20specific,=20and=20add=20cross-refe?= =?UTF-8?q?rences=20with=20=E2=80=9CImplicit=20Classes=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/core/implicit-classes.md | 6 ++++++ _overviews/scala3-book/ca-extension-methods.md | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/_overviews/core/implicit-classes.md b/_overviews/core/implicit-classes.md index d809dc6217..720ba7ea8d 100644 --- a/_overviews/core/implicit-classes.md +++ b/_overviews/core/implicit-classes.md @@ -7,6 +7,12 @@ partof: implicit-classes languages: [zh-cn] permalink: /overviews/core/:title.html +versionSpecific: true +scala2: true +--- + +In Scala 3, implicit classes have been superseded by [extension methods]({% link _overviews/scala3-book/ca-extension-methods.md %}). + --- **Josh Suereth** diff --git a/_overviews/scala3-book/ca-extension-methods.md b/_overviews/scala3-book/ca-extension-methods.md index 354bf8939e..f4ebd3f376 100644 --- a/_overviews/scala3-book/ca-extension-methods.md +++ b/_overviews/scala3-book/ca-extension-methods.md @@ -6,8 +6,13 @@ languages: [zh-cn] num: 59 previous-page: ca-contextual-abstractions-intro next-page: ca-given-using-clauses +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: From 836e98b194a75d17dd134e083a47138164e845cf Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 25 Apr 2023 17:00:37 +0200 Subject: [PATCH 4/8] Add Scala 2 tabs and explanations in the Implicit Conversions page Remove the detailed explanation from the Tour page in favor of the Book. --- .../scala3-book/ca-implicit-conversions.md | 220 ++++++++++++++++-- _tour/implicit-conversions.md | 91 +------- 2 files changed, 198 insertions(+), 113 deletions(-) diff --git a/_overviews/scala3-book/ca-implicit-conversions.md b/_overviews/scala3-book/ca-implicit-conversions.md index 6294ffd64a..ca638ed324 100644 --- a/_overviews/scala3-book/ca-implicit-conversions.md +++ b/_overviews/scala3-book/ca-implicit-conversions.md @@ -1,49 +1,223 @@ --- title: Implicit Conversions type: section -description: This page demonstrates how to implement Implicit Conversions in Scala 3. +description: This page demonstrates how to implement Implicit Conversions in Scala. languages: [zh-cn] num: 65 previous-page: ca-multiversal-equality next-page: ca-summary --- +Implicit conversions are a powerful Scala feature that allows users to supply an argument +of one type as if it were another type, to avoid boilerplate. -Implicit conversions are defined by `given` instances of the `scala.Conversion` class. -For example, not accounting for possible conversion errors, this code defines an implicit conversion from `String` to `Int`: +> Note that in Scala 2, implicit conversions were also used to provide additional members +> to closed classes (see [Implicit Classes]({% link _overviews/core/implicit-classes.md %})). +> In Scala 3, we recommend to address this use-case by defining [extension methods] instead +> of implicit conversions (although the standard library still relies on implicit conversions +> for historical reasons). + +## Example + +Consider for instance a method `findUserById` that takes a parameter of type `Long`: + +{% tabs implicit-conversions-1 %} +{% tab 'Scala 2 and 3' %} +~~~ scala +def findUserById(id: Long): Option[User] +~~~ +{% endtab %} +{% endtabs %} + +We omit the definition of the type `User` for the sake of brevity, it does not matter for +our example. + +In Scala, it is possible to call the method `findUserById` with an argument of type `Int` +instead of the expected type `Long`, because the argument will be implicitly converted +into the type `Long`: + +{% tabs implicit-conversions-2 %} +{% tab 'Scala 2 and 3' %} +~~~ scala +val id: Int = 42 +findUserById(id) // OK +~~~ +{% endtab %} +{% endtabs %} + +This code does not fail to compile with an error like “type mismatch: expected `Long`, +found `Int`” because there is an implicit conversion that converts the argument `id` +to a value of type `Long`. + +## Detailed Explanation + +This section describes how to define and use implicit conversions. + +### Defining an Implicit Conversion + +{% tabs implicit-conversions-3 class=tabs-scala-version %} + +{% tab 'Scala 2' %} +In Scala 2, an implicit conversion from type `S` to type `T` is defined by an +[implicit class]({% link _overviews/core/implicit-classes.md %}) `T` that takes +a single constructor parameter of type `S`, an +[implicit value]({% link _overviews/scala3-book/ca-given-using-clauses.md %}) of +function type `S => T`, or by an implicit method convertible to a value of that type. + +For example, the following code defines an implicit conversion from `Int` to `Long`: + +~~~ scala +import scala.language.implicitConversions + +implicit def int2long(x: Int): Long = x.toLong +~~~ + +This is an implicit method convertible to a value of type `Int => Long`. + +See the section “Beware the Power of Implicit Conversions” below for an +explanation of the clause `import scala.language.implicitConversions` +at the beginning. +{% endtab %} + +{% tab 'Scala 3' %} +In Scala 3, an implicit conversion from type `S` to type `T` is defined by a +[`given` instance]({% link _overviews/scala3-book/ca-given-using-clauses.md %}) +of type `scala.Conversion[S, T]`. For compatibility with Scala 2, it can also +be defined by an implicit method (read more in the Scala 2 tab). + +For example, this code defines an implicit conversion from `Int` to `Long`: ```scala -given Conversion[String, Int] with - def apply(s: String): Int = Integer.parseInt(s) +given int2long: Conversion[Int, Long] with + def apply(x: Int): Long = x.toLong ``` -Using an alias this can be expressed more concisely as: +Like other given definitions, implicit conversions can be anonymous: + +~~~ scala +given Conversion[Int, Long] with + def apply(x: Int): Long = x.toLong +~~~ + +Using an alias, this can be expressed more concisely as: ```scala -given Conversion[String, Int] = Integer.parseInt(_) +given Conversion[Int, Long] = (x: Int) => x.toLong ``` +{% endtab %} -Using either of those conversions, you can now use a `String` in places where an `Int` is expected: +{% endtabs %} -```scala +### Using an Implicit Conversion + +Implicit conversions are applied in two situations: + +1. If an expression `e` is of type `S`, and `S` does not conform to the expression's expected type `T`. +2. In a selection `e.m` with `e` of type `S`, if the selector `m` does not denote a member of `S` + (to support Scala-2-style [extension methods]). + +In the first case, a conversion `c` is searched for, which is applicable to `e` and whose result type conforms to `T`. + +In our example above, when we pass the argument `id` of type `Int` to the method `findUserById`, +the implicit conversion `int2long(id)` is inserted. + +In the second case, a conversion `c` is searched for, which is applicable to `e` and whose result contains a member named `m`. + +An example is to compare two strings `"foo" < "bar"`. In this case, `String` has no member `<`, so the implicit conversion `Predef.augmentString("foo") < "bar"` is inserted. (`scala.Predef` is automatically imported into all Scala programs.) + +### How Are Implicit Conversions Brought Into Scope? + +When the compiler searches for applicable conversions: + +- first, it looks into the current lexical scope + - implicit conversions defined in the current scope or the outer scopes + - imported implicit conversions + - implicit conversions imported by a wildcard import (Scala 2 only) +- then, it looks into the [companion objects] _associated_ with the argument + type `S` or the expected type `T`. The companion objects associated with + a type `X` are: + - the companion object `X` itself + - the companion objects associated with any of `X`’s inherited types + - the companion objects associated with any type argument in `X` + - if `X` is an inner class, the outer objects in which it is embedded + +For instance, consider an implicit conversion `fromStringToUser` defined in an +object `Conversions`: + +{% tabs implicit-conversions-4 class=tabs-scala-version %} +{% tab 'Scala 2' %} +~~~ scala import scala.language.implicitConversions -// a method that expects an Int -def plus1(i: Int) = i + 1 +object Conversions { + implicit def fromStringToUser(name: String): User = (name: String) => User(name) +} +~~~ +{% endtab %} +{% tab 'Scala 3' %} +~~~ scala +object Conversions: + given fromStringToUser: Conversion[String, User] = (name: String) => User(name) +~~~ +{% endtab %} +{% endtabs %} -// pass it a String that converts to an Int -plus1("1") -``` +The following imports would equivalently bring the conversion into scope: -> Note the clause `import scala.language.implicitConversions` at the beginning, -> to enable implicit conversions in the file. +{% tabs implicit-conversions-5 class=tabs-scala-version %} +{% tab 'Scala 2' %} +~~~ scala +import Conversions.fromStringToUser +// or +import Conversions._ +~~~ +{% endtab %} +{% tab 'Scala 3' %} +~~~ scala +import Conversions.fromStringToUser +// or +import Conversions.given +// or +import Conversions.{given Conversion[String, User]} +~~~ -## Discussion +Note that in Scala 3, a wildcard import (ie `import Conversions.*`) does not import given +definitions. +{% endtab %} +{% endtabs %} -The Predef package contains “auto-boxing” conversions that map primitive number types to subclasses of `java.lang.Number`. -For instance, the conversion from `Int` to `java.lang.Integer` can be defined as follows: +In the introductory example, the conversion from `Int` to `Long` does not require an import +because it is defined in the object `Int`, which is the companion object of the type `Int`. -```scala -given int2Integer: Conversion[Int, java.lang.Integer] = - java.lang.Integer.valueOf(_) -``` +Further reading: +[Where does Scala look for implicits? (on Stackoverflow)](https://stackoverflow.com/a/5598107). + +### Beware the Power of Implicit Conversions + +{% tabs implicit-conversions-6 class=tabs-scala-version %} +{% tab 'Scala 2' %} +Because implicit conversions can have pitfalls if used indiscriminately the compiler warns when compiling the implicit conversion definition. + +To turn off the warnings take either of these actions: + +* Import `scala.language.implicitConversions` into the scope of the implicit conversion definition +* Invoke the compiler with `-language:implicitConversions` + +No warning is emitted when the conversion is applied by the compiler. +{% endtab %} +{% tab 'Scala 3' %} +Because implicit conversions can have pitfalls if used indiscriminately the compiler warns in two situations: +- when compiling a Scala 2 style implicit conversion definition. +- at the call site where a given instance of `scala.Conversion` is inserted as a conversion. + +To turn off the warnings take either of these actions: + +- Import `scala.language.implicitConversions` into the scope of: + - a Scala 2 style implicit conversion definition + - call sites where a given instance of `scala.Conversion` is inserted as a conversion. +- Invoke the compiler with `-language:implicitConversions` +{% endtab %} +{% endtabs %} + +[extension methods]: {% link _overviews/scala3-book/ca-extension-methods.md %} +[companion objects]: {% link _overviews/scala3-book/domain-modeling-tools.md %}#companion-objects diff --git a/_tour/implicit-conversions.md b/_tour/implicit-conversions.md index cf93a062f2..7c0b5840e4 100644 --- a/_tour/implicit-conversions.md +++ b/_tour/implicit-conversions.md @@ -38,95 +38,6 @@ In the second case, a conversion `c` is searched for, which is applicable to `e` An example is to compare two strings `"foo" < "bar"`. In this case, `String` has no member `<`, so the implicit conversion `Predef.augmentString("foo") < "bar"` is inserted. (`scala.Predef` is automatically imported into all Scala programs.) -### How are implicit conversions brought into scope? ### - -{% tabs implicit-conversion-scope class=tabs-scala-version %} -{% tab 'Scala 2' %} -In Scala 2, an implicit conversion is brought into scope by importing from the object that defined it, (e.g. `Conversions` in this case). If the implicit conversion is in the companion object of the argument type, (e.g. `Student` in this case), then no import is necessary. - -```scala -import scala.language.implicitConversions // required to define an implicit conversion - -case class Student(name: String) -object Student { - implicit def fromStudentToInt(student: Student): Int = student.name.length -} - -object Conversions { - implicit def fromStringToStudent(name: String): Student = Student(name) -} - -import Conversions._ -object Usage { - def main(args: Array[String]) = { - val reginald: Student = "Reginald" // applies the conversion Conversions.fromStringToStudent - println(reginald + 2) // applies the conversion Student.fromStudentToInt - } -} -``` -{% endtab %} -{% tab 'Scala 3' %} -In Scala 3, an implicit conversion is brought into scope by either importing `given` or the named conversion from the object that defined it, (e.g. `Conversions` in this case). - -Note that as of Scala 3, implicit conversions cannot be brought into scope anymore by means of a wildcard import (`*`). - -Given the example: - -```scala -case class Student(name: String): - def printName: Unit = println(name) -object Student: - given Conversion[Student, Int] = _.name.length - -object Conversions: - given fromStringToStudent: Conversion[String, Student] = Student(_) -``` - -The following imports would bring the `Conversion[String, Student]` into scope: - - `import Conversions.given` - - `import Conversions.{given Conversion[String, Student]}` - - `import Conversions.fromStringToStudent` - -If the implicit conversion is in the companion object of the argument type, (e.g. `Student` in this case), then no import is necessary. - -```scala -import Conversions.given -object Usage: - @main def run = - val reginald: Student = "Reginald" // applies the Conversion[String, Student] - println(reginald + 2) // applies the Conversion[Student, Int] -``` -{% endtab %} -{% endtabs %} - -Further reading: - - [Where does Scala look for implicits? (on StackOverflow)](https://docs.scala-lang.org/tutorials/FAQ/index.html#where-does-scala-look-for-implicits). - -### Beware the power of implicit conversions - -{% tabs implicit-conversion-warning class=tabs-scala-version %} -{% tab 'Scala 2' %} -Because implicit conversions can have pitfalls if used indiscriminately the compiler warns when compiling the implicit conversion definition. - -To turn off the warnings take either of these actions: - -* Import `scala.language.implicitConversions` into the scope of the implicit conversion definition -* Invoke the compiler with `-language:implicitConversions` - -No warning is emitted when the conversion is applied by the compiler. -{% endtab %} -{% tab 'Scala 3' %} -Because implicit conversions can have pitfalls if used indiscriminately the compiler warns in two situations: -- when compiling a Scala 2 style implicit conversion definition. -- at the call site where a given instance of `scala.Conversion` is inserted as a conversion. - -To turn off the warnings take either of these actions: - -- Import `scala.language.implicitConversions` into the scope of: - - a Scala 2 style implicit conversion definition - - call sites where a given instance of `scala.Conversion` is inserted as a conversion. -- Invoke the compiler with `-language:implicitConversions` -{% endtab %} -{% endtabs %} +Further reading: [Implicit Conversions (in the Scala book)]({% link _overviews/scala3-book/ca-implicit-conversions.md %}). [exts]: {% link _overviews/scala3-book/ca-extension-methods.md %} From f23fa7d38fbe33fea6c0d3911574ede6d2a9f120 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 3 May 2023 11:19:37 +0200 Subject: [PATCH 5/8] Add code tabs to page 'Context Parameters' --- _overviews/scala3-book/ca-context-bounds.md | 4 +- .../scala3-book/ca-context-parameters.md | 154 ++++++++++++++++++ .../ca-contextual-abstractions-intro.md | 2 +- .../scala3-book/ca-extension-methods.md | 2 +- .../scala3-book/ca-given-using-clauses.md | 110 ------------- .../scala3-book/ca-implicit-conversions.md | 4 +- .../scala3-book/methods-main-methods.md | 2 +- _overviews/scala3-book/scala-features.md | 4 +- _overviews/scala3-book/scala-for-java-devs.md | 2 +- .../scala3-book/scala-for-javascript-devs.md | 2 +- _overviews/scala3-book/why-scala-3.md | 2 +- _ru/scala3/book/methods-main-methods.md | 2 +- _ru/scala3/book/scala-features.md | 4 +- _ru/scala3/book/why-scala-3.md | 2 +- .../scala3-book/ca-context-bounds.md | 2 +- ...ng-clauses.md => ca-context-parameters.md} | 0 .../ca-contextual-abstractions-intro.md | 4 +- .../scala3-book/ca-extension-methods.md | 2 +- .../scala3-book/methods-main-methods.md | 2 +- .../overviews/scala3-book/scala-features.md | 4 +- .../scala3-book/scala-for-java-devs.md | 2 +- .../scala3-book/scala-for-javascript-devs.md | 2 +- _zh-cn/overviews/scala3-book/why-scala-3.md | 2 +- 23 files changed, 180 insertions(+), 136 deletions(-) create mode 100644 _overviews/scala3-book/ca-context-parameters.md delete mode 100644 _overviews/scala3-book/ca-given-using-clauses.md rename _zh-cn/overviews/scala3-book/{ca-given-using-clauses.md => ca-context-parameters.md} (100%) diff --git a/_overviews/scala3-book/ca-context-bounds.md b/_overviews/scala3-book/ca-context-bounds.md index 2a692ed257..fee7cbd146 100644 --- a/_overviews/scala3-book/ca-context-bounds.md +++ b/_overviews/scala3-book/ca-context-bounds.md @@ -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. diff --git a/_overviews/scala3-book/ca-context-parameters.md b/_overviews/scala3-book/ca-context-parameters.md new file mode 100644 index 0000000000..4c7efdaf42 --- /dev/null +++ b/_overviews/scala3-book/ca-context-parameters.md @@ -0,0 +1,154 @@ +--- +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 = + "" + renderWidget(List("cart"), config) + "" + +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 = + "" + renderWidget(List("cart")) + "" + // ^ + // 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 = + "" + renderWidget(List("cart")) + "" + // ^ + // 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 = + "" + renderWidget(List("cart")) + "" +``` +{% 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 +``` + +[reference]: {{ site.scala3ref }}/overview.html +[blog-post]: /2020/11/06/explicit-term-inference-in-scala-3.html diff --git a/_overviews/scala3-book/ca-contextual-abstractions-intro.md b/_overviews/scala3-book/ca-contextual-abstractions-intro.md index 4174bcf5ee..ca5bae0d20 100644 --- a/_overviews/scala3-book/ca-contextual-abstractions-intro.md +++ b/_overviews/scala3-book/ca-contextual-abstractions-intro.md @@ -78,7 +78,7 @@ 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 %} diff --git a/_overviews/scala3-book/ca-extension-methods.md b/_overviews/scala3-book/ca-extension-methods.md index f4ebd3f376..6c3bcb735c 100644 --- a/_overviews/scala3-book/ca-extension-methods.md +++ b/_overviews/scala3-book/ca-extension-methods.md @@ -5,7 +5,7 @@ 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 --- diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md deleted file mode 100644 index 16169334c5..0000000000 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Given Instances and Using Clauses -type: section -description: This page demonstrates how to use 'given' instances and 'using' clauses in Scala 3. -languages: [zh-cn] -num: 60 -previous-page: ca-extension-methods -next-page: ca-context-bounds -scala3: true -versionSpecific: true ---- - -Scala 3 offers two important feature for contextual abstraction: - -- **Using Clauses** 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** let you define terms that can be used by the Scala compiler to fill in the missing arguments. - -## Using Clauses - -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. - -```scala -case class Config(port: Int, baseUrl: String) - -def renderWebsite(path: String, c: Config): String = - "" + renderWidget(List("cart"), c) + "" - -def renderWidget(items: List[String], c: Config): String = ??? - -val config = Config(8080, "docs.scala-lang.org") -renderWebsite("/home", config) -``` - -Let us assume that the configuration does not change throughout most of our code base. -Passing `c` 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 `c` argument. - -#### Using `using` to mark parameters as contextual - -In Scala 3, we can mark some parameters of our methods as _contextual_. - -```scala -def renderWebsite(path: String)(using c: Config): String = - "" + renderWidget(List("cart")) + "" - // ^^^ - // no argument c required anymore - -def renderWidget(items: List[String])(using c: Config): String = ??? -``` - -By starting a parameter section with the keyword `using`, we tell the Scala 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 `c`) 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 `c` in our implementation of `renderWebsite` anymore, we can even omit its name in the signature: - -```scala -// no need to come up with a parameter name -// vvvvvvvvvvvvv -def renderWebsite(path: String)(using Config): String = - "" + renderWidget(List("cart")) + "" -``` - -#### 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`? - -Like we specified our parameter section with `using`, we can also explicitly provide contextual arguments with `using:` - -```scala -renderWebsite("/home")(using config) -``` - -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 - -We have seen that we can explicitly pass arguments as contextual parameters by marking the argument section of the _call_ with `using`. -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`. - -```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 -``` - -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 given for `Config`, we can simply call `renderWebsite`: - -```scala -renderWebsite("/home") -// ^^^^^ -// again no argument -``` - -[reference]: {{ site.scala3ref }}/overview.html -[blog-post]: /2020/11/06/explicit-term-inference-in-scala-3.html diff --git a/_overviews/scala3-book/ca-implicit-conversions.md b/_overviews/scala3-book/ca-implicit-conversions.md index ca638ed324..2318323b95 100644 --- a/_overviews/scala3-book/ca-implicit-conversions.md +++ b/_overviews/scala3-book/ca-implicit-conversions.md @@ -61,7 +61,7 @@ This section describes how to define and use implicit conversions. In Scala 2, an implicit conversion from type `S` to type `T` is defined by an [implicit class]({% link _overviews/core/implicit-classes.md %}) `T` that takes a single constructor parameter of type `S`, an -[implicit value]({% link _overviews/scala3-book/ca-given-using-clauses.md %}) of +[implicit value]({% link _overviews/scala3-book/ca-context-parameters.md %}) of function type `S => T`, or by an implicit method convertible to a value of that type. For example, the following code defines an implicit conversion from `Int` to `Long`: @@ -81,7 +81,7 @@ at the beginning. {% tab 'Scala 3' %} In Scala 3, an implicit conversion from type `S` to type `T` is defined by a -[`given` instance]({% link _overviews/scala3-book/ca-given-using-clauses.md %}) +[`given` instance]({% link _overviews/scala3-book/ca-context-parameters.md %}) of type `scala.Conversion[S, T]`. For compatibility with Scala 2, it can also be defined by an implicit method (read more in the Scala 2 tab). diff --git a/_overviews/scala3-book/methods-main-methods.md b/_overviews/scala3-book/methods-main-methods.md index 3415ef3496..407c124d2c 100644 --- a/_overviews/scala3-book/methods-main-methods.md +++ b/_overviews/scala3-book/methods-main-methods.md @@ -72,7 +72,7 @@ Happy 23rd Birthday, Lisa and Peter! ``` As shown, the `@main` method can have an arbitrary number of parameters. -For each parameter type there must be a [given instance](./ca-given-using-clauses.html) of the `scala.util.CommandLineParser.FromString` type class that converts an argument `String` to the required parameter type. +For each parameter type there must be a [given instance]({% link _overviews/scala3-book/ca-context-parameters.md %}) of the `scala.util.CommandLineParser.FromString` type class that converts an argument `String` to the required parameter type. Also as shown, a main method’s parameter list can end in a repeated parameter like `String*` that takes all remaining arguments given on the command line. The program implemented from an `@main` method checks that there are enough arguments on the command line to fill in all parameters, and that the argument strings can be converted to the required types. diff --git a/_overviews/scala3-book/scala-features.md b/_overviews/scala3-book/scala-features.md index 0e5b94f0db..d6f72d1873 100644 --- a/_overviews/scala3-book/scala-features.md +++ b/_overviews/scala3-book/scala-features.md @@ -244,7 +244,7 @@ In particular, the type system supports: - [Intersection types]({% link _overviews/scala3-book/types-intersection.md %}) - [Union types]({% link _overviews/scala3-book/types-union.md %}) - [Type lambdas]({{ site.scala3ref }}/new-types/type-lambdas.html) -- [`given` instances and `using` clauses]({% link _overviews/scala3-book/ca-given-using-clauses.md %}) +- [`given` instances and `using` clauses]({% link _overviews/scala3-book/ca-context-parameters.md %}) - [Extension methods]({% link _overviews/scala3-book/ca-extension-methods.md %}) - [Type classes]({% link _overviews/scala3-book/ca-type-classes.md %}) - [Multiversal equality]({% link _overviews/scala3-book/ca-multiversal-equality.md %}) @@ -572,7 +572,7 @@ As this page shows, Scala has many terrific programming language features at a h [reference]: {{ site.scala3ref }}/overview.html [multiversal]: {% link _overviews/scala3-book/ca-multiversal-equality.md %} [extension]: {% link _overviews/scala3-book/ca-extension-methods.md %} -[givens]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _overviews/scala3-book/ca-context-parameters.md %} [opaque_types]: {% link _overviews/scala3-book/types-opaque-types.md %} diff --git a/_overviews/scala3-book/scala-for-java-devs.md b/_overviews/scala3-book/scala-for-java-devs.md index 61b2d558d2..b6c707f038 100644 --- a/_overviews/scala3-book/scala-for-java-devs.md +++ b/_overviews/scala3-book/scala-for-java-devs.md @@ -1306,7 +1306,7 @@ This includes: [equality]: {% link _overviews/scala3-book/ca-multiversal-equality.md %} [error-handling]: {% link _overviews/scala3-book/fp-functional-error-handling.md %} [extension-methods]: {% link _overviews/scala3-book/ca-extension-methods.md %} -[givens]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _overviews/scala3-book/ca-context-parameters.md %} [hofs]: {% link _overviews/scala3-book/fun-hofs.md %} [imports]: {% link _overviews/scala3-book/packaging-imports.md %} [modeling-intro]: {% link _overviews/scala3-book/domain-modeling-intro.md %} diff --git a/_overviews/scala3-book/scala-for-javascript-devs.md b/_overviews/scala3-book/scala-for-javascript-devs.md index 478a0b028d..f2ecd821e4 100644 --- a/_overviews/scala3-book/scala-for-javascript-devs.md +++ b/_overviews/scala3-book/scala-for-javascript-devs.md @@ -1366,7 +1366,7 @@ There are other concepts in Scala which currently have no equivalent in JavaScri [control]: {% link _overviews/scala3-book/control-structures.md %} [extension-methods]: {% link _overviews/scala3-book/ca-extension-methods.md %} [fp-intro]: {% link _overviews/scala3-book/fp-intro.md %} -[givens]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _overviews/scala3-book/ca-context-parameters.md %} [hofs]: {% link _overviews/scala3-book/fun-hofs.md %} [intersection-types]: {% link _overviews/scala3-book/types-intersection.md %} [modeling-fp]: {% link _overviews/scala3-book/domain-modeling-fp.md %} diff --git a/_overviews/scala3-book/why-scala-3.md b/_overviews/scala3-book/why-scala-3.md index 44b14845d5..639c04691e 100644 --- a/_overviews/scala3-book/why-scala-3.md +++ b/_overviews/scala3-book/why-scala-3.md @@ -490,7 +490,7 @@ Several surveys have shown that different groups of developers love different fe Hopefully you’ll discover more great Scala features as you use the language. [java]: {% link _overviews/scala3-book/interacting-with-java.md %} -[given]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} +[given]: {% link _overviews/scala3-book/ca-context-parameters.md %} [contextual]: {% link _overviews/scala3-book/ca-contextual-abstractions-intro.md %} [reference]: {{ site.scala3ref }} [dropped]: {{ site.scala3ref }}/dropped-features diff --git a/_ru/scala3/book/methods-main-methods.md b/_ru/scala3/book/methods-main-methods.md index 3faca33023..79fc169c54 100644 --- a/_ru/scala3/book/methods-main-methods.md +++ b/_ru/scala3/book/methods-main-methods.md @@ -175,4 +175,4 @@ $ scala happyBirthday 23 Lisa Peter Happy 23rd Birthday, Lisa and Peter! ``` -[given]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} +[given]: {% link _overviews/scala3-book/ca-context-parameters.md %} diff --git a/_ru/scala3/book/scala-features.md b/_ru/scala3/book/scala-features.md index 84dedb717f..c5f6308ac9 100644 --- a/_ru/scala3/book/scala-features.md +++ b/_ru/scala3/book/scala-features.md @@ -230,7 +230,7 @@ val z = nums - [Типы пересечения]({% link _overviews/scala3-book/types-intersection.md %}) - [Типы объединения]({% link _overviews/scala3-book/types-union.md %}) - [Лямбда-типы]({{ site.scala3ref }}/new-types/type-lambdas.html) -- [Экземпляры `given` и предложения `using`]({% link _overviews/scala3-book/ca-given-using-clauses.md %}) +- [Экземпляры `given` и предложения `using`]({% link _overviews/scala3-book/ca-context-parameters.md %}) - [Методы расширения]({% link _overviews/scala3-book/ca-extension-methods.md %}) - [Типовые классы]({% link _overviews/scala3-book/ca-type-classes.md %}) - [Многостороннее равенство]({% link _overviews/scala3-book/ca-multiversal-equality.md %}) @@ -482,6 +482,6 @@ JSON библиотеки: [reference]: {{ site.scala3ref }}/overview.html [multiversal]: {% link _overviews/scala3-book/ca-multiversal-equality.md %} [extension]: {% link _overviews/scala3-book/ca-extension-methods.md %} -[givens]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _overviews/scala3-book/ca-context-parameters.md %} [opaque_types]: {% link _overviews/scala3-book/types-opaque-types.md %} diff --git a/_ru/scala3/book/why-scala-3.md b/_ru/scala3/book/why-scala-3.md index 02e260d668..6f2d7eb1a1 100644 --- a/_ru/scala3/book/why-scala-3.md +++ b/_ru/scala3/book/why-scala-3.md @@ -481,7 +481,7 @@ Scala обладает множеством замечательных функ Надеемся, вы откроете для себя больше замечательных возможностей Scala по мере использования языка. [java]: {% link _overviews/scala3-book/interacting-with-java.md %} -[given]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} +[given]: {% link _overviews/scala3-book/ca-context-parameters.md %} [contextual]: {% link _overviews/scala3-book/ca-contextual-abstractions-intro.md %} [reference]: {{ site.scala3ref }} [dropped]: {{ site.scala3ref }}/dropped-features diff --git a/_zh-cn/overviews/scala3-book/ca-context-bounds.md b/_zh-cn/overviews/scala3-book/ca-context-bounds.md index 56d948bad0..c7b81b6d12 100644 --- a/_zh-cn/overviews/scala3-book/ca-context-bounds.md +++ b/_zh-cn/overviews/scala3-book/ca-context-bounds.md @@ -4,7 +4,7 @@ type: section description: This page demonstrates Context Bounds in Scala 3. language: zh-cn num: 61 -previous-page: ca-given-using-clauses +previous-page: ca-context-parameters next-page: ca-given-imports partof: scala3-book diff --git a/_zh-cn/overviews/scala3-book/ca-given-using-clauses.md b/_zh-cn/overviews/scala3-book/ca-context-parameters.md similarity index 100% rename from _zh-cn/overviews/scala3-book/ca-given-using-clauses.md rename to _zh-cn/overviews/scala3-book/ca-context-parameters.md diff --git a/_zh-cn/overviews/scala3-book/ca-contextual-abstractions-intro.md b/_zh-cn/overviews/scala3-book/ca-contextual-abstractions-intro.md index 80136106d6..d8f0f1a4d6 100644 --- a/_zh-cn/overviews/scala3-book/ca-contextual-abstractions-intro.md +++ b/_zh-cn/overviews/scala3-book/ca-contextual-abstractions-intro.md @@ -5,7 +5,7 @@ description: This chapter provides an introduction to the Scala 3 concept of Con language: zh-cn num: 58 previous-page: types-others -next-page: ca-given-using-clauses +next-page: ca-extension-methods partof: scala3-book overview-name: "Scala 3 — Book" @@ -78,7 +78,7 @@ Scala 3 中的这些更改实现了术语推理与语言其余部分的更好分 本章将在以下各节中介绍其中的许多新功能。 -[givens]: {% link _zh-cn/overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _zh-cn/overviews/scala3-book/ca-context-parameters.md %} [given-imports]: {% link _zh-cn/overviews/scala3-book/ca-given-imports.md %} [implicit-conversions]: {% link _zh-cn/overviews/scala3-book/ca-implicit-conversions.md %} [extension-methods]: {% link _zh-cn/overviews/scala3-book/ca-extension-methods.md %} diff --git a/_zh-cn/overviews/scala3-book/ca-extension-methods.md b/_zh-cn/overviews/scala3-book/ca-extension-methods.md index 7d780cae1f..2a8d4588ea 100644 --- a/_zh-cn/overviews/scala3-book/ca-extension-methods.md +++ b/_zh-cn/overviews/scala3-book/ca-extension-methods.md @@ -5,7 +5,7 @@ description: This page demonstrates how Extension Methods work in Scala 3. language: zh-cn num: 59 previous-page: ca-contextual-abstractions-intro -next-page: ca-given-using-clauses +next-page: ca-context-parameters partof: scala3-book overview-name: "Scala 3 — Book" diff --git a/_zh-cn/overviews/scala3-book/methods-main-methods.md b/_zh-cn/overviews/scala3-book/methods-main-methods.md index 4c1e9c57e8..f0b43aab6c 100644 --- a/_zh-cn/overviews/scala3-book/methods-main-methods.md +++ b/_zh-cn/overviews/scala3-book/methods-main-methods.md @@ -82,7 +82,7 @@ Happy 23rd Birthday, Lisa and Peter! ``` 如图所示,`@main` 方法可以有任意数量的参数。 -对于每个参数类型,必须是 `scala.util.CommandLineParser.FromString` 类型类的一个 [given实例](./ca-given-using-clauses.html),它将参数 `String` 转换为所需的参数类型。 +对于每个参数类型,必须是 `scala.util.CommandLineParser.FromString` 类型类的一个 [given实例]({% link _overviews/scala3-book/ca-context-parameters.md %}),它将参数 `String` 转换为所需的参数类型。 同样如图所示,主方法的参数列表可以以重复参数结尾,例如 `String*`,它接受命令行中给出的所有剩余参数。 从 `@main` 方法实现的程序检查命令行上是否有足够的参数来填充所有参数,以及参数字符串是否可以转换为所需的类型。 diff --git a/_zh-cn/overviews/scala3-book/scala-features.md b/_zh-cn/overviews/scala3-book/scala-features.md index 3674c522e5..e86815acda 100644 --- a/_zh-cn/overviews/scala3-book/scala-features.md +++ b/_zh-cn/overviews/scala3-book/scala-features.md @@ -236,7 +236,7 @@ Scala 的类型系统在编译时强制要求以安全与连贯的方式使用 - [交叉类型]({% link _zh-cn/overviews/scala3-book/types-intersection.md %}) - [联合类型]({% link _zh-cn/overviews/scala3-book/types-union.md %}) - [类型 Lambda]({{ site.scala3ref }}/new-types/type-lambdas.html) -- [`given` 实例与 `using` 子句]({% link _zh-cn/overviews/scala3-book/ca-given-using-clauses.md %}) +- [`given` 实例与 `using` 子句]({% link _zh-cn/overviews/scala3-book/ca-context-parameters.md %}) - [扩展方法]({% link _zh-cn/overviews/scala3-book/ca-extension-methods.md %}) - [类型类]({% link _zh-cn/overviews/scala3-book/ca-type-classes.md %}) - [多元相等]({% link _zh-cn/overviews/scala3-book/ca-multiversal-equality.md %}) @@ -533,5 +533,5 @@ JSON 库: [reference]: {{ site.scala3ref }}/overview.html [multiversal]: {% link _zh-cn/overviews/scala3-book/ca-multiversal-equality.md %} [extension]: {% link _zh-cn/overviews/scala3-book/ca-extension-methods.md %} -[givens]: {% link _zh-cn/overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _zh-cn/overviews/scala3-book/ca-context-parameters.md %} [opaque_types]: {% link _zh-cn/overviews/scala3-book/types-opaque-types.md %} diff --git a/_zh-cn/overviews/scala3-book/scala-for-java-devs.md b/_zh-cn/overviews/scala3-book/scala-for-java-devs.md index f4e20aa4c2..dc620ce53f 100644 --- a/_zh-cn/overviews/scala3-book/scala-for-java-devs.md +++ b/_zh-cn/overviews/scala3-book/scala-for-java-devs.md @@ -1257,7 +1257,7 @@ Scala 中还有其他一些概念目前在 Java 11 中是没有的。 [equality]: {% link _zh-cn/overviews/scala3-book/ca-multiversal-equality.md %} [error-handling]: {% link _zh-cn/overviews/scala3-book/fp-functional-error-handling.md %} [extension-methods]: {% link _zh-cn/overviews/scala3-book/ca-extension-methods.md %} -[givens]: {% link _zh-cn/overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _zh-cn/overviews/scala3-book/ca-context-parameters.md %} [hofs]: {% link _zh-cn/overviews/scala3-book/fun-hofs.md %} [imports]: {% link _zh-cn/overviews/scala3-book/packaging-imports.md %} [modeling-intro]: {% link _zh-cn/overviews/scala3-book/domain-modeling-intro.md %} diff --git a/_zh-cn/overviews/scala3-book/scala-for-javascript-devs.md b/_zh-cn/overviews/scala3-book/scala-for-javascript-devs.md index 4563c43af4..e7a74b8757 100644 --- a/_zh-cn/overviews/scala3-book/scala-for-javascript-devs.md +++ b/_zh-cn/overviews/scala3-book/scala-for-javascript-devs.md @@ -1325,7 +1325,7 @@ Scala 中还有其他一些概念目前在 JavaScript 中没有等效的概念 [control]: {% link _zh-cn/overviews/scala3-book/control-structures.md %} [extension-methods]: {% link _zh-cn/overviews/scala3-book/ca-extension-methods.md %} [fp-intro]: {% link _zh-cn/overviews/scala3-book/fp-intro.md %} -[givens]: {% link _zh-cn/overviews/scala3-book/ca-given-using-clauses.md %} +[givens]: {% link _zh-cn/overviews/scala3-book/ca-context-parameters.md %} [hofs]: {% link _zh-cn/overviews/scala3-book/fun-hofs.md %} [intersection-types]: {% link _zh-cn/overviews/scala3-book/types-intersection.md %} [modeling-fp]: {% link _zh-cn/overviews/scala3-book/domain-modeling-fp.md %} diff --git a/_zh-cn/overviews/scala3-book/why-scala-3.md b/_zh-cn/overviews/scala3-book/why-scala-3.md index 2259ad6c01..b07b567fe7 100644 --- a/_zh-cn/overviews/scala3-book/why-scala-3.md +++ b/_zh-cn/overviews/scala3-book/why-scala-3.md @@ -493,7 +493,7 @@ Scala 有许多很棒的特性,选择十大列表可能是主观的。 多项调查表明,不同的开发人员群体喜欢不同的特性。 [java]: {% link _zh-cn/overviews/scala3-book/interacting-with-java.md %} -[given]: {% link _zh-cn/overviews/scala3-book/ca-given-using-clauses.md %} +[given]: {% link _zh-cn/overviews/scala3-book/ca-context-parameters.md %} [contextual]: {% link _zh-cn/overviews/scala3-book/ca-contextual-abstractions-intro.md %} [reference]: {{ site.scala3ref }} [dropped]: {{ site.scala3ref }}/dropped-features From 97022279a741aefe644d68b41c1fb0fa7accdf97 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 3 May 2023 12:33:39 +0200 Subject: [PATCH 6/8] Add tabs to Type Classes page --- _overviews/scala3-book/ca-type-classes.md | 99 +++++++++++++++++++---- 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/_overviews/scala3-book/ca-type-classes.md b/_overviews/scala3-book/ca-type-classes.md index e7f196fa0a..05fe1ed1d2 100644 --- a/_overviews/scala3-book/ca-type-classes.md +++ b/_overviews/scala3-book/ca-type-classes.md @@ -1,7 +1,7 @@ --- title: Type Classes type: section -description: This page demonstrates how to create and use type classes in Scala 3. +description: This page demonstrates how to create and use type classes. languages: [zh-cn] num: 63 previous-page: ca-given-imports @@ -9,7 +9,6 @@ next-page: ca-multiversal-equality redirect_from: /scala3/book/types-type-classes.html --- - A _type class_ is an abstract, parameterized type that lets you add new behavior to any closed data type without using sub-typing. If you are coming from Java, you can think of type classes as something like [`java.util.Comparator[T]`][comparator]. @@ -21,69 +20,118 @@ A type class is useful in multiple use-cases, for example: - Expressing how a type you don’t own---from the standard library or a third-party library---conforms to such behavior - Expressing such a behavior for multiple types without involving sub-typing relationships between those types -In Scala 3, type classes are just traits with one or more parameters whose implementations are provided by `given` instances. - - +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. ## Example -For example, `Show` is a well-known type class in Haskell, and the following code shows one way to implement it in Scala 3. -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. +For example, `Show` is a well-known type class in Haskell, and the following code shows one way to implement it in Scala. +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. ### The type class The first step in creating a type class is to declare a parameterized trait that has one or more abstract methods. Because `Showable` only has one method named `show`, it’s written like this: +{% 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 %} This is the Scala 3 way of saying that any type that implements this trait must define how the `show` method works. Notice that the syntax is very close to a normal 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 %} There are a few important things to point out: 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. 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]`. -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. +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. ### Implement concrete instances The next step is to determine what classes in your application `Showable` should work for, and then implement that behavior for them. For instance, to implement `Showable` for this `Person` class: +{% tabs 'person' %} +{% tab 'Scala 2 and 3' %} ```scala case class Person(firstName: String, lastName: String) ``` +{% endtab %} +{% endtabs %} + +you’ll define a `given` value for `Showable[Person]` in Scala 3, or an `implicit` value for `Showable[Person]` in Scala 2. -you’ll define a `given` value for `Showable[Person]`. This code provides a concrete instance of `Showable` for the `Person` class: +{% 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}" ``` - -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. +{% endtab %} +{% endtabs %} ### Using the type class Now you can use this type class like this: +{% tabs 'usage' class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +val person = Person("John", "Doe") +println(showablePerson.show(person)) +``` + +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. +{% endtab %} +{% tab 'Scala 3' %} ```scala val person = Person("John", "Doe") println(person.show) ``` +{% endtab %} +{% endtabs %} 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`. @@ -91,29 +139,52 @@ Again, if Scala didn’t have a `toString` method available to every class, you As with inheritance, you can define methods that use `Showable` as a type parameter: +{% tabs 'method' class=tabs-scala-version %} +{% tab 'Scala 2' %} ```scala -def showAll[S: Showable](xs: List[S]): Unit = - xs.foreach(x => println(x.show)) +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[A: Showable](as: List[A]): Unit = + as.foreach(a => println(a.show)) + +showAll(List(Person("Jane"), Person("Mary"))) +``` +{% endtab %} +{% endtabs %} ### A type class with multiple methods Note that if you want to create a type class that has multiple methods, the initial syntax looks like this: +{% 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 %} ### A real-world example For a real-world example of how type classes are used in Scala 3, see the `CanEqual` discussion in the [Multiversal Equality section][multiversal]. - [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 From 1b2bac1828fe6d30e61a1206a21789b8b49c2838 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 3 May 2023 14:28:25 +0200 Subject: [PATCH 7/8] Update intro --- .../scala3-book/ca-context-parameters.md | 2 ++ .../ca-contextual-abstractions-intro.md | 25 +++++++++---------- _overviews/scala3-book/ca-summary.md | 17 +++++++------ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/_overviews/scala3-book/ca-context-parameters.md b/_overviews/scala3-book/ca-context-parameters.md index 4c7efdaf42..ed3ff8fa8e 100644 --- a/_overviews/scala3-book/ca-context-parameters.md +++ b/_overviews/scala3-book/ca-context-parameters.md @@ -150,5 +150,7 @@ 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 diff --git a/_overviews/scala3-book/ca-contextual-abstractions-intro.md b/_overviews/scala3-book/ca-contextual-abstractions-intro.md index ca5bae0d20..5b703e5e6f 100644 --- a/_overviews/scala3-book/ca-contextual-abstractions-intro.md +++ b/_overviews/scala3-book/ca-contextual-abstractions-intro.md @@ -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 @@ -21,24 +20,24 @@ 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**. @@ -46,11 +45,11 @@ Instead of offering one very powerful feature of implicits, Scala 3 offers sever 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. @@ -85,4 +84,4 @@ This chapter introduces many of these new features in the following sections. [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 diff --git a/_overviews/scala3-book/ca-summary.md b/_overviews/scala3-book/ca-summary.md index ede541abe2..e3f3da3cad 100644 --- a/_overviews/scala3-book/ca-summary.md +++ b/_overviews/scala3-book/ca-summary.md @@ -10,16 +10,19 @@ next-page: concurrency This chapter provides an introduction to most Contextual Abstractions topics, including: -- Given Instances and Using Clauses -- Context Bounds -- Given Imports -- Extension Methods -- Implementing Type Classes -- Multiversal Equality -- Implicit Conversions +- [Extension Methods]({% link _overviews/scala3-book/ca-extension-methods.md %}) +- [Given Instances and Using Clauses]({% link _overviews/scala3-book/ca-context-parameters.md %}) +- [Context Bounds]({% link _overviews/scala3-book/ca-context-bounds.md %}) +- [Given Imports]({% link _overviews/scala3-book/ca-given-imports.md %}) +- [Type Classes]({% link _overviews/scala3-book/ca-type-classes.md %}) +- [Multiversal Equality]({% link _overviews/scala3-book/ca-multiversal-equality.md %}) +- [Implicit Conversions]({% link _overviews/scala3-book/ca-implicit-conversions.md %}) + +These features are all variants of the core idea of **term inference**: given a type, the compiler synthesizes a “canonical” term that has that type. A few more advanced topics aren’t covered here, including: +- Conditional Given Instances - Type Class Derivation - Context Functions - By-Name Context Parameters From 92c1f63f0e5aad94bf27eb261ef2cbcd305ef837 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Mon, 8 May 2023 12:16:15 +0200 Subject: [PATCH 8/8] Address feedback from review --- _overviews/core/implicit-classes.md | 2 +- _overviews/scala3-book/ca-context-parameters.md | 5 +++-- _overviews/scala3-book/ca-type-classes.md | 15 ++++++--------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/_overviews/core/implicit-classes.md b/_overviews/core/implicit-classes.md index 720ba7ea8d..eca05c593d 100644 --- a/_overviews/core/implicit-classes.md +++ b/_overviews/core/implicit-classes.md @@ -11,7 +11,7 @@ versionSpecific: true scala2: true --- -In Scala 3, implicit classes have been superseded by [extension methods]({% link _overviews/scala3-book/ca-extension-methods.md %}). +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 %}). --- diff --git a/_overviews/scala3-book/ca-context-parameters.md b/_overviews/scala3-book/ca-context-parameters.md index ed3ff8fa8e..81ca5f7023 100644 --- a/_overviews/scala3-book/ca-context-parameters.md +++ b/_overviews/scala3-book/ca-context-parameters.md @@ -129,8 +129,9 @@ implicit val config: Config = Config(8080, "docs.scala-lang.org") {% 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 + +// this is the type that we want to provide the +// canonical value for // vvvvvv given Config = config // ^^^^^^ diff --git a/_overviews/scala3-book/ca-type-classes.md b/_overviews/scala3-book/ca-type-classes.md index 05fe1ed1d2..18f5ef3f72 100644 --- a/_overviews/scala3-book/ca-type-classes.md +++ b/_overviews/scala3-book/ca-type-classes.md @@ -45,13 +45,12 @@ trait Showable[A] { ```scala // a type class trait Showable[A]: - extension(a: A) def show: String + extension (a: A) def show: String ``` {% endtab %} {% endtabs %} -This is the Scala 3 way of saying that any type that implements this trait must define how the `show` method works. -Notice that the syntax is very close to a normal trait: +Notice that this approach is close to the usual object-oriented approach, where you would typically define a trait `Show` as follows: {% tabs 'trait' class=tabs-scala-version %} {% tab 'Scala 2' %} @@ -73,8 +72,8 @@ trait Show: There are a few important things to point out: -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. -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]`. +1. Type-classes like `Showable` take a type parameter `A` to say which type we provide the implementation of `show` for; in contrast, classic traits like `Show` do not. +2. To add the show functionality to a certain type `A`, the classic trait requires that `A extends Show`, while for type-classes we require to have an implementation of `Showable[A]`. 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. ### Implement concrete instances @@ -90,9 +89,7 @@ case class Person(firstName: String, lastName: String) {% endtab %} {% endtabs %} -you’ll define a `given` value for `Showable[Person]` in Scala 3, or an `implicit` value for `Showable[Person]` in Scala 2. - -This code provides a concrete instance of `Showable` for the `Person` class: +you’ll define a single _canonical value_ of type `Showable[Person]`, ie an instance of `Showable` for the type `Person`, as the following code example demonstrates: {% tabs 'instance' class=tabs-scala-version %} {% tab 'Scala 2' %} @@ -106,7 +103,7 @@ implicit val showablePerson: Showable[Person] = new Showable[Person] { {% tab 'Scala 3' %} ```scala given Showable[Person] with - extension(p: Person) def show: String = + extension (p: Person) def show: String = s"${p.firstName} ${p.lastName}" ``` {% endtab %}