From 8149299fa81c37d779bc1b8dd1cc0e0035fcd6d5 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 12 Apr 2023 16:08:31 +0200 Subject: [PATCH 1/9] Clarify how to label entire pages of documentation that are specific to a Scala version --- _overviews/contribute/add-guides.md | 57 +++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) 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 From 8ec63ef4eda8c6e9071e2ab24bad0c1c562ec8a9 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 12 Apr 2023 16:11:16 +0200 Subject: [PATCH 2/9] =?UTF-8?q?Add=20a=20note=20that=20all=20the=20code=20?= =?UTF-8?q?examples=20assume=20a=20specific=20version=20of=20Scala=20in=20?= =?UTF-8?q?the=20=E2=80=9Cversion-specific-notice=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _includes/version-specific-notice.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/_includes/version-specific-notice.html b/_includes/version-specific-notice.html index b7a5a6f291..bbda6e43e1 100644 --- a/_includes/version-specific-notice.html +++ b/_includes/version-specific-notice.html @@ -3,10 +3,13 @@ {% 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. 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. All the code examples in this page assume you are + using Scala 2. {% endif %} From df5c49c95799b3a6d7b551dd5ffc703dc4ac81e2 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 12 Apr 2023 16:13:44 +0200 Subject: [PATCH 3/9] Use the new way of labelling pages in Multiversal Equality --- _overviews/scala3-book/ca-multiversal-equality.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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. From 4455cd2da32d14128ea35ce8e5905d7f0c1366e4 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 12 Apr 2023 16:18:32 +0200 Subject: [PATCH 4/9] =?UTF-8?q?Use=20the=20new=20way=20of=20labelling=20pa?= =?UTF-8?q?ges=20in=20Given=20Instances=20and=20Using=20Clauses,=20and=20a?= =?UTF-8?q?djust=20the=20=E2=80=9Cversion-specific-notice=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _includes/version-specific-notice.html | 9 ++--- .../scala3-book/ca-given-using-clauses.md | 35 ++----------------- 2 files changed, 7 insertions(+), 37 deletions(-) diff --git a/_includes/version-specific-notice.html b/_includes/version-specific-notice.html index bbda6e43e1..1a9ea6832b 100644 --- a/_includes/version-specific-notice.html +++ b/_includes/version-specific-notice.html @@ -3,13 +3,14 @@ {% if include.language == 'scala3' %} This doc page is specific to Scala 3, - and may cover new concepts not available in Scala 2. All the code - examples in this page assume you are using Scala 3. + 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. All the code examples in this page assume you are - using Scala 2. + alternative. Unless otherwise stated, all the code examples + in this page assume you are using Scala 2. {% endif %} diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index a0c747c450..740ec0561a 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. @@ -48,9 +47,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 +56,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 +64,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 +71,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 +78,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 +91,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 +102,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 From dcea456fb5bd70d17be8dae633dd5a0529ecfeef Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 12 Apr 2023 16:22:30 +0200 Subject: [PATCH 5/9] =?UTF-8?q?Label=20=E2=80=9Ctypes-union=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/scala3-book/types-union.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/_overviews/scala3-book/types-union.md b/_overviews/scala3-book/types-union.md index 1636556767..7d54a8b009 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_. @@ -45,12 +47,17 @@ As shown, union types can be used to represent alternatives of several different #### Pre-planning the Class Hierarchy Other languages would require pre-planning of the class hierarchy, like the following example illustrates: +{% 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 %} + 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. From 35467fcd52ffd8f81d243169546ae9f17f48f872 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 12 Apr 2023 16:32:27 +0200 Subject: [PATCH 6/9] Also label the chinese version of types-union.md --- _zh-cn/overviews/scala3-book/types-union.md | 6 ++++++ 1 file changed, 6 insertions(+) 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 使类型层次结构混乱也会使代码更难阅读。 From 28f1d5c05b319f6bac2b2bc1c1120d714a2302ea Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 13 Apr 2023 09:38:28 +0200 Subject: [PATCH 7/9] =?UTF-8?q?Label=20the=20page=20types-adts-gadts.md=20?= =?UTF-8?q?as=20=E2=80=9CNew=20in=20Scala=203=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/scala3-book/types-adts-gadts.md | 2 ++ 1 file changed, 2 insertions(+) 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 --- From d5f6acef2ef191efa989cba5236426f34d4a8da3 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 13 Apr 2023 11:47:24 +0200 Subject: [PATCH 8/9] Remove tab in given-using-clauses.md --- _overviews/scala3-book/ca-given-using-clauses.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index 740ec0561a..16169334c5 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -22,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) @@ -37,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. From 6b0283c7364a0ca68845e2f1878bf6290b8365d5 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 13 Apr 2023 11:53:43 +0200 Subject: [PATCH 9/9] Tweak types-union.md --- _overviews/scala3-book/types-union.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/_overviews/scala3-book/types-union.md b/_overviews/scala3-book/types-union.md index 7d54a8b009..271c9a0148 100644 --- a/_overviews/scala3-book/types-union.md +++ b/_overviews/scala3-book/types-union.md @@ -45,18 +45,14 @@ 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: -{% 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 %} 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.