diff --git a/_includes/version-specific-notice.html b/_includes/version-specific-notice.html index b7a5a6f291..1a9ea6832b 100644 --- a/_includes/version-specific-notice.html +++ b/_includes/version-specific-notice.html @@ -3,10 +3,14 @@ {% if include.language == 'scala3' %} This doc page is specific to Scala 3, - and may cover new concepts not available in Scala 2. + and may cover new concepts not available in Scala 2. Unless + otherwise stated, all the code examples in this page assume + you are using Scala 3. {% else if include.language == 'scala2' %} This doc page is specific to features shipped in Scala 2, - which have either been removed in Scala 3 or replaced by an alternative. + which have either been removed in Scala 3 or replaced by an + alternative. Unless otherwise stated, all the code examples + in this page assume you are using Scala 2. {% endif %} diff --git a/_overviews/contribute/add-guides.md b/_overviews/contribute/add-guides.md index 7598ad5f63..5a4bed14f7 100644 --- a/_overviews/contribute/add-guides.md +++ b/_overviews/contribute/add-guides.md @@ -83,7 +83,7 @@ clarifications, etc. ## Code blocks It's common for various kinds of documents to require code examples. -You can contribute code in a markdown document by either +You can contribute code in a Markdown document by either - in-line by putting backticks around it, - surrounding by triple backticks, - or indenting it by 4 spaces, e.g.: @@ -92,6 +92,7 @@ You can contribute code in a markdown document by either inline example: `val x = 23` block example: + ```scala println("hello") ``` @@ -103,9 +104,31 @@ indented example: ### Scala 2 vs Scala 3 -Sometimes you would like to compare between Scala 2 and Scala 3 in a document, for example in -our [Hello World][hello-world] chapter of the Scala Book. Here is an example of how you -can generate the same tabs in markdown with the `tabs` directive and class `tabs-scala-version`: +Our goal is to have a unified documentation that covers both Scala 2 and Scala 3. In many cases, the +code examples are the same in both Scala 2 and Scala 3, but sometimes there are some syntactic +differences. In some less common cases, a page may explain a concept that is new in Scala 3 and has +no equivalent in Scala 2, or a concept that has been removed in Scala 3. In all the cases, the +documentation should clearly "label" the code examples so that the readers know in which versions +of Scala they are valid. + +The following sections explain how to properly "label" the code examples. + +#### Labelling the code snippets of a page documenting a concept available in both Scala 2 and Scala 3 + +When the content of a page not specific to Scala 2 or Scala 3, like for example our +[Hello World][hello-world] chapter of the Scala Book, the code snippets should show both the +Scala 2 and Scala 3 syntax. We achieve this by labelling the code snippets in tabs according +to the following rules: + +- if the idiomatic syntax is different in Scala 2 and Scala 3, we create two tabs, + “Scala 2” and “Scala 3”, showing the corresponding syntax +- if the code snippet is idiomatic in both Scala 2 and Scala 3, we create a single tab, + “Scala 2 and 3” +- if the code snippet is valid only in Scala 2 or Scala 3, we create a single tab, + “Scala 2 Only” or “Scala 3 Only” + +Here is an example of how you +can generate such tabs in Markdown with the `tabs` directive and class `tabs-scala-version`: ~~~liquid @@ -161,6 +184,32 @@ a parameter `for=tab-group` as in this example: ~~~ +#### Labelling an entire page documenting a concept that is specific to a Scala version + +When the content of a page explains a concept that is new in Scala 3 and has no +equivalent in Scala 2 (e.g. [TASTy]({% link scala3/guides/tasty-overview.md %})), +or a concept that has been removed in Scala 3, we label the entire page instead +of labelling each code example. + +We achieve this by setting a couple of a attributes in the [YAML front +matter](https://jekyllrb.com/docs/front-matter/) of the Markdown file. For +instance, for a page that is specific to Scala 3: + +~~~ yaml +scala3: true +versionSpecific: true +~~~ + +Or, for a page that is specific to Scala 2: + +~~~ yaml +scala2: true +versionSpecific: true +~~~ + +Please note that when the entire page is labelled, its code examples do not +need to have tabs. + ### Typechecked Examples The site build process uses [mdoc](https://scalameta.org/mdoc/) to typecheck diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index a0c747c450..16169334c5 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -6,11 +6,10 @@ languages: [zh-cn] num: 60 previous-page: ca-extension-methods next-page: ca-context-bounds +scala3: true +versionSpecific: true --- - -
Use contextual abstraction Scala 3 Only
- 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. @@ -23,9 +22,6 @@ One common way to achieve this is by passing the configuration as additional arg In the following example, we define a case class `Config` to model some website configuration and pass it around in the different methods. -{% tabs nonusing %} -{% tab 'Scala 2 and 3' %} - ```scala case class Config(port: Int, baseUrl: String) @@ -38,9 +34,6 @@ 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 `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. @@ -48,9 +41,6 @@ Passing `c` to each and every method call (like `renderWidget`) becomes very ted In Scala 3, we can mark some parameters of our methods as _contextual_. -{% tabs using1 %} -{% tab 'Scala 3 Only' %} - ```scala def renderWebsite(path: String)(using c: Config): String = "" + renderWidget(List("cart")) + "" @@ -60,9 +50,6 @@ def renderWebsite(path: String)(using c: Config): String = def renderWidget(items: List[String])(using c: Config): String = ??? ``` -{% endtab %} -{% endtabs %} - 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**. @@ -71,9 +58,6 @@ 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: -{% tabs using2 %} -{% tab 'Scala 3 Only' %} - ```scala // no need to come up with a parameter name // vvvvvvvvvvvvv @@ -81,9 +65,6 @@ def renderWebsite(path: String)(using Config): String = "" + renderWidget(List("cart")) + "" ``` -{% endtab %} -{% endtabs %} - #### Explicitly providing contextual arguments We have seen how to _abstract_ over contextual parameters and that the Scala compiler can provide arguments automatically for us. @@ -91,16 +72,10 @@ 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:` -{% tabs using3 %} -{% tab 'Scala 3 Only' %} - ```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. @@ -110,9 +85,6 @@ For all other cases, as we will see in the next Section, there is also another w 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`. -{% tabs given1 %} -{% tab 'Scala 3 Only' %} - ```scala val config = Config(8080, "docs.scala-lang.org") // this is the type that we want to provide the @@ -124,24 +96,15 @@ given Config = config // 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 given for `Config`, we can simply call `renderWebsite`: -{% tabs given2 %} -{% tab 'Scala 3 Only' %} - ```scala renderWebsite("/home") // ^^^^^ // again no argument ``` -{% endtab %} -{% endtabs %} - [reference]: {{ site.scala3ref }}/overview.html [blog-post]: /2020/11/06/explicit-term-inference-in-scala-3.html diff --git a/_overviews/scala3-book/ca-multiversal-equality.md b/_overviews/scala3-book/ca-multiversal-equality.md index 54aed55fa4..7d5d6d0c92 100644 --- a/_overviews/scala3-book/ca-multiversal-equality.md +++ b/_overviews/scala3-book/ca-multiversal-equality.md @@ -6,12 +6,9 @@ languages: [zh-cn] num: 64 previous-page: ca-type-classes next-page: ca-implicit-conversions +scala3: true +versionSpecific: true --- -New In Scala 3 - -> Multiversal Equality is a new language feature that was introduced in Scala 3. -> Because it has no equivalent in Scala 2, all code examples -> in this lesson assume you are using Scala 3. Previously, Scala had *universal equality*: Two values of any types could be compared with each other using `==` and `!=`. This came from the fact that `==` and `!=` are implemented in terms of Java’s `equals` method, which can also compare values of any two reference types. diff --git a/_overviews/scala3-book/types-adts-gadts.md b/_overviews/scala3-book/types-adts-gadts.md index 1ee8fb48a6..5cb2d0e550 100644 --- a/_overviews/scala3-book/types-adts-gadts.md +++ b/_overviews/scala3-book/types-adts-gadts.md @@ -6,6 +6,8 @@ languages: [zh-cn] num: 52 previous-page: types-union next-page: types-variance +scala3: true +versionSpecific: true --- diff --git a/_overviews/scala3-book/types-union.md b/_overviews/scala3-book/types-union.md index 1636556767..271c9a0148 100644 --- a/_overviews/scala3-book/types-union.md +++ b/_overviews/scala3-book/types-union.md @@ -6,6 +6,8 @@ languages: [zh-cn] num: 51 previous-page: types-intersection next-page: types-adts-gadts +scala3: true +versionSpecific: true --- Used on types, the `|` operator creates a so-called _union type_. @@ -43,7 +45,7 @@ case 1.0 => ??? // ERROR: this line won’t compile As shown, union types can be used to represent alternatives of several different types, without requiring those types to be part of a custom-crafted class hierarchy, or requiring explicit wrapping. #### Pre-planning the Class Hierarchy -Other languages would require pre-planning of the class hierarchy, like the following example illustrates: +Without union types, it would require pre-planning of the class hierarchy, like the following example illustrates: ```scala trait UsernameOrPassword @@ -51,6 +53,7 @@ case class Username(name: String) extends UsernameOrPassword case class Password(hash: Hash) extends UsernameOrPassword def help(id: UsernameOrPassword) = ... ``` + Pre-planning does not scale very well since, for example, requirements of API users might not be foreseeable. Additionally, cluttering the type hierarchy with marker traits like `UsernameOrPassword` also makes the code more difficult to read. diff --git a/_zh-cn/overviews/scala3-book/types-union.md b/_zh-cn/overviews/scala3-book/types-union.md index 204b1fcccb..51f61590f8 100644 --- a/_zh-cn/overviews/scala3-book/types-union.md +++ b/_zh-cn/overviews/scala3-book/types-union.md @@ -11,6 +11,8 @@ partof: scala3-book overview-name: "Scala 3 — Book" layout: multipage-overview permalink: "/zh-cn/scala3/book/:title.html" +scala3: true +versionSpecific: true --- @@ -54,12 +56,16 @@ case 1.0 => ??? // ERROR: this line won’t compile 其他语言需要预先规划类层次结构,如下例所示: +{% tabs pre-planning %} +{% tab 'Scala 2 and 3' %} ```scala trait UsernameOrPassword case class Username(name: String) extends UsernameOrPassword case class Password(hash: Hash) extends UsernameOrPassword def help(id: UsernameOrPassword) = ... ``` +{% endtab %} +{% endtabs %} 预先计划不能很好地扩展,例如,API 用户的需求可能无法预见。 此外,使用诸如 `UsernameOrPassword` 之类的标记 trait 使类型层次结构混乱也会使代码更难阅读。