diff --git a/Gemfile.lock b/Gemfile.lock index e6d0e2222a..d45b49dceb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -281,7 +281,7 @@ GEM concurrent-ruby (~> 1.0) unicode-display_width (1.8.0) uri (0.13.0) - webrick (1.8.1) + webrick (1.8.2) yell (2.2.2) zeitwerk (2.6.7) diff --git a/README.md b/README.md index 6a32febe4c..013a66267c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Scala Documentation # -[![Build Status](https://ci.scala-lang.org/api/badges/scala/docs.scala-lang/status.svg)](https://platform-ci.scala-lang.org/scala/docs.scala-lang) +[![Build Status](https://github.com/scala/docs.scala-lang/actions/workflows/build.yml/badge.svg)](https://github.com/scala/docs.scala-lang/actions/workflows/build.yml?query=branch%3Amain) This repository contains the source for the Scala documentation website, as well as the source for "Scala Improvement Process" (SIP) documents. diff --git a/_ba/tour/automatic-closures.md b/_ba/tour/automatic-closures.md deleted file mode 100644 index 90f751ee2c..0000000000 --- a/_ba/tour/automatic-closures.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: tour -title: Automatic Type-Dependent Closure Construction -partof: scala-tour - -language: ba ---- diff --git a/_config.yml b/_config.yml index 6c2949e151..aed90af2e1 100644 --- a/_config.yml +++ b/_config.yml @@ -15,9 +15,9 @@ keywords: - Document - Guide -scala-version: 2.13.14 -scala-212-version: 2.12.19 -scala-3-version: 3.5.0 +scala-version: 2.13.15 +scala-212-version: 2.12.20 +scala-3-version: 3.5.2 collections: style: diff --git a/_data/doc-nav-header.yml b/_data/doc-nav-header.yml index 9c2c921667..4f2bbd82cf 100644 --- a/_data/doc-nav-header.yml +++ b/_data/doc-nav-header.yml @@ -1,5 +1,10 @@ - title: Getting Started - url: "/getting-started/index.html" + url: "#" + submenu: + - title: Install Scala + url: "/getting-started/install-scala.html" + - title: Scala IDEs + url: "/getting-started/scala-ides.html" - title: Scala 3 url: "#" submenu: diff --git a/_data/footer.yml b/_data/footer.yml index 43338e7b74..5e24e08b6d 100644 --- a/_data/footer.yml +++ b/_data/footer.yml @@ -21,13 +21,11 @@ links: - title: Community url: "http://scala-lang.org/community/" - - title: Governance - url: "http://scala-lang.org/community/index.html#governance" - title: Scala Ambassadors url: "http://scala-lang.org/ambassadors/" - - title: Mailing Lists - url: "http://scala-lang.org/community/index.html#mailing-lists" - - title: Chat Rooms & More + - title: Forums + url: "http://scala-lang.org/community/index.html#forums" + - title: Chat url: "http://scala-lang.org/community/index.html#chat-rooms" - title: Libraries and Tools url: "http://scala-lang.org/community/index.html#community-libraries-and-tools" @@ -43,16 +41,26 @@ - title: Scala class: scala links: + - title: Governance + url: "http://scala-lang.org/governance/" - title: Blog url: "http://scala-lang.org/blog/" - title: Code of Conduct url: "http://scala-lang.org/conduct/" - title: License url: "http://scala-lang.org/license/" + - title: Security Policy + url: "http://scala-lang.org/security/" - title: Social class: social links: - title: GitHub url: "https://github.com/scala/scala" + - title: Mastodon + url: https://fosstodon.org/@scala_lang - title: Twitter url: "https://twitter.com/scala_lang" + - title: Discord + url: "https://discord.com/invite/scala" + - title: LinkedIn + url: "https://www.linkedin.com/company/scala-center/" \ No newline at end of file diff --git a/_data/nav-header.yml b/_data/nav-header.yml index eeaa88291c..792c68fc1e 100644 --- a/_data/nav-header.yml +++ b/_data/nav-header.yml @@ -8,5 +8,7 @@ url: https://index.scala-lang.org - title: Community url: https://www.scala-lang.org/community/ +- title: Governance + url: https://www.scala-lang.org/governance/ - title: Blog url: https://www.scala-lang.org/blog/ diff --git a/_data/overviews.yml b/_data/overviews.yml index cc1534263e..5756db5e3e 100644 --- a/_data/overviews.yml +++ b/_data/overviews.yml @@ -161,6 +161,9 @@ description: "A diverse and comprehensive set of libraries is important to any productive software ecosystem. While it is easy to develop and distribute Scala libraries, good library authorship goes beyond just writing code and publishing it. In this guide, we cover the important topic of Binary Compatibility." icon: puzzle-piece url: "core/binary-compatibility-for-library-authors.html" + - title: Nightly Versions of Scala + description: "We regularly publish 'nightlies' of both Scala 3 and Scala 2 so that users can preview and test the contents of upcoming releases. Here's how to find and use these versions." + url: "core/nightlies.html" - category: "Tools" description: "Reference material on core Scala tools like the Scala REPL and Scaladoc generation." diff --git a/_data/setup-scala.yml b/_data/setup-scala.yml index ad8db6f598..cda4c2361b 100644 --- a/_data/setup-scala.yml +++ b/_data/setup-scala.yml @@ -2,5 +2,5 @@ linux-x86-64: curl -fL https://github.com/coursier/coursier/releases/latest/down linux-arm64: curl -fL https://github.com/VirtusLab/coursier-m1/releases/latest/download/cs-aarch64-pc-linux.gz | gzip -d > cs && chmod +x cs && ./cs setup macOS-x86-64: curl -fL https://github.com/coursier/coursier/releases/latest/download/cs-x86_64-apple-darwin.gz | gzip -d > cs && chmod +x cs && (xattr -d com.apple.quarantine cs || true) && ./cs setup macOS-arm64: curl -fL https://github.com/VirtusLab/coursier-m1/releases/latest/download/cs-aarch64-apple-darwin.gz | gzip -d > cs && chmod +x cs && (xattr -d com.apple.quarantine cs || true) && ./cs setup -macOS-brew: brew install coursier/formulas/coursier && cs setup +macOS-brew: brew install coursier && coursier setup windows-link: https://github.com/coursier/coursier/releases/latest/download/cs-x86_64-pc-win32.zip diff --git a/_es/tour/automatic-closures.md b/_es/tour/automatic-closures.md deleted file mode 100644 index 5d8cb2f998..0000000000 --- a/_es/tour/automatic-closures.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -layout: tour -title: Construcción de closures automáticas -partof: scala-tour - -num: 16 -language: es - -next-page: operators -previous-page: multiple-parameter-lists ---- - -Scala permite pasar funciones sin parámetros como parámetros de un método. Cuando un método así es invocado, los parámetros reales de la función enviada sin parámetros no son evaluados y una función "nularia" (de aridad cero, 0-aria, o sin parámetros) es pasada en su lugar. Esta función encapsula el comportamiento del parámetro correspondiente (comunmente conocido como "llamada por nombre"). - -Para aclarar un poco esto aquí se muestra un ejemplo: - - object TargetTest1 extends App { - def whileLoop(cond: => Boolean)(body: => Unit): Unit = - if (cond) { - body - whileLoop(cond)(body) - } - var i = 10 - whileLoop (i > 0) { - println(i) - i -= 1 - } - } - -La función `whileLoop` recibe dos parámetros `cond` y `body`. Cuando la función es llamada, los parámetros reales no son evaluados en ese momento. Pero cuando los parámetros son utilizados en el cuerpo de la función `whileLoop`, las funciones nularias creadas implícitamente serán evaluadas en su lugar. Así, nuestro método `whileLoop` implementa un bucle tipo Java mediante una implementación recursiva. - -Es posible combinar el uso de [operadores de infijo y postfijo (infix/postfix)](operators.html) con este mecanismo para crear declaraciones más complejas (con una sintaxis agradadable). - -Aquí mostramos la implementación de una declaración tipo repetir-a-menos-que (repetir el bucle a no ser que se cumpla X condición): - - object TargetTest2 extends App { - def loop(body: => Unit): LoopUnlessCond = - new LoopUnlessCond(body) - protected class LoopUnlessCond(body: => Unit) { - def unless(cond: => Boolean): Unit = { - body - if (!cond) unless(cond) - } - } - var i = 10 - loop { - println("i = " + i) - i -= 1 - } unless (i == 0) - } - -La función `loop` solo acepta el cuerpo de un bucle y retorna una instancia de la clase `LoopUnlessCond` (la cual encapsula el cuerpo del objeto). Es importante notar que en este punto el cuerpo del bucle no ha sido evaluado aún. La clase `LoopUnlessCond` tiene un método `unless` el cual puede ser usado como un *operador de infijo (infix)*. De esta manera podemos lograr una sintaxis muy natural para nuestro nuevo bucle `repetir { a_menos_que ( )`. - -A continuación se expone el resultado de la ejecución de `TargetTest2`: - - i = 10 - i = 9 - i = 8 - i = 7 - i = 6 - i = 5 - i = 4 - i = 3 - i = 2 - i = 1 diff --git a/_es/tour/multiple-parameter-lists.md b/_es/tour/multiple-parameter-lists.md index cdb652151d..83b7218c0b 100644 --- a/_es/tour/multiple-parameter-lists.md +++ b/_es/tour/multiple-parameter-lists.md @@ -6,7 +6,7 @@ partof: scala-tour num: 15 language: es -next-page: automatic-closures +next-page: operators previous-page: nested-functions --- diff --git a/_es/tour/operators.md b/_es/tour/operators.md index a2d3b5e4be..6aeb98e046 100644 --- a/_es/tour/operators.md +++ b/_es/tour/operators.md @@ -7,7 +7,7 @@ num: 17 language: es next-page: higher-order-functions -previous-page: automatic-closures +previous-page: multiple-parameter-lists --- En Scala, cualquier método el cual reciba un solo parámetro puede ser usado como un *operador de infijo (infix)*. Aquí se muestra la definición de la clase `MyBool`, la cual define tres métodos `and`, `or`, y `negate`. diff --git a/_es/tour/tour-of-scala.md b/_es/tour/tour-of-scala.md index 19b4f60af8..b742b271ab 100644 --- a/_es/tour/tour-of-scala.md +++ b/_es/tour/tour-of-scala.md @@ -37,7 +37,6 @@ El [mecanismo de inferencia de tipos locales](type-inference.html) se encarga de En la práctica, el desarrollo de aplicaciones específicas para un dominio generalmente requiere de "Lenguajes de dominio específico" (DSL). Scala provee una única combinación de mecanismos del lenguaje que simplifican la creación de construcciones propias del lenguaje en forma de bibliotecas: * cualquier método puede ser usado como un operador de [infijo o postfijo](operators.html) -* [las closures son construidas automáticamente dependiendo del tipo esperado](automatic-closures.html) (tipos objetivo). El uso conjunto de ambas características facilita la definición de nuevas sentencias sin tener que extender la sintaxis y sin usar facciones de meta-programación como tipo macros. diff --git a/_fr/getting-started/index.md b/_fr/getting-started/install-scala.md similarity index 100% rename from _fr/getting-started/index.md rename to _fr/getting-started/install-scala.md diff --git a/_fr/tour/automatic-closures.md b/_fr/tour/automatic-closures.md deleted file mode 100644 index f5a06a5f5f..0000000000 --- a/_fr/tour/automatic-closures.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: tour -title: Automatic Closures -partof: scala-tour - -language: fr ---- diff --git a/_includes/_markdown/install-cask.md b/_includes/_markdown/install-cask.md new file mode 100644 index 0000000000..afb2754321 --- /dev/null +++ b/_includes/_markdown/install-cask.md @@ -0,0 +1,37 @@ +{% altDetails require-info-box 'Getting Cask' %} + +{% tabs cask-install class=tabs-build-tool %} + +{% tab 'Scala CLI' %} +You can declare a dependency on Cask with the following `using` directive: +```scala +//> using dep "com.lihaoyi::cask::0.9.2" +``` +{% endtab %} + +{% tab 'sbt' %} +In your `build.sbt`, you can add a dependency on Cask: +```scala +lazy val example = project.in(file("example")) + .settings( + scalaVersion := "3.4.2", + libraryDependencies += "com.lihaoyi" %% "cask" % "0.9.2", + fork := true + ) +``` +{% endtab %} + +{% tab 'Mill' %} +In your `build.sc`, you can add a dependency on Cask: +```scala +object example extends RootModule with ScalaModule { + def scalaVersion = "3.3.3" + def ivyDeps = Agg( + ivy"com.lihaoyi::cask::0.9.2" + ) +} +``` +{% endtab %} + +{% endtabs %} +{% endaltDetails %} diff --git a/_ja/getting-started/index.md b/_ja/getting-started/install-scala.md similarity index 100% rename from _ja/getting-started/index.md rename to _ja/getting-started/install-scala.md diff --git a/_ja/index.md b/_ja/index.md index b94f171774..358ef903da 100644 --- a/_ja/index.md +++ b/_ja/index.md @@ -14,7 +14,7 @@ sections: - title: "入門" description: "あなたのコンピューターに Scala をインストールして、Scala コードを書きはじめよう!" icon: "fa fa-rocket" - link: /ja/getting-started/index.html + link: /ja/getting-started/install-scala.html - title: "Scala ツアー" description: "コア言語機能をひと口大で紹介" icon: "fa fa-flag" diff --git a/_ja/overviews/macros/paradise.md b/_ja/overviews/macros/paradise.md index 7c897ff357..5fd8e5de7d 100644 --- a/_ja/overviews/macros/paradise.md +++ b/_ja/overviews/macros/paradise.md @@ -19,7 +19,7 @@ title: マクロパラダイス マクロパラダイス (Macro paradise) とは Scala の複数のバージョンをサポートするコンパイラプラグインで、一般向けにリリースされている scalac と共に正しく動作するように設計されている。 これによって、将来の Scala に取り込まれるよりもいち早く最新のマクロ機能を使えるようになっている。 [サポートされている機能とバージョンの一覧](/ja/overviews/macros/roadmap.html))に関してはロードマップページを、 -動作の保証に関しては[マクロパラダイスのアナウンスメント](https://scalamacros.org/news/2013/08/07/roadmap-for-macro-paradise.html)を参照してほしい。 +動作の保証に関しては[マクロパラダイスのアナウンスメント](hxxps://scalamacros.org/news/2013/08/07/roadmap-for-macro-paradise.html)を参照してほしい。 ~/210x $ scalac -Xplugin:paradise_*.jar -Xshow-phases phase name id description diff --git a/_ja/overviews/macros/typemacros.md b/_ja/overviews/macros/typemacros.md index 38dae43189..0ed863eb80 100644 --- a/_ja/overviews/macros/typemacros.md +++ b/_ja/overviews/macros/typemacros.md @@ -9,7 +9,7 @@ title: 型マクロ **Eugene Yokota 訳** 型マクロ (type macro) は[マクロパラダイス](/ja/overviews/macros/paradise.html)の以前のバージョンから利用可能だったが、マクロパラダイス 2.0 ではサポートされなくなった。 -[the paradise 2.0 announcement](https://scalamacros.org/news/2013/08/05/macro-paradise-2.0.0-snapshot.html) に説明と移行のための戦略が書かれている。 +[the paradise 2.0 announcement](hxxps://scalamacros.org/news/2013/08/05/macro-paradise-2.0.0-snapshot.html) に説明と移行のための戦略が書かれている。 ## 直観 diff --git a/_ja/overviews/macros/untypedmacros.md b/_ja/overviews/macros/untypedmacros.md index 08ad463cd9..7b857f783b 100644 --- a/_ja/overviews/macros/untypedmacros.md +++ b/_ja/overviews/macros/untypedmacros.md @@ -9,7 +9,7 @@ title: 型指定の無いマクロ **Eugene Yokota 訳** 型指定の無いマクロ (untyped macro) は[マクロパラダイス](/ja/overviews/macros/paradise.html)の以前のバージョンから利用可能だったが、マクロパラダイス 2.0 ではサポートされなくなった。 -[the paradise 2.0 announcement](https://scalamacros.org/news/2013/08/05/macro-paradise-2.0.0-snapshot.html) に説明と移行のための戦略が書かれている。 +[the paradise 2.0 announcement](hxxps://scalamacros.org/news/2013/08/05/macro-paradise-2.0.0-snapshot.html) に説明と移行のための戦略が書かれている。 ## 直観 diff --git a/_ja/overviews/macros/usecases.md b/_ja/overviews/macros/usecases.md index 9db3bc1398..5a6767e378 100644 --- a/_ja/overviews/macros/usecases.md +++ b/_ja/overviews/macros/usecases.md @@ -20,7 +20,7 @@ Scala の商用ユーザと研究ユーザの両方がマクロを利用して ここ EPFL においても我々はマクロを活用して研究を行っている。Lightbend 社もマクロを数々のプロジェクトに採用している。 マクロはコミュニティー内でも人気があり、既にいくつかの興味深い応用が現れている。 -最近行われた講演の ["What Are Macros Good For?"](https://scalamacros.org/paperstalks/2014-02-04-WhatAreMacrosGoodFor.pdf) では Scala 2.10 ユーザのマクロの利用方法を説明し、システム化した。講演の大筋はマクロはコード生成、静的な検査、および DSL に有効であるということで、これを研究や産業からの例を交えながら説明した。 +最近行われた講演の ["What Are Macros Good For?"](https://github.com/scalamacros/scalamacros.github.com/blob/5904f7ef88a439c668204b4bf262835e89fb13cb/paperstalks/2014-02-04-WhatAreMacrosGoodFor.pdf) では Scala 2.10 ユーザのマクロの利用方法を説明し、システム化した。講演の大筋はマクロはコード生成、静的な検査、および DSL に有効であるということで、これを研究や産業からの例を交えながら説明した。 -Scala'13 ワークショップにおいて ["Scala Macros: Let Our Powers Combine!"](https://scalamacros.org/paperstalks/2013-04-22-LetOurPowersCombine.pdf) という論文を発表した。これは Scala 2.10 における最先端のマクロ論をより学問的な視点から説明した。 +Scala'13 ワークショップにおいて ["Scala Macros: Let Our Powers Combine!"](https://github.com/scalamacros/scalamacros.github.com/blob/5904f7ef88a439c668204b4bf262835e89fb13cb/paperstalks/2013-04-22-LetOurPowersCombine.pdf) という論文を発表した。これは Scala 2.10 における最先端のマクロ論をより学問的な視点から説明した。 この論文では Scala のリッチな構文と静的な型がマクロと相乗することを示し、また既存の言語機能をマクロによって新しい方法で活用できることを考察する。 diff --git a/_ja/overviews/reflection/overview.md b/_ja/overviews/reflection/overview.md index e7d28c55fd..d884785fe5 100644 --- a/_ja/overviews/reflection/overview.md +++ b/_ja/overviews/reflection/overview.md @@ -81,7 +81,7 @@ Scala コンパイラが持つ型情報を全ては入手できない可能性 上の例では、まず `scala.reflect.runtime.universe` をインポートして (型タグを使うためには必ずインポートされる必要がある)、`l` という名前の `List[Int]` を作る。 -次に、context bound を持った型パラメータ `T` を持つ `getTypeTag` というメソッドは定義する +次に、context bound を持った型パラメータ `T` を持つ `getTypeTag` というメソッドを定義する (REPL が示すとおり、これは暗黙の evidence パラメータを定義することに等価であり、コンパイラは `T` に対する型タグを生成する)。 最後に、このメソッドに `l` を渡して呼び出し、`TypeTag` に格納される型を返す `tpe` を呼び出す。 見ての通り、正しい完全な型 (つまり、`List` の具象型引数を含むということ) である `List[Int]` が返ってきた。 diff --git a/_ja/tour/automatic-closures.md b/_ja/tour/automatic-closures.md deleted file mode 100644 index 80cb7edf7e..0000000000 --- a/_ja/tour/automatic-closures.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -layout: tour -title: 型依存クロージャの自動構築 -language: ja -partof: scala-tour ---- - -Scalaはメソッドのパラメータとしてパラメータ無しの関数名を渡せます。そのようなメソッドが呼ばれると、パラメータ無しの関数名は実際に評価されず、代わりに、対応するパラメーターの処理をカプセル化した、引数無しの関数が渡されます(いわゆる *名前渡し*評価です)。 - -以下のコードはこの仕組みを説明しています。 - - object TargetTest1 extends Application { - def whileLoop(cond: => Boolean)(body: => Unit): Unit = - if (cond) { - body - whileLoop(cond)(body) - } - var i = 10 - whileLoop (i > 0) { - println(i) - i -= 1 - } - } - -関数 whileLoop は2つのパラメータ`cond`と`body`を受け取ります。関数が適用される時、実際のパラメータは評価されません。しかし形式上のパラメータが`whileLoop`の本体内で使われる度に、暗黙に生成された引数の無い関数が代わりに評価されます。このようにメソッド`whileLoop`はJavaのようなwhileループを再帰的な方法で実装しています。 - -[中置/後置 演算子](operators.html)とこのメカニズムを組み合わせて利用し、より複雑な命令文を(より良い構文で)作れます。 - -こちらがloop-unless式の実装です。 - - object TargetTest2 extends Application { - def loop(body: => Unit): LoopUnlessCond = - new LoopUnlessCond(body) - protected class LoopUnlessCond(body: => Unit) { - def unless(cond: => Boolean): Unit = { - body - if (!cond) unless(cond) - } - } - var i = 10 - loop { - println("i = " + i) - i -= 1 - } unless (i == 0) - } -この`loop`関数はループ処理の本体を受け取り、クラス`LoopUnlessCond`(この処理の本体をカプセル化する)のインスタンスを返すだけです。処理の本体はまだ評価されていないことに気をつけてください。クラス`LoopUnlessCond`は *中置演算子* として使えるメソッド`unless`を持ちます。このように、新しいループ処理: `loop { < stats > } unless ( < cond > )`のとても自然な構文を作れます。 - -こちらが`TargetTest2`を実行した時の出力です。 - - i = 10 - i = 9 - i = 8 - i = 7 - i = 6 - i = 5 - i = 4 - i = 3 - i = 2 - i = 1 diff --git a/_ko/tour/annotations.md b/_ko/tour/annotations.md index 924664303d..11b5da4b50 100644 --- a/_ko/tour/annotations.md +++ b/_ko/tour/annotations.md @@ -7,7 +7,7 @@ num: 31 language: ko next-page: packages-and-imports -previous-page: automatic-closures +previous-page: operators --- 어노테이션은 메타 정보와 정의 내용을 연결해준다. diff --git a/_ko/tour/automatic-closures.md b/_ko/tour/automatic-closures.md deleted file mode 100644 index 639623c537..0000000000 --- a/_ko/tour/automatic-closures.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -layout: tour -title: 타입 의존 클로저의 자동 구성 -partof: scala-tour - -num: 30 -language: ko - -next-page: annotations -previous-page: operators ---- - -스칼라에선 파라미터가 없는 함수의 이름을 메소드의 파라미터로 사용할 수 있다. 이런 메소드가 호출되면 파라미터가 없는 함수의 이름에 해당하는 실제 파라미터를 찾지 않고, 대신 해당 파라미터의 계산을 캡슐화한 무항 함수를 전달하게 된다(소위 말하는 *이름에 의한 호출* 연산). - -다음 코드는 이 방식을 사용하는 방법을 보여준다. - - object TargetTest1 extends App { - def whileLoop(cond: => Boolean)(body: => Unit): Unit = - if (cond) { - body - whileLoop(cond)(body) - } - var i = 10 - whileLoop (i > 0) { - println(i) - i -= 1 - } - } - -`whileLoop` 함수는 `cond`와 `body`라는 두 파라미터를 받는다. 이 함수가 적용될 때 실제 파라미터는 계산되지 않는다. 대신 `whileLoop`의 내부에서 이 정형 파라미터를 사용할 때마다 암시적으로 생성된 무항 함수로 처리한다. 따라서 `whileLoop` 메소드는 재귀 구현의 방식에 맞춰 자바와 같은 while 반복문을 구현한다. - -[중위/후위 연산자](operators.html)와 이 기법을 함께 사용해 좀 더 복잡한 명령문(보기 좋게 작성된)을 생성할 수 있다. - -다음은 반복문을 제거한 명령문 구현이다. - - object TargetTest2 extends App { - def loop(body: => Unit): LoopUnlessCond = - new LoopUnlessCond(body) - protected class LoopUnlessCond(body: => Unit) { - def unless(cond: => Boolean): Unit = { - body - if (!cond) unless(cond) - } - } - var i = 10 - loop { - println("i = " + i) - i -= 1 - } unless (i == 0) - } - -`loop` 함수는 단순히 반복문의 내용을 받아서 `LoopUnlessCond` 클래스의 인스턴스(반복문 내용에 해당하는 객체를 캡슐화한)를 반환한다. 해당 내용이 아직 계산되지 않았음을 유념하자. `LoopUnlessCond` 클래스는 *중위 연산자*로 사용할 수 있는 `unless`라는 메소드를 포함하고 있다. 이런 접근을 통해 상당히 자연스럽게 표현된 새로운 반복문을 완성하게 된다: `loop { < stats > } unless ( < cond > )`. - -다음은 `TargetTest2`를 실행한 출력 결과다. - - i = 10 - i = 9 - i = 8 - i = 7 - i = 6 - i = 5 - i = 4 - i = 3 - i = 2 - i = 1 - -윤창석, 이한욱 옮김 diff --git a/_ko/tour/operators.md b/_ko/tour/operators.md index fd904bfdd7..4306b08744 100644 --- a/_ko/tour/operators.md +++ b/_ko/tour/operators.md @@ -6,7 +6,7 @@ partof: scala-tour num: 29 language: ko -next-page: automatic-closures +next-page: annotations previous-page: type-inference --- diff --git a/_overviews/FAQ/index.md b/_overviews/FAQ/index.md index 7d3966986e..7c0b101dd2 100644 --- a/_overviews/FAQ/index.md +++ b/_overviews/FAQ/index.md @@ -65,7 +65,7 @@ In short, the only officially sanctioned place is the \#jobs channel ### Who's behind Scala? -This is answered [on the community page](https://www.scala-lang.org/community/#whos-behind-scala). +This is answered [on the Governance page](https://www.scala-lang.org/governance/). ### Can I use the Scala logo? @@ -73,6 +73,10 @@ See [scala/scala-lang#1040](https://github.com/scala/scala-lang/issues/1040). ## Technical questions +### What IDEs are available for Scala? + +See [this doc page](https://docs.scala-lang.org/getting-started/scala-ides.html). + ### What compiler flags are recommended? The list of available options is @@ -244,18 +248,22 @@ differ from a function value such as: val square: Int => Int = x => x * x -For Scala 2, there is a [complete answer on Stack Overflow](https://stackoverflow.com/a/2530007/4111404) +For **Scala 2**, there is a [complete answer on Stack Overflow](https://stackoverflow.com/a/2530007/4111404) and a [summary with practical differences](https://tpolecat.github.io/2014/06/09/methods-functions.html). -Note that in **Scala 3** the differences are fewer; -for example, they will be able to -[accept implicit parameters]({{ site.scala3ref }}/contextual/context-functions.html) -as well as [type parameters]({{ site.scala3ref }}/new-types/polymorphic-function-types.html). +In **Scala 3**, the differences are fewer. +[Context functions]({{ site.scala3ref }}/contextual/context-functions.html) +accept given parameters and +[polymorphic functions]({{ site.scala3ref }}/new-types/polymorphic-function-types.html) +have type parameters. -Nevertheless, it is still recommended to use methods most of the time, -unless you absolutely need a function. And, thanks to -[eta-expansion](https://stackoverflow.com/questions/39445018/what-is-the-eta-expansion-in-scala) -you rarely would need to define a function rather than a method. +It's standard to use methods most of the time, +except when a function value is actually needed. +[Eta-expansion](https://stackoverflow.com/questions/39445018/what-is-the-eta-expansion-in-scala), +converts methods to functions when needed. +For example, a method such as `map` expects a function, +but even if you `def square` as shown above, you can +still `xs.map(square)`. ### What's the difference between types and classes? @@ -341,14 +349,14 @@ setting in a multi-project build. For example, if you add this to your `build.sbt`: - scalaVersion := "2.13.14" + scalaVersion := "2.13.15" that's a "bare" setting, and you might expect it to apply build-wide. But it doesn't. _It only applies to the root project._ In many cases one should instead write: - ThisBuild / scalaVersion := "2.13.14" + ThisBuild / scalaVersion := "2.13.15" Other possibilities include: diff --git a/_overviews/collections-2.13/maps.md b/_overviews/collections-2.13/maps.md index 0d11c535a8..34d9696f19 100644 --- a/_overviews/collections-2.13/maps.md +++ b/_overviews/collections-2.13/maps.md @@ -51,8 +51,8 @@ Immutable maps support in addition operations to add and remove mappings by retu | **Additions and Updates:**| | | `ms.updated(k, v)`
or `ms + (k -> v)` |The map containing all mappings of `ms` as well as the mapping `k -> v` from key `k` to value `v`.| | **Removals:** | | -| `ms.remove(k)`
or `ms - k` |The map containing all mappings of `ms` except for any mapping of key `k`.| -| `ms.removeAll(ks)`
or `ms -- ks` |The map containing all mappings of `ms` except for any mapping with a key in `ks`.| +| `ms.removed(k)`
or `ms - k` |The map containing all mappings of `ms` except for any mapping of key `k`.| +| `ms.removedAll(ks)`
or `ms -- ks` |The map containing all mappings of `ms` except for any mapping with a key in `ks`.| Mutable maps support in addition the operations summarized in the following table. diff --git a/_overviews/contribute/add-guides.md b/_overviews/contribute/add-guides.md index 5a4bed14f7..4840739cda 100644 --- a/_overviews/contribute/add-guides.md +++ b/_overviews/contribute/add-guides.md @@ -338,7 +338,7 @@ You must also add the tutorial to the drop-down list in the navigation bar. To d --- - title: Getting Started - url: "/getting-started/index.html" + url: "/getting-started/install-scala.html" - title: Learn ... - title: Tutorials diff --git a/_overviews/contribute/bug-reporting-guide.md b/_overviews/contribute/bug-reporting-guide.md index c7bc2636dc..d168797a2d 100644 --- a/_overviews/contribute/bug-reporting-guide.md +++ b/_overviews/contribute/bug-reporting-guide.md @@ -65,7 +65,7 @@ If you cannot find your issue in the issue tracker, create a new bug. The detail Please make sure to fill in as many fields as possible. Make sure you've indicated the following: - 1. **Exact Scala version** that you are using. For example, `2.13.14` or `3.3.3`. If the bug happens in multiple versions indicate all of them. + 1. **Exact Scala version** that you are using. For example, `2.13.15` or `3.3.3`. If the bug happens in multiple versions indicate all of them. 2. **The component** that is affected by the bug. For example, the Standard Library, Scaladoc, etc. 3. **Labels** related to your issue. For example, if you think your issue is related to the typechecker, and if you have successfully minimized your issue, label your bug as "typechecker" and "minimized". Issue tracker will suggest names for existing labels as you type them so try not to create duplicates. 4. **Running environment**. Are you running on Linux? Windows? What JVM version are you using? diff --git a/_overviews/contribute/guide.md b/_overviews/contribute/guide.md index beffb01868..f5307a325a 100644 --- a/_overviews/contribute/guide.md +++ b/_overviews/contribute/guide.md @@ -47,7 +47,7 @@ This is the impatient developer's checklist for the steps to submit a bug-fix pu 5. [Fix the bug, or implement the new small feature][hackers-implement], include new tests (yes, for bug fixes too). 6. [Test, rinse][hackers-test] and [test some more][partest-guide] until [all the tests pass][hackers-verify]. 7. [Commit your changes][hackers-commit] to your feature branch in your fork. Please choose your commit message based on the [Git Hygiene](https://github.com/scala/scala#user-content-git-hygiene) section of the Scala project README. -8. If necessary [re-write git history](https://git-scm.com/book/en/Git-Branching-Rebasing) so that [commits are organized by major steps to the fix/feature]( +8. If necessary [re-write git history](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) so that [commits are organized by major steps to the fix/feature]( https://github.com/scala/scala#git-hygiene). For bug fixes, a single commit is requested, for features several commits may be desirable (but each separate commit must compile and pass all tests) 9. [Submit a pull request][hackers-submit]. 10. [Work with a reviewer](https://github.com/scala/scala#reviewing) to [get your pull request merged in][hackers-review]. diff --git a/_overviews/contribute/hacker-guide.md b/_overviews/contribute/hacker-guide.md index e78df88b12..ea77feee0d 100644 --- a/_overviews/contribute/hacker-guide.md +++ b/_overviews/contribute/hacker-guide.md @@ -51,7 +51,7 @@ Hacking Scala begins with creating a branch for your work item. To develop Scala and [GitHub](https://github.com/). This section of the guide provides a short walkthrough, but if you are new to Git, it probably makes sense to familiarize yourself with Git first. We recommend -* the [Git Pro](https://git-scm.com/book/en/) online book. +* the [Git Pro](https://git-scm.com/book/en/v2) online book. * the help page on [Forking a Git Repository](https://help.github.com/articles/fork-a-repo). * this great training tool [LearnGitBranching](https://pcottle.github.io/learnGitBranching/). One-hour hands-on training helps more than 1000 hours reading. @@ -96,7 +96,7 @@ Since in our example, we're going to fix an existing bug 16:39 ~/Projects/scala (master)$ git checkout -b ticket/6725 Switched to a new branch 'ticket/6725' -If you are new to Git and branching, read the [Branching Chapter](https://git-scm.com/book/en/Git-Branching) in the Git Pro book. +If you are new to Git and branching, read the [Branching Chapter](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell) in the Git Pro book. ### Build @@ -333,11 +333,11 @@ Let's go into each of these points in more detail. ### Commit -The [Git Basics](https://git-scm.com/book/en/Git-Basics) chapter in the Git online book covers most of the basic workflow during this stage. +The [Git Basics](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) chapter in the Git online book covers most of the basic workflow during this stage. There are two things you should know here: 1. Commit messages are often the only way to understand the intentions of authors of code written a few years ago. Thus, writing a quality is of utmost importance. The more context you provide for the change you've introduced, the larger the chance that some future maintainer understand your intentions. Consult [the pull request policies](https://github.com/scala/scala/blob/2.12.x/CONTRIBUTING.md) for more information about the desired style of your commits. -2. Keeping Scala's git history clean is also important. Therefore we won't accept pull requests for bug fixes that have more than one commit. For features, it is okay to have several commits, but all tests need to pass after every single commit. To clean up your commit structure, you want to [rewrite history](https://git-scm.com/book/en/Git-Branching-Rebasing) using `git rebase` so that your commits are against the latest revision of `master`. +2. Keeping Scala's git history clean is also important. Therefore we won't accept pull requests for bug fixes that have more than one commit. For features, it is okay to have several commits, but all tests need to pass after every single commit. To clean up your commit structure, you want to [rewrite history](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) using `git rebase` so that your commits are against the latest revision of `master`. Once you are satisfied with your work, synced with `master` and cleaned up your commits you are ready to submit a patch to the central Scala repository. Before proceeding make sure you have pushed all of your local changes to your fork on GitHub. diff --git a/_overviews/core/binary-compatibility-of-scala-releases.md b/_overviews/core/binary-compatibility-of-scala-releases.md index ebe205403c..e65407c926 100644 --- a/_overviews/core/binary-compatibility-of-scala-releases.md +++ b/_overviews/core/binary-compatibility-of-scala-releases.md @@ -23,7 +23,7 @@ Thus, backward compatibility precludes the removal of (non-private) methods, as For Scala 2, the *minor* version is the *third* number in a version, e.g., 10 in v2.13.10. The major version is the second number, which is 13 in our example. -Scala 2 up to 2.13.14 guarantees both backward and forward compatibility across *minor* releases within a single major release. +Scala 2 up to 2.13.15 guarantees both backward and forward compatibility across *minor* releases within a single major release. This is about to change now that [SIP-51 has been accepted](https://docs.scala-lang.org/sips/drop-stdlib-forwards-bin-compat.html), future Scala 2.13 releases may be backward compatible only. For Scala 3, the minor version is the *second* number in a version, e.g., 2 in v3.2.1. diff --git a/_overviews/core/nightlies.md b/_overviews/core/nightlies.md new file mode 100644 index 0000000000..8155ea2bfe --- /dev/null +++ b/_overviews/core/nightlies.md @@ -0,0 +1,87 @@ +--- +layout: singlepage-overview +title: Nightly Versions of Scala +permalink: /overviews/core/:title.html +--- + +We regularly publish nightly versions of both Scala 3 and 2 so that users can preview and test the contents of upcoming releases. + +Here's how to find and use these versions. + +## Scala 3 + +Scala 3 nightly versions are published to Maven Central. If you know the full version number of the nightly you want to use, you can use it just like any other Scala 3 version. + +One quick way to get that version number is to visit [https://dotty.epfl.ch](https://dotty.epfl.ch) and look in the upper left corner. + +Another way is to scrape Maven Central, as shown in this script: [https://raw.githubusercontent.com/VirtusLab/community-build3/master/scripts/lastVersionNightly.sc](https://raw.githubusercontent.com/VirtusLab/community-build3/master/scripts/lastVersionNightly.sc) + +A third way is to use [scala-cli](https://scala-cli.virtuslab.org), as follows. (Since Scala 3.5.0, the `scala` command runs `scala-cli`.) + +### scala-cli + +You can run nightlies with commands such as: + + scala-cli -S 3.nightly + scala-cli -S 3.3.nightly + +The default command is `repl`, but all the other scala-cli subcommands such as `compile` and `run` work, too. It also works with `//>` directives in your script itself, for example: + + //> using scala 3.nightly + +See this [scala-cli doc page](https://scala-cli.virtuslab.org/docs/commands/compile#scala-nightlies) for details. + +## Scala 2.13 or 2.12 + +We informally refer to Scala 2 “nightly” versions, but technically it's a misnomer. A so-called “nightly” is built for every merged PR. + +Scala 2 nightly versions are published to a special resolver. Unless you are using scala-cli, you'll need to add that resolver to your build configuration in order to use these versions. + +### quick version (sbt) + + Global / resolvers += "scala-integration" at + "https://scala-ci.typesafe.com/artifactory/scala-integration/" + scalaVersion := "2.13.15-bin-abcd123" + +For a 2.12 nightly, substitute e.g. `2.12.20` for `2.13.15`; in either case, it's the version number of the _next_ release on that branch. + +For `abcd123`, substitute the first 7 characters of the SHA of the latest commit to the [2.13.x branch](https://github.com/scala/scala/commits/2.13.x) or [2.12.x branch](https://github.com/scala/scala/commits/2.12.x) that has a green checkmark. (Clicking the checkmark will show a CI job name with the whole version in its name.) + +A quick way to find out the full version number of a current nightly is to use [scala-cli](https://scala-cli.virtuslab.org), as follows. + +### quick version (scala-cli) + +You can run nightlies with: + + scala-cli -S 2.13.nightly + scala-cli -S 2.nightly # same as 2.13.nightly + scala-cli -S 2.12.nightly + +The default command is `repl`, but all the other scala-cli subcommands such as `compile` and `run` work, too. It also works with `//>` directives in your script itself, for example: + + //> using scala 2.nightly + +### Longer explanation + +We no longer publish `-SNAPSHOT` versions of Scala 2. + +But the team does publish nightly versions, each with its own fixed version number. The version number of a nightly looks like e.g. `2.13.1-bin-abcd123`. (`-bin-` signals binary compatibility to sbt; all 2.13.x releases since 2.13.0 are binary compatible with each other.) + +To tell sbt to use one of these nightlies, you need to do three things. + +First, add the resolver where the nightlies are kept: + + Global / resolvers += "scala-integration" at + "https://scala-ci.typesafe.com/artifactory/scala-integration/" + +Second, specify the Scala version: + + scalaVersion := "2.13.1-bin-abcd123" + +But that isn't a real version number. Manually substitute a version number containing the 7-character SHA of the last commit in the [scala/scala repository](https://github.com/scala/scala) for which a nightly version was published. Look at [https://travis-ci.org/scala/scala/branches](https://travis-ci.org/scala/scala/branches) and you'll see the SHA in the upper right corner of the 2.13.x (or 2.12.x) section. + +As soon as 2.13.1 is released, the version number in the nightly will bump to 2.13.2, and so on. + +If you have a multiproject build, be sure you set these settings across all projects when you modify your build definition. Or, you may set them temporarily in the sbt shell with `++2.13.1-bin-abcd123` (sbt 0.13.x) or `++2.13.1-bin-abcd123!` (sbt 1.x; the added exclamation point is necessary to force a version not included in `crossScalaVersions` to be used). + +Ideally, we would suggest an automated way to ask Travis-CI for the right SHA. This is presumably possible via Travis-CI's API, but as far as we know, nobody has looked into it yet. (Is there a volunteer?) diff --git a/_overviews/getting-started/index.md b/_overviews/getting-started/index.md deleted file mode 100644 index 8c33b915dd..0000000000 --- a/_overviews/getting-started/index.md +++ /dev/null @@ -1,243 +0,0 @@ ---- -layout: singlepage-overview -title: Getting Started -partof: getting-started -languages: [fr, ja, ru, uk] -includeTOC: true -newcomer_resources: - - title: Are You Coming From Java? - description: What you should know to get to speed with Scala after your initial setup. - icon: "fa fa-coffee" - link: /tutorials/scala-for-java-programmers.html - - title: Scala in the Browser - description: > - To start experimenting with Scala right away, use "Scastie" in your browser. - icon: "fa fa-cloud" - link: https://scastie.scala-lang.org/pEBYc5VMT02wAGaDrfLnyw - -redirect_from: - - /getting-started.html - - /scala3/getting-started.html # we deleted the scala 3 version of this page ---- - -The instructions below cover both Scala 2 and Scala 3. - -
-{% altDetails need-help-info-box 'Need Help?' class=help-info %} -*If you are having trouble with setting up Scala, feel free to ask for help in the `#scala-users` channel of -[our Discord](https://discord.com/invite/scala).* -{% endaltDetails %} -
- -## Resources For Newcomers - -{% include inner-documentation-sections.html links=page.newcomer_resources %} - -## Install Scala on your computer - -Installing Scala means installing various command-line tools such as the Scala compiler and build tools. -We recommend using the Scala installer tool "Coursier" that automatically installs all the requirements, but you can still manually install each tool. - -### Using the Scala Installer (recommended way) - -The Scala installer is a tool named [Coursier](https://get-coursier.io/docs/cli-overview), whose main command is named `cs`. -It ensures that a JVM and standard Scala tools are installed on your system. -Install it on your system with the following instructions. - - -{% tabs install-cs-setup-tabs class=platform-os-options %} - - -{% tab macOS for=install-cs-setup-tabs %} -Run the following command in your terminal, following the on-screen instructions: -{% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.macOS-brew %} -{% altDetails cs-setup-macos-nobrew "Alternatively for Apple Silicon, or if you don't use Homebrew:" %} - On the Apple Silicon (M1, M2, …) architecture: - {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.macOS-arm64 %} - Otherwise, on the x86-64 architecture: - {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.macOS-x86-64 %} -{% endaltDetails %} -{% endtab %} - - - -{% tab Linux for=install-cs-setup-tabs %} - Run the following command in your terminal, following the on-screen instructions. - - On the x86-64 architecture: - {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.linux-x86-64 %} - Otherwise, on the ARM64 architecture: - {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.linux-arm64 %} -{% endtab %} - - - -{% tab Windows for=install-cs-setup-tabs %} - Download and execute [the Scala installer for Windows]({{site.data.setup-scala.windows-link}}) - based on Coursier, and follow the on-screen instructions. -{% endtab %} - - - -{% tab Other for=install-cs-setup-tabs defaultTab %} - - Follow the documentation from Coursier on - [how to install and run `cs setup`](https://get-coursier.io/docs/cli-installation). -{% endtab %} - - -{% endtabs %} - - ->    You may need to restart your terminal, log out, -> or reboot in order for the changes to take effect. -{: .help-info} - - -{% altDetails testing-your-setup 'Testing your setup' %} -Check your setup with the command `scala -version`, which should output: -```bash -$ scala -version -Scala code runner version {{site.scala-3-version}} -- Copyright 2002-2022, LAMP/EPFL -``` -{% endaltDetails %} - - - -Along with managing JVMs, `cs setup` also installs useful command-line tools: - -| Commands | Description | -|----------|-------------| -| `scalac` | the Scala compiler | -| `scala` | the Scala REPL and script runner | -| `scala-cli`| [Scala CLI](https://scala-cli.virtuslab.org), interactive toolkit for Scala | -| `sbt`, `sbtn` | The [sbt](https://www.scala-sbt.org/) build tool | -| `amm` | [Ammonite](https://ammonite.io/) is an enhanced REPL | -| `scalafmt` | [Scalafmt](https://scalameta.org/scalafmt/) is the Scala code formatter | - -For more information about `cs`, read -[coursier-cli documentation](https://get-coursier.io/docs/cli-overview). - -> `cs setup` installs the Scala 3 compiler and runner by default (the `scalac` and -> `scala` commands, respectively). Whether you intend to use Scala 2 or 3, -> this is usually not an issue because most projects use a build tool that will -> use the correct version of Scala irrespective of the one installed "globally". -> Nevertheless, you can always launch a specific version of Scala using -> ``` -> $ cs launch scala:{{ site.scala-version }} -> $ cs launch scalac:{{ site.scala-version }} -> ``` -> If you prefer Scala 2 to be run by default, you can force that version to be installed with: -> ``` -> $ cs install scala:{{ site.scala-version }} scalac:{{ site.scala-version }} -> ``` - -### ...or manually - -You only need two tools to compile, run, test, and package a Scala project: Java 8 or 11, -and sbt. -To install them manually: - -1. if you don't have Java 8 or 11 installed, download - Java from [Oracle Java 8](https://www.oracle.com/java/technologies/javase-jdk8-downloads.html), [Oracle Java 11](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html), - or [AdoptOpenJDK 8/11](https://adoptopenjdk.net/). Refer to [JDK Compatibility](/overviews/jdk-compatibility/overview.html) for Scala/Java compatibility detail. -1. Install [sbt](https://www.scala-sbt.org/download.html) - -## Create a "Hello World" project with sbt - -Once you have installed sbt, you are ready to create a Scala project, which -is explained in the following sections. - -To create a project, you can either use the command line or an IDE. -If you are familiar with the command line, we recommend that approach. - -### Using the command line - -sbt is a build tool for Scala. sbt compiles, runs, -and tests your Scala code. (It can also publish libraries and do many other tasks.) - -To create a new Scala project with sbt: - -1. `cd` to an empty folder. -1. Run the command `sbt new scala/scala3.g8` to create a Scala 3 project, or `sbt new scala/hello-world.g8` to create a Scala 2 project. - This pulls a project template from GitHub. - It will also create a `target` folder, which you can ignore. -1. When prompted, name the application `hello-world`. This will - create a project called "hello-world". -1. Let's take a look at what just got generated: - -``` -- hello-world - - project (sbt uses this for its own files) - - build.properties - - build.sbt (sbt's build definition file) - - src - - main - - scala (all of your Scala code goes here) - - Main.scala (Entry point of program) <-- this is all we need for now -``` - -More documentation about sbt can be found in the [Scala Book](/scala3/book/tools-sbt.html) (see [here](/overviews/scala-book/scala-build-tool-sbt.html) for the Scala 2 version) -and in the official sbt [documentation](https://www.scala-sbt.org/1.x/docs/index.html) - -### With an IDE - -You can skip the rest of this page and go directly to [Building a Scala Project with IntelliJ and sbt](/getting-started/intellij-track/building-a-scala-project-with-intellij-and-sbt.html) - - -## Open hello-world project - -Let's use an IDE to open the project. The most popular ones are [IntelliJ](https://www.jetbrains.com/idea/) and -[VSCode](https://scalameta.org/metals/docs/editors/vscode). -They both offer rich IDE features, but you can still use [many other editors](https://scalameta.org/metals/docs/editors/overview.html). - -### Using IntelliJ - -1. Download and install [IntelliJ Community Edition](https://www.jetbrains.com/help/idea/installation-guide.html) -1. Install the Scala plugin by following [the instructions on how to install IntelliJ plugins](https://www.jetbrains.com/help/idea/discover-intellij-idea-for-scala.html) -1. Open the `build.sbt` file then choose *Open as a project* - -### Using VSCode with metals - -1. Download [VSCode](https://code.visualstudio.com/Download) -1. Install the Metals extension from [the Marketplace](https://marketplace.visualstudio.com/items?itemName=scalameta.metals) -1. Next, open the directory containing a `build.sbt` file (this should be the directory `hello-world` if you followed the previous instructions). When prompted to do so, select *Import build*. - -### Play with the source code - -View these two files in your IDE: - -- _build.sbt_ -- _src/main/scala/Main.scala_ - -When you run your project in the next step, the configuration in _build.sbt_ will be used to run the code in _src/main/scala/Main.scala_. - -## Run Hello World - -If you’re comfortable using your IDE, you can run the code in _Main.scala_ from your IDE. - -Otherwise, you can run the application from a terminal with these steps: - -1. `cd` into `hello-world`. -1. Run `sbt`. This opens up the sbt console. -1. Type `~run`. The `~` is optional and causes sbt to re-run on every file save, - allowing for a fast edit/run/debug cycle. sbt will also generate a `target` directory - which you can ignore. - -When you’re finished experimenting with this project, press `[Enter]` to interrupt the `run` command. -Then type `exit` or press `[Ctrl+D]` to exit sbt and return to your command line prompt. - -## Next Steps - -Once you've finished the above tutorials, consider checking out: - -* [The Scala Book](/scala3/book/introduction.html) (see the Scala 2 version [here](/overviews/scala-book/introduction.html)), which provides a set of short lessons introducing Scala’s main features. -* [The Tour of Scala](/tour/tour-of-scala.html) for bite-sized introductions to Scala's features. -* [Learning Resources](/learn.html), which includes online interactive tutorials and courses. -* [Our list of some popular Scala books](/books.html). -* [The migration guide](/scala3/guides/migration/compatibility-intro.html) helps you to migrate your existing Scala 2 code base to Scala 3. - -## Getting Help -There are a multitude of mailing lists and real-time chat rooms in case you want to quickly connect with other Scala users. Check out our [community](https://scala-lang.org/community/) page for a list of these resources, and for where to reach out for help. diff --git a/_overviews/getting-started/install-scala.md b/_overviews/getting-started/install-scala.md new file mode 100644 index 0000000000..9514530fa2 --- /dev/null +++ b/_overviews/getting-started/install-scala.md @@ -0,0 +1,345 @@ +--- +layout: singlepage-overview +title: Getting Started +partof: getting-started +languages: [fr, ja, ru, uk] +includeTOC: true +newcomer_resources: + - title: Are You Coming From Java? + description: What you should know to get to speed with Scala after your initial setup. + icon: "fa fa-coffee" + link: /tutorials/scala-for-java-programmers.html + - title: Scala in the Browser + description: > + To start experimenting with Scala right away, use "Scastie" in your browser. + icon: "fa fa-cloud" + link: https://scastie.scala-lang.org/pEBYc5VMT02wAGaDrfLnyw + +redirect_from: + - /getting-started.html + - /scala3/getting-started.html # we deleted the scala 3 version of this page +--- + +The instructions below cover both Scala 2 and Scala 3. + +
+{% altDetails need-help-info-box 'Need Help?' class=help-info %} +*If you are having trouble with setting up Scala, feel free to ask for help in the `#scala-users` channel of +[our Discord](https://discord.com/invite/scala).* +{% endaltDetails %} +
+ +## Resources For Newcomers + +{% include inner-documentation-sections.html links=page.newcomer_resources %} + +## Install Scala on your computer + +Installing Scala means installing various command-line tools such as the Scala compiler and build tools. +We recommend using the Scala installer tool "Coursier" that automatically installs all the requirements, but you can still manually install each tool. + +### Using the Scala Installer (recommended way) + +The Scala installer is a tool named [Coursier](https://get-coursier.io/docs/cli-overview), whose main command is named `cs`. +It ensures that a JVM and standard Scala tools are installed on your system. +Install it on your system with the following instructions. + + +{% tabs install-cs-setup-tabs class=platform-os-options %} + + +{% tab macOS for=install-cs-setup-tabs %} +Run the following command in your terminal, following the on-screen instructions: +{% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.macOS-brew %} +{% altDetails cs-setup-macos-nobrew "Alternatively, if you don't use Homebrew:" %} + On the Apple Silicon (M1, M2, …) architecture: + {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.macOS-arm64 %} + Otherwise, on the x86-64 architecture: + {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.macOS-x86-64 %} +{% endaltDetails %} +{% endtab %} + + + +{% tab Linux for=install-cs-setup-tabs %} + Run the following command in your terminal, following the on-screen instructions. + + On the x86-64 architecture: + {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.linux-x86-64 %} + Otherwise, on the ARM64 architecture: + {% include code-snippet.html language='bash' codeSnippet=site.data.setup-scala.linux-arm64 %} +{% endtab %} + + + +{% tab Windows for=install-cs-setup-tabs %} + Download and execute [the Scala installer for Windows]({{site.data.setup-scala.windows-link}}) + based on Coursier, and follow the on-screen instructions. +{% endtab %} + + + +{% tab Other for=install-cs-setup-tabs defaultTab %} + + Follow the documentation from Coursier on + [how to install and run `cs setup`](https://get-coursier.io/docs/cli-installation). +{% endtab %} + + +{% endtabs %} + + +>    You may need to restart your terminal, log out, +> or reboot in order for the changes to take effect. +{: .help-info} + + +{% altDetails testing-your-setup 'Testing your setup' %} +Check your setup with the command `scala -version`, which should output: +```bash +$ scala -version +Scala code runner version: 1.4.3 +Scala version (default): {{site.scala-3-version}} +``` +{% endaltDetails %} + + + +Along with managing JVMs, `cs setup` also installs useful command-line tools: + +| Commands | Description | +|----------|-------------| +| `scalac` | the Scala compiler | +| `scala`, `scala-cli` | [Scala CLI](https://scala-cli.virtuslab.org), interactive toolkit for Scala | +| `sbt`, `sbtn` | The [sbt](https://www.scala-sbt.org/) build tool | +| `amm` | [Ammonite](https://ammonite.io/) is an enhanced REPL | +| `scalafmt` | [Scalafmt](https://scalameta.org/scalafmt/) is the Scala code formatter | + +For more information about `cs`, read +[coursier-cli documentation](https://get-coursier.io/docs/cli-overview). + +> `cs setup` installs the Scala 3 compiler and runner by default (the `scalac` and +> `scala` commands, respectively). Whether you intend to use Scala 2 or 3, +> this is usually not an issue because most projects use a build tool that will +> use the correct version of Scala irrespective of the one installed "globally". +> Nevertheless, you can always launch a specific version of Scala using +> ``` +> $ cs launch scala:{{ site.scala-version }} +> $ cs launch scalac:{{ site.scala-version }} +> ``` +> If you prefer Scala 2 to be run by default, you can force that version to be installed with: +> ``` +> $ cs install scala:{{ site.scala-version }} scalac:{{ site.scala-version }} +> ``` + +### ...or manually + +You only need two tools to compile, run, test, and package a Scala project: Java 8 or 11, +and Scala CLI. +To install them manually: + +1. if you don't have Java 8 or 11 installed, download + Java from [Oracle Java 8](https://www.oracle.com/java/technologies/javase-jdk8-downloads.html), [Oracle Java 11](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html), + or [AdoptOpenJDK 8/11](https://adoptopenjdk.net/). Refer to [JDK Compatibility](/overviews/jdk-compatibility/overview.html) for Scala/Java compatibility detail. +1. Install [Scala CLI](https://scala-cli.virtuslab.org/install) + +## Using the Scala CLI + +In a directory of your choice, which we will call ``, create a file named `hello.scala` with the following code: +```scala +//> using scala {{site.scala-3-version}} + +@main +def hello(): Unit = + println("Hello, World!") +``` + +You can define a method with the `def` keyword and mark it as a "main" method with the `@main` annotation, designating it as +the entry point in program execution. The method's type is `Unit`, which means it does not return a value. `Unit` +can be thought of as an analogue to the `void` keyword found in other languages. The `println` method will print the `"Hello, World!"` +string to standard output. + +To run the program, execute `scala run hello.scala` command from a terminal, within the `` directory. The file will be compiled and executed, with console output +similar to following: +``` +$ scala run hello.scala +Compiling project (Scala {{site.scala-3-version}}, JVM (20)) +Compiled project (Scala {{site.scala-3-version}}, JVM (20)) +Hello, World! +``` + +### Handling command-line arguments + +Rewrite the `hello.scala` file so that the program greets the person running it. +```scala +//> using scala {{site.scala-3-version}} + +@main +def hello(name: String): Unit = + println(s"Hello, $name!") +``` + +The `name` argument is expected to be provided when executing the program, and if it's not found, the execution will fail. +The `println` method receives an interpolated string, as indicated by the `s` letter preceding its content. `$name` will be substituted by +the content of the `name` argument. + +To pass the arguments when executing the program, put them after `--`: +``` +$ scala run hello.scala -- Gabriel +Compiling project (Scala {{site.scala-3-version}}, JVM (20)) +Compiled project (Scala {{site.scala-3-version}}, JVM (20)) +Hello, Gabriel! +``` + +You can read more about [main methods](/scala3/book/methods-main-methods.html) and [string interpolation](/scala3/book/string-interpolation.html) in the Scala Book. + +### Adding dependencies + +We now write a program that will count the files and directories present in its working directory. +We use the [os-lib](https://github.com/com-lihaoyi/os-lib) library from the [Scala toolkit](/toolkit/introduction.html) +for that purpose. A dependency on the library can be added with the `//> using` directive. Put the following code in `counter.scala`. +```scala +//> using scala {{site.scala-3-version}} +//> using dep "com.lihaoyi::os-lib:0.10.7" + +@main +def countFiles(): Unit = + val paths = os.list(os.pwd) + println(paths.length) +``` + +In the code above, `os.pwd` returns the current working directory. We pass it to `os.list`, which returns a sequence +of paths directly within the directory passed as an argument. We use a `val` to declare an immutable value, in this example storing the +sequence of paths. + +Execute the program. The dependency will be automatically downloaded. The execution should result in a similar output: +``` +$ scala run counter.scala +Compiling project (Scala {{site.scala-3-version}}, JVM (20)) +Compiled project (Scala {{site.scala-3-version}}, JVM (20)) +4 +``` +The printed number should be 4: `hello.scala`, `counter.scala` and two hidden directories created automatically when a program is executed: +`.bsp` containing information about project used by IDEs, and `.scala-build` containing the results of compilation. + +As it turns out, the `os-lib` library is a part of Scala Toolkit, a collection of libraries recommended for tasks like testing, +operating system interaction or handling JSONs. You can read more about the libraries included in the toolkit [here](/toolkit/introduction.html). +To include the toolkit libraries, use the `//> using toolkit 0.5.0` directive: +```scala +//> using scala {{site.scala-3-version}} +//> using toolkit 0.5.0 + +@main +def countFiles(): Unit = + val paths = os.list(os.pwd) + println(paths.length) +``` + +This program is identical to the one above. However, other toolkit libraries will also be available to use, should you need them. + +### Using the REPL + +You can execute code interactively using the REPL provided by the `scala` command. Execute `scala` in the console without any arguments. +``` +$ scala +Welcome to Scala {{site.scala-3-version}} (20-ea, Java OpenJDK 64-Bit Server VM). +Type in expressions for evaluation. Or try :help. + +scala> +``` + +Write a line of code to be executed and press enter. +``` +scala> println("Hello, World!") +Hello, World! + +scala> +``` + +The result will be printed immediately after executing the line. You can declare values: +``` +scala> val i = 1 +val i: Int = 1 + +scala> +``` + +A new value of type `Int` has been created. If you provide an expression that can be evaluated, its result will be stored in an automatically created value. +``` +scala> i + 3 +val res0: Int = 4 + +scala> +``` +You can exit the REPL with `:exit`. + +## Using an IDE + +> You can read a short summary of Scala IDEs on [a dedicated page](/getting-started/scala-ides.html). + +Let's use an IDE to open the code we wrote above. The most popular ones are [IntelliJ](https://www.jetbrains.com/idea/) and +[VSCode](https://scalameta.org/metals/docs/editors/vscode). +They both offer rich IDE features, but you can still use [many other editors](https://scalameta.org/metals/docs/editors/overview.html). + +### Prepare the project + +First, remove all the using directives, and put them in a single file `project.scala` in the `` directory. +This makes it easier to import as a project in an IDE: + +```scala +//> using scala {{site.scala-3-version}} +//> using toolkit 0.5.0 +``` + +> Optionally, you can re-initialise the necessary IDE files from within the `` directory with the command `scala setup-ide .`, but these files will already exist if you have previously run the project with the Scala CLI `run` command. + +### Using IntelliJ + +1. Download and install [IntelliJ Community Edition](https://www.jetbrains.com/help/idea/installation-guide.html) +1. Install the Scala plugin by following [the instructions on how to install IntelliJ plugins](https://www.jetbrains.com/help/idea/discover-intellij-idea-for-scala.html) +1. Open the `` directory, which should be imported automatically as a BSP project. + +### Using VSCode with Metals + +1. Download [VSCode](https://code.visualstudio.com/Download) +1. Install the Metals extension from [the Marketplace](https://marketplace.visualstudio.com/items?itemName=scalameta.metals) +1. Next, open the `` directory in VSCode. Metals should activate and begin importing the project automatically. + +### Play with the source code + +View these three files in your IDE: + +- _project.scala_ +- _hello.scala_ +- _counter.scala_ + +You should notice the benefits of an IDE, such as syntax highlighting, and smart code interactions. +For example you can place the cursor over any part of the code, such as `os.pwd` in _counter.scala_ and documentation for the method will appear. + +When you run your project in the next step, the configuration in _project.scala_ will be used to run the code in the other source files. + +### Run the code + +If you’re comfortable using your IDE, you can run the code in _counter.scala_ from your IDE. +Attached to the `countFiles` method should be a prompt button. Click it to run the method. This should run without issue. +The `hello` method in _hello.scala_ needs arguments however, so will require extra configuration via the IDE to provide the argument. + +Otherwise, you can run either application from the IDE's built-in terminal as described in above sections. + +## Next steps + +Now that you have tasted a little bit of Scala, you can further explore the language itself, consider checking out: + +* [The Scala Book](/scala3/book/introduction.html) (see the Scala 2 version [here](/overviews/scala-book/introduction.html)), which provides a set of short lessons introducing Scala’s main features. +* [The Tour of Scala](/tour/tour-of-scala.html) for bite-sized introductions to Scala's features. +* [Learning Resources](/learn.html), which includes online interactive tutorials and courses. +* [Our list of some popular Scala books](/books.html). + +There are also other tutorials for other build-tools you can use with Scala: +* [Getting Started with Scala and sbt](/getting-started/sbt-track/getting-started-with-scala-and-sbt-on-the-command-line.html) +* [Using Scala and Maven](/tutorials/scala-with-maven.html) + +## Getting Help +There are a multitude of mailing lists and real-time chat rooms in case you want to quickly connect with other Scala users. Check out our [community](https://scala-lang.org/community/) page for a list of these resources, and for where to reach out for help. diff --git a/_overviews/getting-started/scala-ides.md b/_overviews/getting-started/scala-ides.md new file mode 100644 index 0000000000..9f210d4b1e --- /dev/null +++ b/_overviews/getting-started/scala-ides.md @@ -0,0 +1,55 @@ +--- +layout: singlepage-overview +title: Scala IDEs + +partof: scala-ides + +permalink: /getting-started/:title.html + +keywords: +- Scala +- IDE +- JetBrains +- IntelliJ +- VSCode +- Metals +--- + +It's of course possible to write Scala code in any editor and compile and run the code from the command line. But most developers prefer to use an IDE (Integrated Development Environment), especially for coding anything beyond simple exercises. + +The following IDEs are available for Scala: + +## IntelliJ IDEA + Scala plugin + +[https://jetbrains.com/scala](https://jetbrains.com/scala) + +![](../../resources/images/getting-started/IntelliJScala.png) + +IntelliJ IDEA is a cross-platform IDE developed by JetBrains that provides a consistent experience for a wide range of programming languages and technologies. It also supports Scala through the IntelliJ Scala Plugin, which is being developed at JetBrains. First, install IntelliJ IDEA Community Edition (unless you don't already use the Ultimate edition) and then add the IntelliJ Scala Plugin. + +IntelliJ IDEA and Scala Plugin will assist you in virtually every part of a Scala software developer's work. Use it if you like a solid integrated experience, sane default settings, and tested solutions. + +For more information, check out our tutorial [Getting Started with Scala in IntelliJ](/getting-started/intellij-track/building-a-scala-project-with-intellij-and-sbt.html) + +## Visual Studio Code + Metals + +[https://scalameta.org/metals](https://scalameta.org/metals) + +![](../../resources/images/getting-started/VSCodeMetals.png) + +Visual Studio Code, commonly called VS Code, is a source code editor from Microsoft. To add Scala support, you install an extension called Metals. + +(Why "Metals"? Because the underlying technologies are Scalameta and LSP ([Language Server Protocol](https://microsoft.github.io/language-server-protocol/)), and "Meta" + "LS" equals "Metals".) + +In contrast to IntelliJ IDEA + Scala Plugin, VS Code + Metals is aimed at people who like to get feedback and code intelligence straight from the compiler, which enables them to also try out experimental Scala features. + +## Your favorite editor + Metals + +Metals is most commonly used with VS Code, but it's also available for the following popular editors: + +* Emacs +* Vim +* Sublime Text +* Helix + +as documented [here](https://scalameta.org/metals/docs/#editor-support). diff --git a/_overviews/jdk-compatibility/overview.md b/_overviews/jdk-compatibility/overview.md index 8d8849cc79..99b953306f 100644 --- a/_overviews/jdk-compatibility/overview.md +++ b/_overviews/jdk-compatibility/overview.md @@ -14,7 +14,7 @@ Minimum Scala versions: | JDK | 3 | 2.13 | 2.12 | 2.11 | |:-----------:|:--------:|:---------:|:---------:|:----------:| -| 23 (ea) | 3.3.5* | 2.13.15* | 2.12.20* | | +| 23 | 3.3.5* | 2.13.15 | 2.12.20 | | | 22 | 3.3.4* | 2.13.13 | 2.12.19 | | | 21 (LTS) | 3.3.1 | 2.13.11 | 2.12.18 | | | 17 (LTS) | 3.0.0 | 2.13.6 | 2.12.15 | | @@ -119,16 +119,18 @@ Scala 2.13.13+ and 2.12.19+ support JDK 22. We are working on adding JDK 22 support to the 3.3.x release series. (Support may be available in nightly builds.) -For possible Scala issues, see the [jdk11](https://github.com/scala/bug/labels/jdk11), [jdk17](https://github.com/scala/bug/labels/jdk17), and [jdk21](https://github.com/scala/bug/labels/jdk21) labels in the Scala 2 bug tracker. +For possible Scala 2 issues, see the [jdk11](https://github.com/scala/bug/labels/jdk11), [jdk17](https://github.com/scala/bug/labels/jdk17), and [jdk21](https://github.com/scala/bug/labels/jdk21) labels in the Scala 2 bug tracker. ## JDK 23 compatibility notes -Early access builds of JDK 23 are available. JDK 23 will be non-LTS. +JDK 23 is non-LTS. -We are working on adding JDK 23 support to Scala 3 and Scala 2. -(Support may be available in nightly builds.) +Scala 2.13.15+ and Scala 2.12.20+ support JDK 23. -For possible Scala issues, see the [jdk11](https://github.com/scala/bug/labels/jdk11), [jdk17](https://github.com/scala/bug/labels/jdk17), and [jdk21](https://github.com/scala/bug/labels/jdk21) labels in the Scala 2 bug tracker. +We are working on adding JDK 23 support to Scala 3. +(Support may be available in nightly builds and/or release candidates.) + +For possible Scala 2 issues, see the [jdk11](https://github.com/scala/bug/labels/jdk11), [jdk17](https://github.com/scala/bug/labels/jdk17), and [jdk21](https://github.com/scala/bug/labels/jdk21) labels in the Scala 2 bug tracker. ## GraalVM Native Image compatibility notes diff --git a/_overviews/scala-book/preliminaries.md b/_overviews/scala-book/preliminaries.md index cbc5221dff..8308f59818 100644 --- a/_overviews/scala-book/preliminaries.md +++ b/_overviews/scala-book/preliminaries.md @@ -21,7 +21,7 @@ That being said, there are a few good things to know before you read this book. ## Installing Scala -First, to run the examples in this book you’ll need to install Scala on your computer. See our general [Getting Started]({{site.baseurl}}/getting-started/index.html) page for details on how to use Scala (a) in an IDE and (b) from the command line. +First, to run the examples in this book you’ll need to install Scala on your computer. See our general [Getting Started]({{site.baseurl}}/getting-started/install-scala.html) page for details on how to use Scala (a) in an IDE and (b) from the command line. diff --git a/_overviews/scala-book/two-types-variables.md b/_overviews/scala-book/two-types-variables.md index 8c6a105c79..3ce00a0e54 100644 --- a/_overviews/scala-book/two-types-variables.md +++ b/_overviews/scala-book/two-types-variables.md @@ -94,8 +94,7 @@ object Hello3 extends App { As before: - Save that code in a file named *Hello3.scala* -- Compile it with `scalac Hello3.scala` -- Run it with `scala Hello3` +- Compile and run it with `scala run Hello3.scala` diff --git a/_overviews/scala3-book/ca-implicit-conversions.md b/_overviews/scala3-book/ca-implicit-conversions.md index b35902d71a..0fb665eda3 100644 --- a/_overviews/scala3-book/ca-implicit-conversions.md +++ b/_overviews/scala3-book/ca-implicit-conversions.md @@ -150,7 +150,7 @@ object `Conversions`: import scala.language.implicitConversions object Conversions { - implicit def fromStringToUser(name: String): User = (name: String) => User(name) + implicit def fromStringToUser(name: String): User = User(name) } ~~~ {% endtab %} diff --git a/_overviews/scala3-book/control-structures.md b/_overviews/scala3-book/control-structures.md index 6598cb6047..b982cf654a 100644 --- a/_overviews/scala3-book/control-structures.md +++ b/_overviews/scala3-book/control-structures.md @@ -810,6 +810,53 @@ speak(Person("Bam Bam")) // "Bam Bam says, Bam bam!" {% endtab %} {% endtabs %} +#### Binding matched patterns to variables + +You can bind the matched pattern to a variable to use type-specific behavior. + +{% tabs pattern-binding class=tabs-scala-version %} +{% tab 'Scala 2' for=pattern-binding %} +```scala +trait Animal { + val name: String +} +case class Cat(name: String) extends Animal { + def meow: String = "Meow" +} +case class Dog(name: String) extends Animal { + def bark: String = "Bark" +} + +def speak(animal: Animal) = animal match { + case c @ Cat(name) if name == "Felix" => println(s"$name says, ${c.meow}!") + case d @ Dog(name) if name == "Rex" => println(s"$name says, ${d.bark}!") + case _ => println("I don't know you!") +} + +speak(Cat("Felix")) // "Felix says, Meow!" +speak(Dog("Rex")) // "Rex says, Bark!" +``` +{% endtab %} +{% tab 'Scala 3' for=pattern-binding %} +```scala +trait Animal: + val name: String +case class Cat(name: String) extends Animal: + def meow: String = "Meow" +case class Dog(name: String) extends Animal: + def bark: String = "Bark" + +def speak(animal: Animal) = animal match + case c @ Cat(name) if name == "Felix" => println(s"$name says, ${c.meow}!") + case d @ Dog(name) if name == "Rex" => println(s"$name says, ${d.bark}!") + case _ => println("I don't know you!") + +speak(Cat("Felix")) // "Felix says, Meow!" +speak(Dog("Rex")) // "Rex says, Bark!" +``` +{% endtab %} +{% endtabs %} + ### Using a `match` expression as the body of a method Because `match` expressions return a value, they can be used as the body of a method. diff --git a/_overviews/scala3-book/domain-modeling-tools.md b/_overviews/scala3-book/domain-modeling-tools.md index 9b6901504f..c1475ce161 100644 --- a/_overviews/scala3-book/domain-modeling-tools.md +++ b/_overviews/scala3-book/domain-modeling-tools.md @@ -373,7 +373,7 @@ Those constructors can be called like this: ```scala val s1 = new Student("Mary", "123") -val s2 = new Student("Mary", "123", LocalDate.now) +val s2 = new Student("Mary", "123", LocalDate.now()) val s3 = new Student("Mary", "123", 456) ``` @@ -383,7 +383,7 @@ val s3 = new Student("Mary", "123", 456) ```scala val s1 = Student("Mary", "123") -val s2 = Student("Mary", "123", LocalDate.now) +val s2 = Student("Mary", "123", LocalDate.now()) val s3 = Student("Mary", "123", 456) ``` diff --git a/_overviews/scala3-book/methods-main-methods.md b/_overviews/scala3-book/methods-main-methods.md index 797d7d04a4..e2a30abfb1 100644 --- a/_overviews/scala3-book/methods-main-methods.md +++ b/_overviews/scala3-book/methods-main-methods.md @@ -25,7 +25,7 @@ Scala 3 offers a new way to define programs that can be invoked from the command To run this program, save the line of code in a file named as e.g. *Hello.scala*---the filename doesn’t have to match the method name---and run it with `scala`: ```bash -$ scala Hello.scala +$ scala run Hello.scala Hello, World ``` @@ -64,10 +64,10 @@ For example, given this `@main` method that takes an `Int`, a `String`, and a va {% endtab %} {% endtabs %} -When you compile that code, it creates a main program named `happyBirthday` that’s called like this: +Pass the arguments after `--`: ``` -$ scala happyBirthday 23 Lisa Peter +$ scala run happyBirthday.scala -- 23 Lisa Peter Happy 23rd Birthday, Lisa and Peter! ``` @@ -79,10 +79,10 @@ The program implemented from an `@main` method checks that there are enough argu If a check fails, the program is terminated with an error message: ``` -$ scala happyBirthday 22 +$ scala run happyBirthday.scala -- 22 Illegal command line after first argument: more arguments expected -$ scala happyBirthday sixty Fred +$ scala run happyBirthday.scala -- sixty Fred Illegal command line: java.lang.NumberFormatException: For input string: "sixty" ``` @@ -176,11 +176,9 @@ object happyBirthday { {% endtab %} {% endtabs %} -If you place that code in a file named *happyBirthday.scala*, you can then compile it with `scalac` and run it with `scala`, as shown previously: +If you place that code in a file named *happyBirthday.scala*, you can then compile and run it with `scala`, as shown previously: ```bash -$ scalac happyBirthday.scala - -$ scala happyBirthday 23 Lisa Peter +$ scala run happyBirthday.scala -- 23 Lisa Peter Happy 23rd Birthday, Lisa and Peter! ``` diff --git a/_overviews/scala3-book/scala-for-java-devs.md b/_overviews/scala3-book/scala-for-java-devs.md index 37482bdc49..b80f3a427a 100644 --- a/_overviews/scala3-book/scala-for-java-devs.md +++ b/_overviews/scala3-book/scala-for-java-devs.md @@ -1064,7 +1064,7 @@ Pair pair = Triplet triplet = Triplet.with("Eleven", 11, 11.0); -Quartet triplet = +Quartet quartet = Quartet.with("Eleven", 11, 11.0, new Person("Eleven")); ``` diff --git a/_overviews/scala3-book/string-interpolation.md b/_overviews/scala3-book/string-interpolation.md index 2e47a872b1..e1c4f10054 100644 --- a/_overviews/scala3-book/string-interpolation.md +++ b/_overviews/scala3-book/string-interpolation.md @@ -34,7 +34,7 @@ The `s` that you place before the string is just one possible interpolator that provides. Scala provides three string interpolation methods out of the box: `s`, `f` and `raw`. -Further, a string interpolator is a just special method so it is possible to define your +Further, a string interpolator is just a special method, so it is possible to define your own. For instance, some database libraries define a `sql` interpolator that returns a database query. diff --git a/_overviews/scala3-book/taste-hello-world.md b/_overviews/scala3-book/taste-hello-world.md index 4bdf996f08..52fc532e5e 100644 --- a/_overviews/scala3-book/taste-hello-world.md +++ b/_overviews/scala3-book/taste-hello-world.md @@ -47,53 +47,23 @@ object hello { {% endtabs %} -Next, compile the code with `scalac`: +Next, compile and run the code with `scala`: ```bash -$ scalac hello.scala +$ scala run hello.scala ``` -If you’re coming to Scala from Java, `scalac` is just like `javac`, so that command creates several files: - - -{% tabs hello-world-outputs class=tabs-scala-version %} - -{% tab 'Scala 2' for=hello-world-outputs %} -```bash -$ ls -1 -hello$.class -hello.class -hello.scala +The command should produce an output similar to: ``` -{% endtab %} - -{% tab 'Scala 3' for=hello-world-outputs %} -```bash -$ ls -1 -hello$package$.class -hello$package.class -hello$package.tasty -hello.scala -hello.class -hello.tasty -``` -{% endtab %} - -{% endtabs %} - - -Like Java, the _.class_ files are bytecode files, and they’re ready to run in the JVM. - -Now you can run the `hello` method with the `scala` command: - -```bash -$ scala hello +Compiling project (Scala {{site.scala-3-version}}, JVM (20)) +Compiled project (Scala {{site.scala-3-version}}, JVM (20)) Hello, World! ``` Assuming that worked, congratulations, you just compiled and ran your first Scala application. > More information about sbt and other tools that make Scala development easier can be found in the [Scala Tools][scala_tools] chapter. +> The Scala CLI documentation can be found [here](https://scala-cli.virtuslab.org/). ## Ask For User Input @@ -152,16 +122,13 @@ use the `+` operator on strings to join `"Hello, "` with `name` and `"!"`, makin > You can learn more about using `val` by reading [Variables and Data Types](/scala3/book/taste-vars-data-types.html). -Then compile the code with `scalac`: - -```bash -$ scalac helloInteractive.scala -``` -Then run it with `scala helloInteractive`, this time the program will pause after asking for your name, +Then run the code with `scala`. This time the program will pause after asking for your name, and wait until you type a name and press return on the keyboard, looking like this: ```bash -$ scala helloInteractive +$ scala run helloInteractive.scala +Compiling project (Scala {{site.scala-3-version}}, JVM (20)) +Compiled project (Scala {{site.scala-3-version}}, JVM (20)) Please enter your name: ▌ ``` @@ -169,7 +136,9 @@ Please enter your name: When you enter your name at the prompt, the final interaction should look like this: ```bash -$ scala helloInteractive +$ scala run helloInteractive.scala +Compiling project (Scala {{site.scala-3-version}}, JVM (20)) +Compiled project (Scala {{site.scala-3-version}}, JVM (20)) Please enter your name: Alvin Alexander Hello, Alvin Alexander! diff --git a/_overviews/scala3-book/taste-intro.md b/_overviews/scala3-book/taste-intro.md index 4820bdfe26..72f74faeed 100644 --- a/_overviews/scala3-book/taste-intro.md +++ b/_overviews/scala3-book/taste-intro.md @@ -23,4 +23,4 @@ can be installed by following our [getting started guide][get-started]. [reference]: {{ site.scala3ref }}/overview.html -[get-started]: {% link _overviews/getting-started/index.md %} +[get-started]: {% link _overviews/getting-started/install-scala.md %} diff --git a/_overviews/scala3-migration/tooling-scala2-xsource3.md b/_overviews/scala3-migration/tooling-scala2-xsource3.md index 63ecbb6e8b..e88f711c32 100644 --- a/_overviews/scala3-migration/tooling-scala2-xsource3.md +++ b/_overviews/scala3-migration/tooling-scala2-xsource3.md @@ -105,6 +105,7 @@ The following table shows backported Scala 3 language semantics available in `-X | `case-apply-copy-access`: modifiers of synthetic methods | fatal warning | constructor modifiers are used for apply / copy methods of case classes | | `case-companion-function`: companions are Functions | fatal warning at use site | synthetic case companion objects no longer extend FunctionN, but are adapted at use site with warning | | `infer-override`: override type inference | fatal warning | inferred type of member uses type of overridden member | +| `double-definitions`: definitions differing in empty parens 2 | fatal warning | double definition error | Example 1: @@ -114,9 +115,17 @@ Example 1: + 2 {% endhighlight %} +Example 2: + +{% highlight scala %} +class C(x: Int) { + def x(): Int = x // allowed in Scala 2, double definition error in Scala 3 +} +{% endhighlight %} + ### Changes affecting binary encoding -As of Scala 2.13.14, there are 3 changes in `-Xsource-features` that affect binary encoding of classfiles: +As of Scala 2.13.15, there are 3 changes in `-Xsource-features` that affect binary encoding of classfiles: 1. `case-apply-copy-access`: the constructor modifiers of case classes (`case class C private[p] (x: Int)`) are copied to the synthetic `apply` and `copy` methods. 1. `case-companion-function`: the synthetic companion objects of case classes no longer extend `FunctionN`. diff --git a/_overviews/toolkit/OrderedListOfMdFiles b/_overviews/toolkit/OrderedListOfMdFiles index ea248772fe..b2790bd58a 100644 --- a/_overviews/toolkit/OrderedListOfMdFiles +++ b/_overviews/toolkit/OrderedListOfMdFiles @@ -27,3 +27,10 @@ http-client-request-body.md http-client-json.md http-client-upload-file.md http-client-what-else.md +web-server-intro.md +web-server-static.md +web-server-dynamic.md +web-server-query-parameters.md +web-server-input.md +web-server-websockets.md +web-server-cookies-and-decorators.md diff --git a/_overviews/toolkit/http-client-what-else.md b/_overviews/toolkit/http-client-what-else.md index 11b577449d..865a031557 100644 --- a/_overviews/toolkit/http-client-what-else.md +++ b/_overviews/toolkit/http-client-what-else.md @@ -4,7 +4,7 @@ type: section description: An incomplete list of features of sttp num: 29 previous-page: http-client-upload-file -next-page: +next-page: web-server-intro --- {% include markdown.html path="_markdown/install-upickle.md" %} diff --git a/_overviews/toolkit/introduction.md b/_overviews/toolkit/introduction.md index 1656ed9662..9bc97cb2d1 100644 --- a/_overviews/toolkit/introduction.md +++ b/_overviews/toolkit/introduction.md @@ -22,6 +22,10 @@ toolkit-index: description: Sending HTTP requests and uploading files with sttp. icon: "fa fa-globe" link: /toolkit/http-client-intro.html + - title: Web servers + description: Building web servers with Cask. + icon: "fa fa-server" + link: /toolkit/web-server-intro.html --- ## What is the Scala Toolkit? diff --git a/_overviews/toolkit/web-server-cookies-and-decorators.md b/_overviews/toolkit/web-server-cookies-and-decorators.md new file mode 100644 index 0000000000..36caeac4de --- /dev/null +++ b/_overviews/toolkit/web-server-cookies-and-decorators.md @@ -0,0 +1,188 @@ +--- +title: How to use cookies and decorators? +type: section +description: Using cookies and decorators with Cask +num: 36 +previous-page: web-server-websockets +next-page: +--- + +{% include markdown.html path="_markdown/install-cask.md" %} + +## Using cookies + +Cookies are saved by adding them to the `cookies` parameter of the `cask.Response` constructor. + +In this example, we are building a rudimentary authentication service. The `getLogin` method provides a form where +the user can enter their username and password. The `postLogin` method reads the credentials. If they match the expected ones, it generates a session +identifier is generated, saves it in the application state, and sends back a cookie with the identifier. + +Cookies can be read either with a method parameter of `cask.Cookie` type or by accessing the `cask.Request` directly. +If using the former method, the names of parameters have to match the names of cookies. If a cookie with a matching name is not +found, an error response will be returned. In the `checkLogin` function, the former method is used, as the cookie is not +present before the user logs in. + +To delete a cookie, set its `expires` parameter to an instant in the past, for example `Instant.EPOCH`. + +{% tabs web-server-cookies-1 class=tabs-scala-version %} +{% tab 'Scala 2' %} + +```scala +import java.util.UUID +import java.util.concurrent.ConcurrentHashMap + +object Example extends cask.MainRoutes { + + val sessionIds = ConcurrentHashMap.newKeySet[String]() + + @cask.get("/login") + def getLogin(): cask.Response[String] = { + val html = + """ + | + | + |
+ |
+ |
+ |
+ |

+ | + |
+ | + |""".stripMargin + + cask.Response(data = html, headers = Seq("Content-Type" -> "text/html")) + } + + @cask.postForm("/login") + def postLogin(name: String, password: String): cask.Response[String] = { + if (name == "user" && password == "password") { + val sessionId = UUID.randomUUID().toString + sessionIds.add(sessionId) + cask.Response(data = "Success!", cookies = Seq(cask.Cookie("sessionId", sessionId))) + } else { + cask.Response(data = "Authentication failed", statusCode = 401) + } + } + + @cask.get("/check") + def checkLogin(request: cask.Request): String = { + val sessionId = request.cookies.get("sessionId") + if (sessionId.exists(cookie => sessionIds.contains(cookie.value))) { + "You are logged in" + } else { + "You are not logged in" + } + } + + @cask.get("/logout") + def logout(sessionId: cask.Cookie) = { + sessionIds.remove(sessionId.value) + cask.Response(data = "Successfully logged out!", cookies = Seq(cask.Cookie("sessionId", "", expires = Instant.EPOCH))) + } + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +import java.util.UUID +import java.util.concurrent.ConcurrentHashMap + +object Example extends cask.MainRoutes: + + val sessionIds = ConcurrentHashMap.newKeySet[String]() + + @cask.get("/login") + def getLogin(): cask.Response[String] = + val html = + """ + | + | + |
+ |
+ |
+ |
+ |

+ | + |
+ | + |""".stripMargin + + cask.Response(data = html, headers = Seq("Content-Type" -> "text/html")) + + @cask.postForm("/login") + def postLogin(name: String, password: String): cask.Response[String] = + if name == "user" && password == "password" then + val sessionId = UUID.randomUUID().toString + sessionIds.add(sessionId) + cask.Response(data = "Success!", cookies = Seq(cask.Cookie("sessionId", sessionId))) + else + cask.Response(data = "Authentication failed", statusCode = 401) + + @cask.get("/check") + def checkLogin(request: cask.Request): String = + val sessionId = request.cookies.get("sessionId") + if sessionId.exists(cookie => sessionIds.contains(cookie.value)) then + "You are logged in" + else + "You are not logged in" + + @cask.get("/logout") + def logout(sessionId: cask.Cookie): cask.Response[String] = + sessionIds.remove(sessionId.value) + cask.Response(data = "Successfully logged out!", cookies = Seq(cask.Cookie("sessionId", "", expires = Instant.EPOCH))) + + initialize() +``` +{% endtab %} +{% endtabs %} + +## Using decorators + +Decorators can be used for extending endpoints functionality with validation or new parameters. They are defined by extending +`cask.RawDecorator` class. They are used as annotations. + +In this example, the `loggedIn` decorator is used to check if the user is logged in before accessing the `/decorated` +endpoint. + +The decorator class can pass additional arguments to the decorated endpoint using a map. The passed arguments are available +through the last argument group. Here we are passing the session identifier to an argument named `sessionId`. + +{% tabs web-server-cookies-2 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +class loggedIn extends cask.RawDecorator { + override def wrapFunction(ctx: cask.Request, delegate: Delegate): Result[Raw] = { + ctx.cookies.get("sessionId") match { + case Some(cookie) if sessionIds.contains(cookie.value) => delegate(Map("sessionId" -> cookie.value)) + case _ => cask.router.Result.Success(cask.model.Response("You aren't logged in", 403)) + } + } +} + +@loggedIn() +@cask.get("/decorated") +def decorated()(sessionId: String): String = { + s"You are logged in with id: $sessionId" +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +class loggedIn extends cask.RawDecorator: + override def wrapFunction(ctx: cask.Request, delegate: Delegate): Result[Raw] = + ctx.cookies.get("sessionId") match + case Some(cookie) if sessionIds.contains(cookie.value) => + delegate(Map("sessionId" -> cookie.value)) + case _ => + cask.router.Result.Success(cask.model.Response("You aren't logged in", 403)) + + +@loggedIn() +@cask.get("/decorated") +def decorated()(sessionId: String): String = s"You are logged in with id: $sessionId" +``` +{% endtab %} +{% endtabs %} \ No newline at end of file diff --git a/_overviews/toolkit/web-server-dynamic.md b/_overviews/toolkit/web-server-dynamic.md new file mode 100644 index 0000000000..49101505c7 --- /dev/null +++ b/_overviews/toolkit/web-server-dynamic.md @@ -0,0 +1,237 @@ +--- +title: How to serve a dynamic page? +type: section +description: Serving a dynamic page with Cask +num: 32 +previous-page: web-server-static +next-page: web-server-query-parameters +--- + +{% include markdown.html path="_markdown/install-cask.md" %} + +## Serving dynamically generated content + +You can create an endpoint returning dynamically generated content with the `@cask.get` annotation. + +{% tabs web-server-dynamic-1 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +import java.time.ZonedDateTime + +object Example extends cask.MainRoutes { + @cask.get("/time") + def dynamic(): String = s"Current date is: ${ZonedDateTime.now()}" + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +import java.time.ZonedDateTime + +object Example extends cask.MainRoutes: + @cask.get("/time") + def dynamic(): String = s"Current date is: ${ZonedDateTime.now()}" + + initialize() +``` +{% endtab %} +{% endtabs %} + +The example above creates an endpoint that returns the current date and time available at `/time`. The exact response will be +recreated every time you refresh the webpage. + +Since the endpoint method has the `String` output type, the result will be sent with the `text/plain` [content type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type). +If you want an HTML output to be interpreted by the browser, you will need to set the `Content-Type` header manually +or [use the Scalatags templating library](/toolkit/web-server-dynamic.html#using-html-templates), supported by Cask. + +### Running the example + +Run the example the same way as before, assuming you use the same project structure as described in [the static file tutorial](/toolkit/web-server-static.html). + +{% tabs web-server-dynamic-2 class=tabs-build-tool %} +{% tab 'Scala CLI' %} +In the terminal, the following command will start the server: +``` +scala-cli run Example.scala +``` +{% endtab %} +{% tab 'sbt' %} +In the terminal, the following command will start the server: +``` +sbt example/run +``` +{% endtab %} +{% tab 'Mill' %} +In the terminal, the following command will start the server: +``` +./mill run +``` +{% endtab %} +{% endtabs %} + +Access the endpoint at [http://localhost:8080/time](http://localhost:8080/time). You should see a result similar to the one below. + +``` +Current date is: 2024-07-22T09:07:05.752534+02:00[Europe/Warsaw] +``` + +## Using path segments + +Cask gives you the ability to access segments of the URL path within the endpoint function. +Building on the example above, you can add a segment to specify that the endpoint should return the date and time +in a given city. + +{% tabs web-server-dynamic-3 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +import java.time.{ZoneId, ZonedDateTime} + +object Example extends cask.MainRoutes { + + private def getZoneIdForCity(city: String): Option[ZoneId] = { + import scala.jdk.CollectionConverters._ + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + } + + @cask.get("/time/:city") + def dynamicWithCity(city: String): String = { + getZoneIdForCity(city) match { + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + } + } + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +import java.time.{ZoneId, ZonedDateTime} + +object Example extends cask.MainRoutes: + + private def getZoneIdForCity(city: String): Option[ZoneId] = + import scala.jdk.CollectionConverters.* + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + + @cask.get("/time/:city") + def dynamicWithCity(city: String): String = + getZoneIdForCity(city) match + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + + initialize() +``` +{% endtab %} +{% endtabs %} + +In the example above, the `:city` segment in `/time/:city` is available through the `city` argument of the endpoint method. +The name of the argument must be identical to the segment name. The `getZoneIdForCity` helper method finds the timezone for +a given city, and then the current date and time are translated to that timezone. + +Accessing the endpoint at [http://localhost:8080/time/Paris](http://localhost:8080/time/Paris) will result in: +``` +Current date is: 2024-07-22T09:08:33.806259+02:00[Europe/Paris] +``` + +You can use more than one path segment in an endpoint by adding more arguments to the endpoint method. It's also possible to use paths +with an unspecified number of segments (for example `/path/foo/bar/baz/`) by giving the endpoint method an argument with `cask.RemainingPathSegments` type. +Consult the [documentation](https://com-lihaoyi.github.io/cask/index.html#variable-routes) for more details. + +## Using HTML templates + +To create an HTML response, you can combine Cask with the [Scalatags](https://com-lihaoyi.github.io/scalatags/) templating library. + +Import the Scalatags library: + +{% tabs web-server-dynamic-4 class=tabs-build-tool %} +{% tab 'Scala CLI' %} +Add the Scalatags dependency in `Example.sc` file: +```scala +//> using dep "com.lihaoyi::scalatags::0.13.1" +``` +{% endtab %} +{% tab 'sbt' %} +Add the Scalatags dependency in `build.sbt` file: +```scala +libraryDependencies += "com.lihaoyi" %% "scalatags" % "0.13.1" +``` +{% endtab %} +{% tab 'Mill' %} +Add the Scalatags dependency in `build.cs` file: +```scala +ivy"com.lihaoyi::scalatags::0.13.1" +``` +{% endtab %} +{% endtabs %} + +Now the example above can be rewritten to use a template. Cask will build a response out of the `doctype` automatically, +setting the `Content-Type` header to `text/html`. + +{% tabs web-server-dynamic-5 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +import java.time.{ZoneId, ZonedDateTime} +import scalatags.Text.all._ + +object Example extends cask.MainRoutes { + + private def getZoneIdForCity(city: String): Option[ZoneId] = { + import scala.jdk.CollectionConverters._ + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + } + + @cask.get("/time/:city") + def dynamicWithCity(city: String): doctype = { + val text = getZoneIdForCity(city) match { + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + } + + doctype("html")( + html( + body( + p(text) + ) + ) + ) + } + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +import java.time.{ZoneId, ZonedDateTime} +import scalatags.Text.all.* + +object Example extends cask.MainRoutes: + + private def getZoneIdForCity(city: String): Option[ZoneId] = + import scala.jdk.CollectionConverters.* + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + + @cask.get("/time/:city") + def dynamicWithCity(city: String): doctype = + val text = getZoneIdForCity(city) match + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + doctype("html")( + html( + body( + p(text) + ) + ) + ) + + initialize() +``` +{% endtab %} +{% endtabs %} + +Here we get the text of the response and wrap it in a Scalatags template. Notice that the return type changed from `String` +to `doctype`. \ No newline at end of file diff --git a/_overviews/toolkit/web-server-input.md b/_overviews/toolkit/web-server-input.md new file mode 100644 index 0000000000..8be4c659d3 --- /dev/null +++ b/_overviews/toolkit/web-server-input.md @@ -0,0 +1,243 @@ +--- +title: How to handle user input? +type: section +description: Handling user input with Cask +num: 34 +previous-page: web-server-query-parameters +next-page: web-server-websockets +--- + +{% include markdown.html path="_markdown/install-cask.md" %} + +## Handling form-encoded input + +To create an endpoint that handles the data provided in an HTML form, use the `@cask.postForm` annotation. Add arguments to the endpoint method +with names corresponding to names of fields in the form and set the form method to `post`. + +{% tabs web-server-input-1 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +object Example extends cask.MainRoutes { + + @cask.get("/form") + def getForm(): cask.Response[String] = { + val html = + """ + | + | + |
+ |
+ |
+ |
+ |

+ | + |
+ | + |""".stripMargin + + cask.Response(data = html, headers = Seq("Content-Type" -> "text/html")) + } + + @cask.postForm("/form") + def formEndpoint(name: String, surname: String): String = + "Hello " + name + " " + surname + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +object Example extends cask.MainRoutes: + + @cask.get("/form") + def getForm(): cask.Response[String] = + val html = + """ + | + | + |
+ |
+ |
+ |
+ |

+ | + |
+ | + |""".stripMargin + + cask.Response(data = html, headers = Seq("Content-Type" -> "text/html")) + + @cask.postForm("/form") + def formEndpoint(name: String, surname: String): String = + "Hello " + name + " " + surname + + initialize() +``` +{% endtab %} +{% endtabs %} + +In this example we create a form asking for name and surname of a user and then redirect the user to a greeting page. Notice the +use of `cask.Response`. The `cask.Response` type allows the user to set the status code, headers and cookies. The default +content type for an endpoint method returning a `String` is `text/plain`. Set it to `text/html` in order for the browser to display the form correctly. + +The `formEndpoint` endpoint reads the form data using the `name` and `surname` parameters. The names of parameters must +be identical to the field names of the form. + +## Handling JSON-encoded input + +JSON fields are handled in the same way as form fields, except that we use the `@cask.PostJson` annotation. The fields +will be read into the endpoint method arguments. + +{% tabs web-server-input-2 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +object Example extends cask.MainRoutes { + + @cask.postJson("/json") + def jsonEndpoint(name: String, surname: String): String = + "Hello " + name + " " + surname + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +object Example extends cask.MainRoutes: + + @cask.postJson("/json") + def jsonEndpoint(name: String, surname: String): String = + "Hello " + name + " " + surname + + initialize() +``` +{% endtab %} +{% endtabs %} + +Send the POST request using `curl`: + +```shell +curl --header "Content-Type: application/json" \ + --data '{"name":"John","surname":"Smith"}' \ + http://localhost:8080/json +``` + +The response will be: +``` +Hello John Smith +``` + +The endpoint will accept JSONs that have only the fields with names specified as the endpoint method arguments. If there +are more fields than expected, some fields are missing or have an incorrect data type, an error message +will be returned with the response code 400. + +To handle the case when the fields of the JSON are not known in advance, you can use an argument with the `ujson.Value` type, +from uPickle library. + +{% tabs web-server-input-3 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +object Example extends cask.MainRoutes { + + @cask.postJson("/json") + def jsonEndpoint(value: ujson.Value): String = + value.toString + + initialize() +} + +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +object Example extends cask.MainRoutes: + + @cask.postJson("/json") + def jsonEndpoint(value: ujson.Value): String = + value.toString + + initialize() + +``` +{% endtab %} +{% endtabs %} + +In this example the JSON is merely converted to `String`. Check the [*uPickle tutorial*](/toolkit/json-intro.html) for more information +on what can be done with the `ujson.Value` type. + +Send a POST request. +```shell +curl --header "Content-Type: application/json" \ + --data '{"value":{"name":"John","surname":"Smith"}}' \ + http://localhost:8080/json2 +``` + +The server will respond with: +``` +"{\"name\":\"John\",\"surname\":\"Smith\"}" +``` + +## Handling JSON-encoded output + +Cask endpoints can return JSON objects returned by uPickle library functions. Cask will automatically handle the `ujson.Value` +type and set the `Content-Type` header to `application/json`. + +In this example, the `TimeData` case class stores the information about the time zone and current time in a chosen +location. To serialize a case class into JSON, use type class derivation or define the serializer in its companion object in the case of Scala 2. + +{% tabs web-server-input-4 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +import java.time.{ZoneId, ZonedDateTime} + +object Example extends cask.MainRoutes { + import upickle.default.{ReadWriter, macroRW, writeJs} + case class TimeData(timezone: Option[String], time: String) + object TimeData { + implicit val rw: ReadWriter[TimeData] = macroRW + } + + private def getZoneIdForCity(city: String): Option[ZoneId] = { + import scala.jdk.CollectionConverters._ + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + } + + @cask.get("/time_json/:city") + def timeJSON(city: String): ujson.Value = { + val timezone = getZoneIdForCity(city) + val time = timezone match { + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + } + writeJs(TimeData(timezone.map(_.toString), time)) + } + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +import java.time.{ZoneId, ZonedDateTime} + +object Example extends cask.MainRoutes: + import upickle.default.{ReadWriter, writeJs} + case class TimeData(timezone: Option[String], time: String) derives ReadWriter + + private def getZoneIdForCity(city: String): Option[ZoneId] = + import scala.jdk.CollectionConverters.* + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + + @cask.get("/time_json/:city") + def timeJSON(city: String): ujson.Value = + val timezone = getZoneIdForCity(city) + val time = timezone match + case Some(zoneId)=> s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + writeJs(TimeData(timezone.map(_.toString), time)) + + initialize() +``` +{% endtab %} +{% endtabs %} \ No newline at end of file diff --git a/_overviews/toolkit/web-server-intro.md b/_overviews/toolkit/web-server-intro.md new file mode 100644 index 0000000000..4a1efcdc6a --- /dev/null +++ b/_overviews/toolkit/web-server-intro.md @@ -0,0 +1,23 @@ +--- +title: Building web servers with Cask +type: chapter +description: The introduction of the Cask library +num: 30 +previous-page: http-client-what-else +next-page: web-server-static +--- + +Cask is an HTTP micro-framework, providing a simple and flexible way to build web applications. + +Its main focus is on the ease of use, which makes it ideal for newcomers, at the cost of eschewing some features other +frameworks provide, like asynchronicity. + +To define an endpoint it's enough to annotate a function with an annotation specifying the request path. +Cask allows for building the response manually using tools that the library provides, specifying the content, headers, +status code, etc. An endpoint function can also return a string, a [uPickle](https://com-lihaoyi.github.io/upickle/) JSON type, or a [Scalatags](https://com-lihaoyi.github.io/scalatags/) +template. In that case, Cask will automatically create a response with the appropriate headers. + +Cask comes bundled with the uPickle library for handling JSONs, supports WebSockets and allows for extending endpoints with +decorators, which can be used to handle authentication or rate limiting. + +{% include markdown.html path="_markdown/install-cask.md" %} diff --git a/_overviews/toolkit/web-server-query-parameters.md b/_overviews/toolkit/web-server-query-parameters.md new file mode 100644 index 0000000000..8da1dc9ccc --- /dev/null +++ b/_overviews/toolkit/web-server-query-parameters.md @@ -0,0 +1,74 @@ +--- +title: How to handle query parameters? +type: section +description: Handling query parameters with Cask +num: 33 +previous-page: web-server-dynamic +next-page: web-server-input +--- + +{% include markdown.html path="_markdown/install-cask.md" %} + +Query parameters are the key-value pairs coming after the question mark in a URL. They can be used for filtering, +sorting or limiting the results provided by the server. For example, in the `/time?city=Paris` URL, the `city` part +is the name of a parameter, and `Paris` is its value. Cask allows for reading the query parameters by defining an endpoint +method with arguments matching the names of the expected parameters and not matching any of the URL segments. + +In this example, we give an `Option` type and the default value `None` to the `city` parameter. This tells Cask that it is optional. +If not provided, the time for the current timezone will be returned. + +{% tabs web-server-query-1 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +import java.time.{ZoneId, ZonedDateTime} + +object Example extends cask.MainRoutes { + + private def getZoneIdForCity(city: String): Option[ZoneId] = { + import scala.jdk.CollectionConverters._ + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + } + + @cask.get("/time") + def dynamicWithParam(city: Option[String] = None): String = { + city match { + case Some(value) => getZoneIdForCity(value) match { + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $value" + } + case None => s"Current date is: ${ZonedDateTime.now()}" + } + } + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +import java.time.{ZoneId, ZonedDateTime} + +object Example extends cask.MainRoutes: + + private def getZoneIdForCity(city: String): Option[ZoneId] = + import scala.jdk.CollectionConverters.* + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + + @cask.get("/time") + def dynamicWithParam(city: Option[String] = None): String = + city match + case Some(value) => getZoneIdForCity(value) match + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $value" + case None => s"Current date is: ${ZonedDateTime.now()}" + + initialize() +``` +{% endtab %} +{% endtabs %} + +Run the example as before and access the endpoint at [http://localhost:8080/time?city=Paris](http://localhost:8080/time?city=Paris). +You should get a result similar to the following one. +``` +Current date is: 2024-07-22T10:08:18.218736+02:00[Europe/Paris] +``` diff --git a/_overviews/toolkit/web-server-static.md b/_overviews/toolkit/web-server-static.md new file mode 100644 index 0000000000..780941efb0 --- /dev/null +++ b/_overviews/toolkit/web-server-static.md @@ -0,0 +1,159 @@ +--- +title: How to serve a static file? +type: section +description: Serving a static file with Cask +num: 31 +previous-page: web-server-intro +next-page: web-server-dynamic +--- + +{% include markdown.html path="_markdown/install-cask.md" %} + +## Serving a static file + +An endpoint is a specific URL where a particular webpage can be accessed. In Cask, an endpoint is a function returning the +webpage data, together with an annotation describing its URL. + +To create an endpoint serving static files, we need two things: an HTML file with the page content and a function that +points to that file. + +Create a minimal HTML file named `hello.html` with the following contents. + +```html + + + + Hello World + + +

Hello world

+ + +``` + +Place it in the `resources` directory. + +{% tabs web-server-static-1 class=tabs-build-tool %} +{% tab 'Scala CLI' %} +``` +example +├── Example.scala +└── resources + └── hello.html +``` +{% endtab %} +{% tab 'sbt' %} +``` +example +└──src + └── main + ├── resources + │ └── hello.html + └── scala + └── Example.scala +``` +{% endtab %} +{% tab 'Mill' %} +``` +example +├── src +│ └── Example.scala +└── resources + └── hello.html +``` +{% endtab %} +{% endtabs %} + +The `@cask.staticFiles` annotation specifies at which path the webpage will be available. The endpoint function returns +the location of the file. + +{% tabs web-server-static-2 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +object Example extends cask.MainRoutes { + @cask.staticFiles("/static") + def staticEndpoint(): String = "src/main/resources" // or "resources" if not using SBT + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +object Example extends cask.MainRoutes: + @cask.staticFiles("/static") + def staticEndpoint(): String = "src/main/resources" // or "resources" if not using SBT + + initialize() +``` +{% endtab %} +{% endtabs %} + +In the example above, `@cask.staticFiles` instructs the server to look for files accessed at the `/static` path in the +`src/main/resources` directory. Cask will match any subpath coming after `/static` and append it to the directory path. +If you access the `/static/hello.html` file, it will serve the file available at `src/main/resources/hello.html`. +The directory path can be any path available to the server, relative or not. If the requested file cannot be found in the +specified location, the server will return a 404 response with an error message. + +The `Example` object inherits from the `cask.MainRoutes` class. It provides the main function that starts the server. The `initialize()` +method call initializes the server routes, i.e., the association between URL paths and the code that handles them. + +### Using the resources directory + +The `@cask.staticResources` annotation works in the same way as the `@cask.staticFiles` used above, with the difference that +the path returned by the endpoint method describes the location of files _inside_ the resources directory. Since the +previous example conveniently used the resources directory, it can be simplified with `@cask.staticResources`. + +{% tabs web-server-static-3 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +object Example extends cask.MainRoutes { + @cask.staticResources("/static") + def staticEndpoint(): String = "." + + initialize() +} +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +object Example extends cask.MainRoutes: + @cask.staticResources("/static") + def staticEndpoint(): String = "." + + initialize() +``` +{% endtab %} +{% endtabs %} + +In the endpoint method, the location is set to `"."`, telling the server that the files are available directly in the +resources directory. In general, you can use any nested location within the resources directory. For instance, you could opt +for placing your HTML files in the `static` directory inside the resources directory or using different directories to sort out +files used by different endpoints. + +## Running the example + +Run the example with the build tool of your choice. + +{% tabs munit-unit-test-4 class=tabs-build-tool %} +{% tab 'Scala CLI' %} +In the terminal, the following command will start the server: +``` +scala run Example.scala +``` +{% endtab %} +{% tab 'sbt' %} +In the terminal, the following command will start the server: +``` +sbt example/run +``` +{% endtab %} +{% tab 'Mill' %} +In the terminal, the following command will start the server: +``` +./mill run +``` +{% endtab %} +{% endtabs %} + +The example page will be available at [http://localhost:8080/static/hello.html](http://localhost:8080/static/hello.html). diff --git a/_overviews/toolkit/web-server-websockets.md b/_overviews/toolkit/web-server-websockets.md new file mode 100644 index 0000000000..653bd7f154 --- /dev/null +++ b/_overviews/toolkit/web-server-websockets.md @@ -0,0 +1,118 @@ +--- +title: How to use websockets? +type: section +description: Using websockets with Cask +num: 35 +previous-page: web-server-input +next-page: web-server-cookies-and-decorators +--- + +{% include markdown.html path="_markdown/install-cask.md" %} + +You can create a WebSocket endpoint with the `@cask.websocket` annotation. The endpoint method should return a +`cask.WsHandler` instance defining how the communication should take place. It can also return a `cask.Response`, which rejects the +attempt at forming a WebSocket connection. + +The connection can also be closed by sending a `cask.Ws.close()` message through the WebSocket channel. + +Create an HTML file named `websockets.html` with the following content and place it in the `resources ` directory. + +```html + + + +
+ + +
+
+ + + +``` + +The JavaScript code opens a WebSocket connection using the `ws://localhost:8080/websocket` endpoint. The `ws.onmessage` +event handler is executed when the server pushes a message to the browser and `ws.onclose` when the connection is closed. + +Create an endpoint for serving static files using the `@cask.staticResources` annotation and an endpoint for handling +the WebSocket connection. + +{% tabs web-server-websocket-1 class=tabs-scala-version %} +{% tab 'Scala 2' %} +```scala +@cask.staticResources("/static") +def static() = "." + +private def getZoneIdForCity(city: String): Option[ZoneId] = { + import scala.jdk.CollectionConverters._ + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) +} + +@cask.websocket("/websocket") +def websocket(): cask.WsHandler = { + cask.WsHandler { channel => + cask.WsActor { + case cask.Ws.Text("") => channel.send(cask.Ws.Close()) + case cask.Ws.Text(city) => + val text = getZoneIdForCity(city) match { + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + } + channel.send(cask.Ws.Text(text)) + } + } +} + +initialize() +``` +{% endtab %} +{% tab 'Scala 3' %} +```scala +@cask.staticResources("/static") +def static() = "." + +private def getZoneIdForCity(city: String): Option[ZoneId] = + import scala.jdk.CollectionConverters.* + ZoneId.getAvailableZoneIds.asScala.find(_.endsWith("/" + city)).map(ZoneId.of) + +@cask.websocket("/websocket") +def websocket(): cask.WsHandler = + cask.WsHandler { channel => + cask.WsActor { + case cask.Ws.Text("") => channel.send(cask.Ws.Close()) + case cask.Ws.Text(city) => + val text = getZoneIdForCity(city) match + case Some(zoneId) => s"Current date is: ${ZonedDateTime.now().withZoneSameInstant(zoneId)}" + case None => s"Couldn't find time zone for city $city" + channel.send(cask.Ws.Text(text)) + } + } + +initialize() +``` +{% endtab %} +{% endtabs %} + +In the `cask.WsHandler` we define a `cask.WsActor`. It reacts to events (of type `cask.util.Ws.Event`) and uses the +WebSocket channel to send messages. In this example, we receive the name of a city and return the current time there. If the server +receives an empty message, the connection is closed. \ No newline at end of file diff --git a/_overviews/tutorials/scala-for-java-programmers.md b/_overviews/tutorials/scala-for-java-programmers.md index 7da45df6c6..ac31c42dd0 100644 --- a/_overviews/tutorials/scala-for-java-programmers.md +++ b/_overviews/tutorials/scala-for-java-programmers.md @@ -160,38 +160,49 @@ package, so can be accessed from anywhere in a program. > **Note:** The following assumes you are using Scala on the command line +If we save the above program in a file called +`HelloWorld.scala`, we can run it by issuing the following +command (the greater-than sign `>` represents the shell prompt +and should not be typed): + +```shell +> scala run HelloWorld.scala +``` + +The program will be automatically compiled (with compiled classes somewhere in the newly created `.scala-build` directory) +and executed, producing an output similar to: +``` +Compiling project (Scala {{site.scala-3-version}}, JVM (20)) +Compiled project (Scala {{site.scala-3-version}}, JVM (20)) +Hello, World! +``` + #### Compiling From the Command Line -To compile the example, we use `scalac`, the Scala compiler. `scalac` +To compile the example, we use `scala compile` command, which will invoke the Scala compiler, `scalac`. `scalac` works like most compilers: it takes a source file as argument, maybe some options, and produces one or several output files. The outputs it produces are standard Java class files. -If we save the above program in a file called -`HelloWorld.scala`, we can compile it by issuing the following -command (the greater-than sign `>` represents the shell prompt -and should not be typed): - ```shell -> scalac HelloWorld.scala +> scala compile HelloWorld.scala -d . ``` -This will generate a few class files in the current directory. One of +This will generate a few class files in the current directory (`-d` option sets the compilation output directory). One of them will be called `HelloWorld.class`, and contains a class which can be directly executed using the `scala` command, as the following section shows. #### Running From the Command Line -Once compiled, a Scala program can be run using the `scala` command. +Once compiled, the program can be run using the `scala run` command. Its usage is very similar to the `java` command used to run Java -programs, and accepts the same options. The above example can be +programs, and accepts similar options. The above example can be executed using the following command, which produces the expected output: ```shell -> scala -classpath . HelloWorld - +> scala run --main-class HelloWorld -classpath . Hello, World! ``` diff --git a/_overviews/tutorials/scala-on-android.md b/_overviews/tutorials/scala-on-android.md index 459e485ce8..49749aa85f 100644 --- a/_overviews/tutorials/scala-on-android.md +++ b/_overviews/tutorials/scala-on-android.md @@ -68,7 +68,7 @@ Make sure your `gcc` is at least version 6. [You can try following these steps]( #### The example app -if you reached this point and everything seems to work, it means you probably should be able to compile and run the example app called [HelloScala](https://github.com/makingthematrix/scalaonandroid/tree/main/helloscala). HelloScala is based on [HelloGluon](https://github.com/gluonhq/gluon-samples/tree/master/HelloGluon) from [Gluon samples](https://github.com/gluonhq/gluon-samples). Gluon is a company that maintains JavaFX and provides libraries that give us a layer of abstraction between our code and the device — be it desktop, Android, or iOS. It has some interesting implications: for example, you will see in the code that we check if we are on the desktop instead of Android, because if yes then we need to provide window size for our app. If we are on Android, we can just let the app’s window take the whole screen. If you decide to write something more complex with this tech stack, you will quickly see that you can use Gluon’s libraries and JavaFX (maybe together with [ScalaFX](http://www.scalafx.org/)) to achieve the same results other developers get by tinkering with Android SDK, while you are writing code that can be easily re-used on other platforms as well. +if you reached this point and everything seems to work, it means you probably should be able to compile and run the example app called [HelloScala](https://github.com/makingthematrix/scalaonandroid/tree/main/helloscala). HelloScala is based on [HelloGluon](https://github.com/gluonhq/gluon-samples/tree/master/HelloGluon) from [Gluon samples](https://github.com/gluonhq/gluon-samples). Gluon is a company that maintains JavaFX and provides libraries that give us a layer of abstraction between our code and the device — be it desktop, Android, or iOS. It has some interesting implications: for example, you will see in the code that we check if we are on the desktop instead of Android, because if yes then we need to provide window size for our app. If we are on Android, we can just let the app’s window take the whole screen. If you decide to write something more complex with this tech stack, you will quickly see that you can use Gluon’s libraries and JavaFX (maybe together with [ScalaFX](https://scalafx.github.io/)) to achieve the same results other developers get by tinkering with Android SDK, while you are writing code that can be easily re-used on other platforms as well. In the `pom.xml` of HelloScala you will find a list of plugins and dependencies our example app uses. Let’s take a look at some of them. @@ -136,7 +136,7 @@ If you managed to build one of the example apps and want to code something more - Install [Scene Builder](https://gluonhq.com/products/scene-builder/) and learn how to build GUI with it. Apart from the docs, you can find a lot of tutorials about it on YouTube. - Look through [Gluon’s documentation of Glisten and Attach](https://docs.gluonhq.com/) to learn how to make your app look better on a mobile device, and how to get access to your device’s features. - Download an example from [Gluon’s list of samples](https://docs.gluonhq.com/) and rewrite it to Scala. And when you do, let me know! -- Look into [ScalaFX](http://www.scalafx.org/) — a more declarative, Scala-idiomatic wrapper over JavaFX. +- Look into [ScalaFX](https://scalafx.github.io/) — a more declarative, Scala-idiomatic wrapper over JavaFX. - Download some other examples from [the “Scala on Android” repository on GitHub](https://github.com/makingthematrix/scalaonandroid). Contact me, if you write an example app of your own and want me to include it. - Join us on the official Scala discord — we have a [#scala-android channel](https://discord.gg/UuDawpq7) there. - There is also an [#android channel](https://discord.gg/XHMt6Yq4) on the “Learning Scala” discord. diff --git a/_pl/tour/automatic-closures.md b/_pl/tour/automatic-closures.md deleted file mode 100644 index 8251a6be35..0000000000 --- a/_pl/tour/automatic-closures.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -layout: tour -title: Automatyczna konstrukcja domknięć -partof: scala-tour - -num: 30 -language: pl -next-page: annotations -previous-page: operators ---- - -Scala pozwala na przekazywanie funkcji bezparametrycznych jako argumenty dla metod. Kiedy tego typu metoda jest wywołana, właściwe parametry dla funkcji bezparametrycznych nie są ewaluowane i przekazywana jest pusta funkcja, która enkapsuluje obliczenia odpowiadającego parametru (tzw. *wywołanie-przez-nazwę*). - -Poniższy kod demonstruje działanie tego mechanizmu: - -```scala mdoc -def whileLoop(cond: => Boolean)(body: => Unit): Unit = - if (cond) { - body - whileLoop(cond)(body) - } -var i = 10 -whileLoop (i > 0) { - println(i) - i -= 1 -} -``` - -Funkcja `whileLoop` pobiera dwa parametry: `cond` i `body`. Kiedy funkcja jest aplikowana, jej właściwe parametry nie są ewaluowane. Lecz gdy te parametry są wykorzystane w ciele `whileLoop`, zostanie ewaluowana niejawnie utworzona funkcja zwracająca ich prawdziwą wartość. Zatem metoda `whileLoop` implementuje rekursywnie pętlę while w stylu Javy. - -Możemy połączyć ze sobą wykorzystanie [operatorów infiksowych/postfiksowych](operators.html) z tym mechanizmem aby utworzyć bardziej złożone wyrażenia. - -Oto implementacja pętli w stylu wykonaj-dopóki: - -```scala mdoc:reset -def loop(body: => Unit): LoopUnlessCond = - new LoopUnlessCond(body) -protected class LoopUnlessCond(body: => Unit) { - def unless(cond: => Boolean): Unit = { - body - if (!cond) unless(cond) - } -} -var i = 10 -loop { - println("i = " + i) - i -= 1 -} unless (i == 0) -``` - -Funkcja `loop` przyjmuje ciało pętli oraz zwraca instancję klasy `LoopUnlessCond` (która enkapsuluje to ciało). Warto zwrócić uwagę, że ciało tej funkcji nie zostało jeszcze ewaluowane. Klasa `LoopUnlessCond` posiada metodę `unless`, którą możemy wykorzystać jako *operator infiksowy*. W ten sposób uzyskaliśmy całkiem naturalną składnię dla naszej nowej pętli: `loop { < stats > } unless ( < cond > )`. - -Oto wynik działania programu `TargetTest2`: - -``` -i = 10 -i = 9 -i = 8 -i = 7 -i = 6 -i = 5 -i = 4 -i = 3 -i = 2 -i = 1 -``` diff --git a/_pt-br/tour/annotations.md b/_pt-br/tour/annotations.md index 5d0842e42c..55d49cc441 100644 --- a/_pt-br/tour/annotations.md +++ b/_pt-br/tour/annotations.md @@ -5,7 +5,7 @@ partof: scala-tour num: 31 next-page: packages-and-imports -previous-page: automatic-closures +previous-page: operators language: pt-br --- diff --git a/_pt-br/tour/automatic-closures.md b/_pt-br/tour/automatic-closures.md deleted file mode 100644 index dfa5b9dd12..0000000000 --- a/_pt-br/tour/automatic-closures.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: tour -title: Construção Automática de Closures de Tipo-Dependente -partof: scala-tour - -num: 30 -next-page: annotations -previous-page: operators -language: pt-br ---- - -_Nota de tradução: A palavra `closure` em pode ser traduzida como encerramento/fechamento, porém é preferível utilizar a notação original_ - -Scala permite funções sem parâmetros como parâmetros de métodos. Quando um tal método é chamado, os parâmetros reais para nomes de função sem parâmetros não são avaliados e uma função nula é passada em vez disso, tal função encapsula a computação do parâmetro correspondente (isso é conhecido por avaliação *call-by-name*). - -O código a seguir demonstra esse mecanismo: - -```scala mdoc -def whileLoop(cond: => Boolean)(body: => Unit): Unit = - if (cond) { - body - whileLoop(cond)(body) - } -var i = 10 -whileLoop (i > 0) { - println(i) - i -= 1 -} -``` - -A função `whileLoop` recebe dois parâmetros: `cond` e `body`. Quando a função é aplicada, os parâmetros reais não são avaliados. Mas sempre que os parâmetros formais são usados no corpo de `whileLoop`, as funções nulas criadas implicitamente serão avaliadas em seu lugar. Assim, o nosso método `whileLoop` implementa um while-loop Java-like com um esquema de implementação recursiva. - -Podemos combinar o uso de [operadores infix/postfix](operators.html) com este mecanismo para criar declarações mais complexas (com uma sintaxe agradável). - -Aqui está a implementação de uma instrução que executa loop a menos que uma condição seja satisfeita: - -```scala mdoc:reset -def loop(body: => Unit): LoopUnlessCond = - new LoopUnlessCond(body) -protected class LoopUnlessCond(body: => Unit) { - def unless(cond: => Boolean): Unit = { - body - if (!cond) unless(cond) - } -} -var i = 10 -loop { - println("i = " + i) - i -= 1 -} unless (i == 0) -``` - -A função `loop` aceita apenas um corpo e retorna uma instância da classe` LoopUnlessCond` (que encapsula este objeto de corpo). Note que o corpo ainda não foi avaliado. A classe `LoopUnlessCond` tem um método `unless` que podemos usar como um *operador infix*. Dessa forma, obtemos uma sintaxe bastante natural para nosso novo loop: `loop { } unless ( )`. - -Aqui está a saída de quando o `TargetTest2` é executado: - -``` -i = 10 -i = 9 -i = 8 -i = 7 -i = 6 -i = 5 -i = 4 -i = 3 -i = 2 -i = 1 -``` diff --git a/_pt-br/tour/operators.md b/_pt-br/tour/operators.md index b38e0e3105..627b968e73 100644 --- a/_pt-br/tour/operators.md +++ b/_pt-br/tour/operators.md @@ -4,7 +4,7 @@ title: Operadores partof: scala-tour num: 29 -next-page: automatic-closures +next-page: annotations previous-page: type-inference language: pt-br --- diff --git a/_pt-br/tour/tour-of-scala.md b/_pt-br/tour/tour-of-scala.md index a455ddd001..970b47d0c0 100644 --- a/_pt-br/tour/tour-of-scala.md +++ b/_pt-br/tour/tour-of-scala.md @@ -38,7 +38,6 @@ Um [mecanismo de inferência de tipo local](type-inference.html) se encarrega pa Na prática, o desenvolvimento de aplicações de um determinado domínio geralmente requer uma linguagem de domínio específico. Scala fornece uma combinação única de mecanismos de linguagem que facilitam a adição suave de novas construções de linguagem na forma de bibliotecas: * qualquer método pode ser utilizado como um [operador infix ou postfix](operators.html) -* [closures são construídas automaticamente dependendo do tipo esperado](automatic-closures.html) (tipo alvo). Uma utilização conjunta de ambos os recursos facilita a definição de novas instruções sem estender a sintaxe e sem usar meta-programação como macros. diff --git a/_ru/getting-started/index.md b/_ru/getting-started/install-scala.md similarity index 99% rename from _ru/getting-started/index.md rename to _ru/getting-started/install-scala.md index e9d8e0beeb..f6968753f5 100644 --- a/_ru/getting-started/index.md +++ b/_ru/getting-started/install-scala.md @@ -90,7 +90,8 @@ newcomer_resources: Проверьте корректность установки с помощью команды `scala -version`, которая должна вывести: ```bash $ scala -version -Scala code runner version {{site.scala-3-version}} -- Copyright 2002-2022, LAMP/EPFL +Scala code runner version: 1.4.3 +Scala version (default): {{site.scala-3-version}} ``` Если сообщение не выдано, возможно, необходимо перезайти в терминал (или перезагрузиться), чтобы изменения вступили в силу. diff --git a/_ru/index.md b/_ru/index.md index df628955a5..7ac8c2a455 100644 --- a/_ru/index.md +++ b/_ru/index.md @@ -11,7 +11,7 @@ sections: - title: "Приступая к работе" description: "Установите Scala на свой компьютер и начните писать код на Scala!" icon: "fa fa-rocket" - link: /ru/getting-started/index.html + link: /ru/getting-started/install-scala.html - title: "Тур по Scala" description: "Вступительный обзор по основным возможностям языка." icon: "fa fa-flag" diff --git a/_ru/scala3/book/taste-intro.md b/_ru/scala3/book/taste-intro.md index 74f5996a5b..58e15d8e22 100644 --- a/_ru/scala3/book/taste-intro.md +++ b/_ru/scala3/book/taste-intro.md @@ -28,4 +28,4 @@ next-page: taste-hello-world [reference]: {{ site.scala3ref }}/overview.html -[get-started]: {% link _overviews/getting-started/index.md %} +[get-started]: {% link _overviews/getting-started/install-scala.md %} diff --git a/_ru/scala3/book/tools-sbt.md b/_ru/scala3/book/tools-sbt.md index 6dd330c28e..be70b9b050 100644 --- a/_ru/scala3/book/tools-sbt.md +++ b/_ru/scala3/book/tools-sbt.md @@ -485,4 +485,4 @@ sbt:HelloScalaTest> test - [The sbt documentation](https://www.scala-sbt.org/1.x/docs/) - [The ScalaTest website](https://www.scalatest.org/) -[getting_started]: {{ site.baseurl }}/ru/getting-started/index.html +[getting_started]: {{ site.baseurl }}/ru/getting-started/install-scala.html diff --git a/_ru/tour/automatic-closures.md b/_ru/tour/automatic-closures.md deleted file mode 100644 index 8f0a8d4451..0000000000 --- a/_ru/tour/automatic-closures.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: tour -title: Конструкция Автоматического Замыкания Зависимого Типа -language: ru -partof: scala-tour -num: 14 ---- - -Scala допускает использование в качестве параметров методов имена беспараметрических функций. При вызове такого метода фактические параметры для беспараметрических функций не вычисляются, а передается функция с нулем аргументов, которая захватывает вычисление соответствующего параметра (так называемый *вызов по имени*). - -Следующий код демонстрирует этот механизм: - - object TargetTest1 extends Application { - def whileLoop(cond: => Boolean)(body: => Unit): Unit = - if (cond) { - body - whileLoop(cond)(body) - } - var i = 10 - whileLoop (i > 0) { - println(i) - i -= 1 - } - } - -Функция whileLoop принимает два параметра `cond` и `body`. При использовании функции значения этих параметров не вычисляются. Но всякий раз, когда параметры используются в теле `whileLoop`, их значение будет вычисляться заново через использование автоматически созданных неявно вызываемых функций. Таким образом, наш метод `whileLoop` реализует Java-подобный цикл while-loop со схемой рекурсивной реализации. - -Мы можем комбинировать использование [инфиксных/постфиксных операторов](operators.html) с этим механизмом для создания более сложных выражений (с хорошим синтаксисом). - -Вот реализация loop-unless выражения: - - object TargetTest2 extends Application { - def loop(body: => Unit): LoopUnlessCond = - new LoopUnlessCond(body) - protected class LoopUnlessCond(body: => Unit) { - def unless(cond: => Boolean): Unit = { - body - if (!cond) unless(cond) - } - } - var i = 10 - loop { - println("i = " + i) - i -= 1 - } unless (i == 0) - } -Функция `loop` принимает только тело цикла и возвращает экземпляр класса `LoopUnlessCond` (который захватывает это тело цикла). Обратите внимание, что тело еще не вычислено. Класс `LoopUnlessCond` имеет метод `unless`, который мы можем использовать как *инфиксный оператор*. Таким образом, мы получаем вполне естественный синтаксис для нашего нового цикла: `loop { < выражение > } unless ( < условие > )`. - - -Ниже приведен вывод выполнения `TargetTest2`: - - i = 10 - i = 9 - i = 8 - i = 7 - i = 6 - i = 5 - i = 4 - i = 3 - i = 2 - i = 1 diff --git a/_sips/process-specification.md b/_sips/process-specification.md index 8263fae131..93a15ce812 100644 --- a/_sips/process-specification.md +++ b/_sips/process-specification.md @@ -266,12 +266,11 @@ The current committee members are: - Lukas Rytz ([@lrytz](https://github.com/lrytz)), Lightbend - Martin Odersky ([@odersky](https://github.com/odersky)), EPFL - Oron Port ([@soronpo](https://github.com/soronpo)), DFiant Inc -- Paweł Marks ([@Kordyjan](https://github.com/Kordyjan)), VirtusLab - Sébastien Doeraene ([@sjrd](https://github.com/sjrd)), Scala Center The current Chairperson is: -- Anatolii Kmetiuk ([@Toli](https://github.com/anatoliykmetyuk)), Scala Center +- Dimi Racordon ([@kyouko-taiga](https://github.com/kyouko-taiga)), EPFL The current Secretary is: diff --git a/_sips/sips/implicit-macro-conversions.md b/_sips/sips/implicit-macro-conversions.md new file mode 100644 index 0000000000..82e7ab2ff5 --- /dev/null +++ b/_sips/sips/implicit-macro-conversions.md @@ -0,0 +1,7 @@ +--- +title: SIP-66 - Implicit macro conversions +status: under-review +pull-request-number: 86 +stage: design + +--- diff --git a/_sips/sips/named-tuples.md b/_sips/sips/named-tuples.md index fd20742737..db240571a4 100644 --- a/_sips/sips/named-tuples.md +++ b/_sips/sips/named-tuples.md @@ -230,7 +230,7 @@ and singleton types with case classes as underlying type (in terms of the implem We allow named patterns not just for named tuples but also for case classes. For instance: ```scala city match - case c @ City(name = "London") => println(p.population) + case c @ City(name = "London") => println(c.population) case City(name = n, zip = 1026, population = pop) => println(pop) ``` diff --git a/_sips/sips/typeclasses-syntax.md b/_sips/sips/typeclasses-syntax.md index 0beed4b3e1..3fb3b807fd 100644 --- a/_sips/sips/typeclasses-syntax.md +++ b/_sips/sips/typeclasses-syntax.md @@ -13,7 +13,7 @@ title: SIP-64 - Improve Syntax for Context Bounds and Givens | Date | Version | |---------------|--------------------| | March 11, 2024| Initial Draft | -| July 18, 2014 | Revised Draft | +| July 18, 2024 | Revised Draft | ## Summary @@ -95,7 +95,7 @@ Aggregate context bounds like `A : X : Y` are not obvious to read, and it become to import clauses. Example: ```scala - trait: + trait A: def showMax[X : {Ordering, Show}](x: X, y: X): String class B extends A: def showMax[X : {Ordering as ordering, Show as show}](x: X, y: X): String = diff --git a/_th/cheatsheets/index.md b/_th/cheatsheets/index.md index 49861d3f0c..b59cf817a3 100644 --- a/_th/cheatsheets/index.md +++ b/_th/cheatsheets/index.md @@ -66,9 +66,9 @@ language: "th" | `for (i <- 1 until 5) {`
`println(i)`
`}` | ทำความเข้าใจ for : ทำซ้ำโดยละเว้นขอบเขตบน | | จับคู่รูปแบบ | | | Good
`(xs zip ys) map { case (x,y) => x*y }`
Bad
`(xs zip ys) map( (x,y) => x*y )` | ใช้ case ใน arg ของฟังก์ชันสำหรับ จับคู่รูปแบบ (pattern maching) | -| Bad
`val v42 = 42`
`Some(3) match {`
` case Some(v42) => println("42")`
` case _ => println("Not 42")`
`}` | "v42" ถูกตีความว่าเป็นชื่อที่ตรงกับค่า Int และพิมพ์ "42" | -| Good
`val v42 = 42`
`Some(3) match {`
`` case Some(`v42`) => println("42")``
`case _ => println("Not 42")`
`}` | "v42" กับ backticks ถูกตีความว่าเป็น v42 val ที่มีอยู่และ
พิมพ์ "Not 42" | -| Good
`val UppercaseVal = 42`
`Some(3) match {`
` case Some(UppercaseVal) => println("42")`
` case _ => println("Not 42")`
`}` | UppercaseVal ถือว่าเป็น val ที่มีอยู่ไม่ใช่ตัวแปรรูปแบบใหม่
เพราะมันเริ่มต้นด้วยตัวอักษรตัวพิมพ์ใหญ่ ดังนั้นค่าที่มีอยู่ใน
UppercaseVal จะถูกตรวจสอบเทียบกับ 3 และพิมพ์ "Not 42" | +| Bad
`val v42 = 42`
`Some(3) match {`
` case Some(v42) => println("42")`
` case _ => println("Not 42")`
`}` | "v42" ถูกตีความว่าเป็นชื่อที่ตรงกับค่า Int และแสดงค่า "42" | +| Good
`val v42 = 42`
`Some(3) match {`
`` case Some(`v42`) => println("42")``
`case _ => println("Not 42")`
`}` | "v42" กับ backticks ถูกตีความว่าเป็น v42 val ที่มีอยู่และ
แสดงค่า "Not 42" | +| Good
`val UppercaseVal = 42`
`Some(3) match {`
` case Some(UppercaseVal) => println("42")`
` case _ => println("Not 42")`
`}` | UppercaseVal ถือว่าเป็น val ที่มีอยู่ไม่ใช่ตัวแปรรูปแบบใหม่
เพราะมันเริ่มต้นด้วยตัวอักษรตัวพิมพ์ใหญ่ ดังนั้นค่าที่มีอยู่ใน
UppercaseVal จะถูกตรวจสอบเทียบกับ 3 และแสดงค่า "Not 42" | | การใช้งาน object | | | `class C(x: R)` _เหมือนกับ_
`class C(private val x: R)`
`var c = new C(4)` | ตัวสร้างพารามิเตอร์ - x มีเฉพาะในคลาส body เท่านั้น | | `class C(val x: R)`
`var c = new C(4)`
`c.x` | ตัวสร้างพารามิเตอร์ - กำหนดสมาชิกสาธารณะโดยอัตโนมัติ | diff --git a/_th/tour/automatic-closures.md b/_th/tour/automatic-closures.md deleted file mode 100644 index 1c30b693a9..0000000000 --- a/_th/tour/automatic-closures.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: tour -title: Automatic Closures -partof: scala-tour - -language: th ---- diff --git a/_th/tour/classes.md b/_th/tour/classes.md index 7800a5f492..3054dbcc8d 100644 --- a/_th/tour/classes.md +++ b/_th/tour/classes.md @@ -11,7 +11,7 @@ next-page: traits previous-page: unified-types --- -คลาสใน Scala เป็นพิมพ์เขียวสำหรับสร้าง object ในคลาสสามารถมี method, value, ตัวแปร, type, object, +คลาสใน Scala เป็นพิมพ์เขียวสำหรับสร้าง object ในคลาสสามารถมี method, value, ตัวแปร, type, object, trait และคลาส ซึ่งเรียกรวมๆ กันว่า _members_ หรือ _สมาชิก_ ของคลาส type, object และ trait จะกล่าวถึงภายหลัง ## การกำหนดคลาส @@ -22,7 +22,7 @@ class User val user1 = new User ``` -keyword `new` ใช้เพื่อสร้าง instance ของคลาส `User` มี constructor เริ่มต้นซึ่งไม่รับค่า argument เพราะว่าไม่ได้กำหนด constructor ไว้ตอนประกาสคลาส +keyword `new` ใช้เพื่อสร้าง instance ของคลาส `User` มี constructor เริ่มต้นซึ่งไม่รับค่า argument เพราะว่าไม่ได้กำหนด constructor ไว้ตอนประกาสคลาส อย่างไรก็ตาม, เราอาจจะมี constructor และ body ของคลาส ตัวอย่างดังนี้ เป็นการประกาศคลาสสำหรับจุด (point): @@ -41,11 +41,11 @@ class Point(var x: Int, var y: Int) { val point1 = new Point(2, 3) point1.x // 2 -println(point1) // พิมพ์ (2, 3) +println(point1) // แสดงค่า (2, 3) ``` คลาส `Point` นี้มีสมาชิก 4 ตัว คือ ตัวแปร `x` และ `y` และ method `move` และ `toString` -ไม่เหมือนภาษาอื่นๆ, ซึ่ง constructor หลักจะอยู่ใน class signature `(var x: Int, var y: Int)` +ไม่เหมือนภาษาอื่นๆ, ซึ่ง constructor หลักจะอยู่ใน class signature `(var x: Int, var y: Int)` method `move` รับ argument ชนิดตัวเลข 2 ตัว และ return เป็นค่า Unit `()` ซึ่งไม่มีค่า จะมีผลลัพธ์คลายกับ `void` ในภาษาที่เหมือน Java, `toString` ในทางกลับกัน ไม่รับ argument ใดๆ แต่ return เป็นค่า `String` ซึ่งแทนที่ method `toString` จาก [`AnyRef`](unified-types.html) โดยจะมี keyword `override` @@ -58,7 +58,7 @@ class Point(var x: Int = 0, var y: Int = 0) val origin = new Point // x and y are both set to 0 val point1 = new Point(1) -println(point1.x) // พิมพ์ 1 +println(point1.x) // แสดงค่า 1 ``` @@ -68,13 +68,13 @@ println(point1.x) // พิมพ์ 1 ```scala mdoc:nest class Point(var x: Int = 0, var y: Int = 0) val point2 = new Point(y=2) -println(point2.y) // พิมพ์ 2 +println(point2.y) // แสดงค่า 2 ``` นี้เป็นวิธีปฏิบัติที่ดีเพื่อจะทำให้โค้ดชัดเจนมากขึ้น ## Private Members และ Getter/Setter -สมาชิกของคลาสจะเป็น public โดยค่าเริ่มต้น ใช้ access modifier `private` +สมาชิกของคลาสจะเป็น public โดยค่าเริ่มต้น ใช้ access modifier `private` เพื่อซ่อนสมาชิกนั้นจากภายนอกของคลาส ```scala mdoc:reset class Point { @@ -97,10 +97,10 @@ class Point { val point1 = new Point point1.x = 99 -point1.y = 101 // พิมพ์คำเตือน warning +point1.y = 101 // แสดงค่า "WARNING: Out of bounds" ``` -คลาส `Point` เวอร์ชันนี้ ข้อมูลจะถูกเก็บไว้ในตัวแปรชนิด private ที่ชื่อว่า `_x` และ `_y` และมี method ที่ชื่อว่า `def x` และ `def y` ทีจะใช้ในการเข้าถึงข้อมูล private เป็น getter, `def x_=` และ `def y=` -เป็น method สำหรับตรวจสอบข้อมูลและ setting ค่าของตัวแปร `_x` และ `_y` +คลาส `Point` เวอร์ชันนี้ ข้อมูลจะถูกเก็บไว้ในตัวแปรชนิด private ที่ชื่อว่า `_x` และ `_y` และมี method ที่ชื่อว่า `def x` และ `def y` ทีจะใช้ในการเข้าถึงข้อมูล private เป็น getter, `def x_=` และ `def y=` +เป็น method สำหรับตรวจสอบข้อมูลและ setting ค่าของตัวแปร `_x` และ `_y` สังเกตว่า syntax พิเศษนี้สำหรับ setter: คือ method ที่ตามด้วย `_=` ไปยังตัวระบุของ setter และ parameter ตามหลังมา constructor หลักกำหนด parameter ด้วย `val` และ `var` เป็น public อย่างไรก็ตามเพราะว่า `val` เป็นตัวแปรที่เปลี่ยนแปลงไม่ได้ (immutable) เราไม่สามารถเขียบแบบนี้ได้ diff --git a/_th/tour/named-arguments.md b/_th/tour/named-arguments.md index 161b53b50e..0257732c5d 100644 --- a/_th/tour/named-arguments.md +++ b/_th/tour/named-arguments.md @@ -10,3 +10,56 @@ language: th next-page: packages-and-imports previous-page: default-parameter-values --- + +เมื่อเราเรียกใช้ method แล้วเราสามารถระบุชื่อ argument (label the argument) สำหรับ parameter ใดๆ ได้ดังนี้: + +{% tabs named-arguments-when-good %} + +{% tab 'Scala 2 and 3' for=named-arguments-when-good %} + +```scala mdoc +def printName(first: String, last: String): Unit = + println(s"$first $last") + +printName("John", "Public") // แสดงค่า "John Public" +printName(first = "John", last = "Public") // แสดงค่า "John Public" +printName(last = "Public", first = "John") // แสดงค่า "John Public" +printName("Elton", last = "John") // แสดงค่า "Elton John" +``` + +{% endtab %} + +{% endtabs %} + +named argument นั้นมีประโยชน์เมื่อ parameter 2 ตัวมี type เดียวกัน\ +ทำให้ argument ที่เราส่งไปให้ function อาจถูกสลับกันโดยไม่ได้ตั้งใจ + +สังเกตว่าเราจะเขียน argument ที่ระบุชื่อในลำดับใดก็ได้\ +แต่ถ้า argument ไม่ได้อยู่ในลำดับของ parameter ใน function จากซ้ายไปขวา แล้ว argument ที่เหลือจะต้องระบุชื่อทั้งหมด + +ในตัวอย่างข้างล่างนี้ named argument ทำให้เราสามารถเว้น parameter `middle` ได้\ +แต่ในกรณีที่เกิด `error: positional after named argument`\ +เนื่องจาก argument ตัวแรกไม่ได้เรียงตามลำดับของ parameter (ตัวแรกไม่ใช่ parameter `first` และ argument ตัวที่ 2 เป็นต้นไปก็ไม่ได้ระบุชื่อด้วย)\ +ดังนั้น เราจะต้องระบุชื่อ argument ตั้งแต่ตัวที่ 2 เป็นต้นไป + +{% tabs named-arguments-when-error %} + +{% tab 'Scala 2 and 3' for=named-arguments-when-error %} + +```scala mdoc:fail +def printFullName(first: String, middle: String = "Q.", last: String): Unit = + println(s"$first $middle $last") + +printFullName(first = "John", last = "Public") // แสดงค่า "John Q. Public" +printFullName("John", last = "Public") // แสดงค่า "John Q. Public" +printFullName("John", middle = "Quincy", "Public") // แสดงค่า "John Quincy Public" +printFullName(last = "Public", first = "John") // แสดงค่า "John Q. Public" +printFullName(last = "Public", "John") // error: positional after named argument +``` + +{% endtab %} + +{% endtabs %} + +เราสามารถใช้ Named Argument กับการเรียกใช้ method ของ Java ได้\ +แต่ทำได้เฉพาะในกรณีที่ Java library นั้นถูกคอมไพล์ด้วยออพชั่น `-parameters` เท่านั้น diff --git a/_th/tour/traits.md b/_th/tour/traits.md index dfc3b31a74..92dc07597c 100644 --- a/_th/tour/traits.md +++ b/_th/tour/traits.md @@ -75,7 +75,7 @@ val cat = new Cat("Sally") val animals = ArrayBuffer.empty[Pet] animals.append(dog) animals.append(cat) -animals.foreach(pet => println(pet.name)) // พิมพ์ Harry Sally +animals.foreach(pet => println(pet.name)) // แสดงค่า Harry Sally ``` -`trait Pet` มี abstract field `name` ซึ่ง implement โดย Cat และ Dog ใน constructor ของมัน +`trait Pet` มี abstract field `name` ซึ่ง implement โดย Cat และ Dog ใน constructor ของมัน ในบรรทัดสุดท้าย เราเรียก `pet.name` ซึ่งจะต้องถูก implement แล้วใน subtype ใดๆ ของ trait `Pet` diff --git a/_tour/automatic-closures.md b/_tour/automatic-closures.md deleted file mode 100644 index 2a3d889d35..0000000000 --- a/_tour/automatic-closures.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: tour -title: Automatic Type-Dependent Closure Construction -partof: scala-tour - -redirect_from: "/tutorials/tour/automatic-closures.html" ---- - -Scala allows parameterless function names as parameters of methods. When such a method is called, the actual parameters for parameterless function names are not evaluated and a nullary function is passed instead which encapsulates the computation of the corresponding parameter (so-called *call-by-name* evaluation). - -The following code demonstrates this mechanism: - -```scala mdoc -def whileLoop(cond: => Boolean)(body: => Unit): Unit = - if (cond) { - body - whileLoop(cond)(body) - } -var i = 10 -whileLoop (i > 0) { - println(i) - i -= 1 -} -``` - -The function whileLoop takes two parameters `cond` and `body`. When the function is applied, the actual parameters do not get evaluated. But whenever the formal parameters are used in the body of `whileLoop`, the implicitly created nullary functions will be evaluated instead. Thus, our method `whileLoop` implements a Java-like while-loop with a recursive implementation scheme. - -We can combine the use of [infix/postfix operators](operators.html) with this mechanism to create more complex statements (with a nice syntax). - -Here is the implementation of a loop-unless statement: - -```scala mdoc:reset -def loop(body: => Unit): LoopUnlessCond = - new LoopUnlessCond(body) -protected class LoopUnlessCond(body: => Unit) { - def unless(cond: => Boolean): Unit = { - body - if (!cond) unless(cond) - } -} -var i = 10 -loop { - println("i = " + i) - i -= 1 -} unless (i == 0) -``` - -The `loop` function just accepts a body of a loop and returns an instance of class `LoopUnlessCond` (which encapsulates this body object). Note that the body didn't get evaluated yet. Class `LoopUnlessCond` has a method `unless` which we can use as a *infix operator*. This way, we achieve a quite natural syntax for our new loop: `loop { < stats > } unless ( < cond > )`. - -Here's the output when `TargetTest2` gets executed: - - i = 10 - i = 9 - i = 8 - i = 7 - i = 6 - i = 5 - i = 4 - i = 3 - i = 2 - i = 1 diff --git a/_tour/by-name-parameters.md b/_tour/by-name-parameters.md index b8318783f4..441aefa597 100644 --- a/_tour/by-name-parameters.md +++ b/_tour/by-name-parameters.md @@ -8,6 +8,7 @@ next-page: annotations previous-page: operators redirect_from: "/tutorials/tour/by-name-parameters.html" +redirect_from: "/tutorials/tour/automatic-closures.html" --- _By-name parameters_ are evaluated every time they are used. They won't be evaluated at all if they are unused. This is similar to replacing the by-name parameters with the passed expressions. They are in contrast to _by-value parameters_. To make a parameter called by-name, simply prepend `=>` to its type. diff --git a/_tour/pattern-matching.md b/_tour/pattern-matching.md index 1038e6be13..a89c16775f 100644 --- a/_tour/pattern-matching.md +++ b/_tour/pattern-matching.md @@ -236,6 +236,27 @@ def goIdle(device: Device): String = device match `def goIdle` has a different behavior depending on the type of `Device`. This is useful when the case needs to call a method on the pattern. It is a convention to use the first letter of the type as the case identifier (`p` and `c` in this case). +## Binding matched patterns to variables +You can use variable binding to get type-dependent behavior while simultaneously extracting fields from the matched pattern. + +{% tabs pattern-matching-variable-binding class=tabs-scala-version %} +{% tab 'Scala 2' for=pattern-matching-variable-binding %} +```scala mdoc +def goIdleWithModel(device: Device): String = device match { + case p @ Phone(model) => s"$model: ${p.screenOff}" + case c @ Computer(model) => s"$model: ${c.screenSaverOn}" +} +``` +{% endtab %} +{% tab 'Scala 3' for=pattern-matching-variable-binding %} +```scala +def goIdleWithModel(device: Device): String = device match + case p @ Phone(model) => s"$model: ${p.screenOff}" + case c @ Computer(model) => s"$model: ${c.screenSaverOn}" +``` +{% endtab %} +{% endtabs %} + ## Sealed types You may have noticed that in the examples above the base types are qualified diff --git a/_tour/unified-types.md b/_tour/unified-types.md index 191dc28c1e..a6f0e9c0dd 100644 --- a/_tour/unified-types.md +++ b/_tour/unified-types.md @@ -57,6 +57,8 @@ true Value types can be cast in the following way: Scala Type Hierarchy +Note that `Long` to `Float` conversion is deprecated in new versions of Scala, because of the potential precision lost. + For example: {% tabs unified-types-2 %} diff --git a/_uk/getting-started/index.md b/_uk/getting-started/install-scala.md similarity index 99% rename from _uk/getting-started/index.md rename to _uk/getting-started/install-scala.md index e8275ada94..9725b362e9 100644 --- a/_uk/getting-started/index.md +++ b/_uk/getting-started/install-scala.md @@ -75,7 +75,8 @@ _Scastie_ це онлайн “пісочниця”, де ви можете е Перевірте ваші налаштування виконавши команду `scala -version`, яка має вивести: ```bash $ scala -version -Scala code runner version {{site.scala-3-version}} -- Copyright 2002-2022, LAMP/EPFL +Scala code runner version: 1.4.3 +Scala version (default): {{site.scala-3-version}} ``` Якщо це не спрацювало, необхідно завершити сеанс та зайти в систему знову (або перезавантажити), щоб зміни застосувались на вашій системі. {% endaltDetails %} diff --git a/_uk/index.md b/_uk/index.md index 375daaba5d..6242b6e807 100644 --- a/_uk/index.md +++ b/_uk/index.md @@ -16,7 +16,7 @@ sections: - title: "Початок роботи" description: "Встанови Scala на свій комп'ютер і почни писати код Scala!" icon: "fa fa-rocket" - link: /uk/getting-started/index.html + link: /uk/getting-started/install-scala.html - title: "Екскурсія по Скала" description: "Короткі введення в основні особливості мови." icon: "fa fa-flag" diff --git a/_zh-cn/overviews/scala3-book/taste-intro.md b/_zh-cn/overviews/scala3-book/taste-intro.md index 1c0a5a6c3d..0d2f0fdf50 100644 --- a/_zh-cn/overviews/scala3-book/taste-intro.md +++ b/_zh-cn/overviews/scala3-book/taste-intro.md @@ -24,4 +24,4 @@ permalink: "/zh-cn/scala3/book/:title.html" > 或者,您可以使用 [Scastie](https://scastie.scala-lang.org) 在 Web 浏览器中运行这些示例,这是一个完全在线的编辑器和 Scala 的代码运行器。 [reference]: {{ site.scala3ref }}/overview.html -[get-started]: {% link _overviews/getting-started/index.md %} +[get-started]: {% link _overviews/getting-started/install-scala.md %} diff --git a/_zh-cn/tour/automatic-closures.md b/_zh-cn/tour/automatic-closures.md deleted file mode 100644 index b51652c94e..0000000000 --- a/_zh-cn/tour/automatic-closures.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: tour -title: Automatic Closures -partof: scala-tour - -language: zh-cn ---- diff --git a/api/all.md b/api/all.md index d67ee30084..db37824fc7 100644 --- a/api/all.md +++ b/api/all.md @@ -8,22 +8,22 @@ redirect_from: ## Latest releases -* Scala 3.5.0 - * [Library API](https://www.scala-lang.org/api/3.5.0/) -* Scala 3.3.3 LTS - * [Library API](https://www.scala-lang.org/api/3.3.3/) -* Scala 2.13.14 - * [Library API](https://www.scala-lang.org/api/2.13.14/) - * [Compiler API](https://www.scala-lang.org/api/2.13.14/scala-compiler/scala/) - * [Reflection API](https://www.scala-lang.org/api/2.13.14/scala-reflect/scala/reflect/) -* Scala 2.12.19 - * [Library API](https://www.scala-lang.org/api/2.12.19/) - * [Compiler API](https://www.scala-lang.org/api/2.12.19/scala-compiler/scala/) - * [Reflection API](https://www.scala-lang.org/api/2.12.19/scala-reflect/scala/reflect/) +* Scala {{site.scala-3-version}} + * [Library API](https://www.scala-lang.org/api/{{site.scala-3-version}}/) +* Scala 3.3.4 LTS + * [Library API](https://www.scala-lang.org/api/3.3.4/) +* Scala 2.13.15 + * [Library API](https://www.scala-lang.org/api/2.13.15/) + * [Compiler API](https://www.scala-lang.org/api/2.13.15/scala-compiler/scala/) + * [Reflection API](https://www.scala-lang.org/api/2.13.15/scala-reflect/scala/reflect/) +* Scala 2.12.20 + * [Library API](https://www.scala-lang.org/api/2.12.20/) + * [Compiler API](https://www.scala-lang.org/api/2.12.20/scala-compiler/scala/) + * [Reflection API](https://www.scala-lang.org/api/2.12.20/scala-reflect/scala/reflect/) * Scala Modules - * [XML API](https://www.scala-lang.org/api/2.12.19/scala-xml/scala/xml/) - * [Parser Combinators API](https://www.scala-lang.org/api/2.12.19/scala-parser-combinators/scala/util/parsing/) - * [Swing API](https://www.scala-lang.org/api/2.12.19/scala-swing/scala/swing/) + * [XML API](https://www.scala-lang.org/api/2.12.20/scala-xml/scala/xml/) + * [Parser Combinators API](https://www.scala-lang.org/api/2.12.20/scala-parser-combinators/scala/util/parsing/) + * [Swing API](https://www.scala-lang.org/api/2.12.20/scala-swing/scala/swing/) * Scala 2.11.12 * [Library API](https://www.scala-lang.org/api/2.11.12/) * [Compiler API](https://www.scala-lang.org/api/2.11.12/scala-compiler/) @@ -64,6 +64,10 @@ https://scala-ci.typesafe.com/artifactory/scala-integration/org/scala-lang/ ## Previous releases +* Scala 3.5.1 + * [Library API](https://www.scala-lang.org/api/3.5.1/) +* Scala 3.5.0 + * [Library API](https://www.scala-lang.org/api/3.5.0/) * Scala 3.4.3 * [Library API](https://www.scala-lang.org/api/3.4.3/) * Scala 3.4.2 @@ -72,6 +76,8 @@ https://scala-ci.typesafe.com/artifactory/scala-integration/org/scala-lang/ * [Library API](https://www.scala-lang.org/api/3.4.1/) * Scala 3.4.0 * [Library API](https://www.scala-lang.org/api/3.4.0/) +* Scala 3.3.3 LTS + * [Library API](https://www.scala-lang.org/api/3.3.3/) * Scala 3.3.1 LTS * [Library API](https://www.scala-lang.org/api/3.3.1/) * Scala 3.3.0 LTS @@ -96,6 +102,10 @@ https://scala-ci.typesafe.com/artifactory/scala-integration/org/scala-lang/ * [Library API](https://www.scala-lang.org/api/3.0.1/) * Scala 3.0.0 * [Library API](https://www.scala-lang.org/api/3.0.0/) +* Scala 2.13.14 + * [Library API](https://www.scala-lang.org/api/2.13.14/) + * [Compiler API](https://www.scala-lang.org/api/2.13.14/scala-compiler/scala/) + * [Reflection API](https://www.scala-lang.org/api/2.13.14/scala-reflect/scala/reflect/) * Scala 2.13.13 * [Library API](https://www.scala-lang.org/api/2.13.13/) * [Compiler API](https://www.scala-lang.org/api/2.13.13/scala-compiler/scala/) @@ -152,6 +162,14 @@ https://scala-ci.typesafe.com/artifactory/scala-integration/org/scala-lang/ * [Library API](https://www.scala-lang.org/api/2.13.0/) * [Compiler API](https://www.scala-lang.org/api/2.13.0/scala-compiler/scala/) * [Reflection API](https://www.scala-lang.org/api/2.13.0/scala-reflect/scala/reflect/) +* Scala 2.12.19 + * [Library API](https://www.scala-lang.org/api/2.12.19/) + * [Compiler API](https://www.scala-lang.org/api/2.12.19/scala-compiler/scala/) + * [Reflection API](https://www.scala-lang.org/api/2.12.19/scala-reflect/scala/reflect/) + * Scala Modules + * [XML API](https://www.scala-lang.org/api/2.12.19/scala-xml/scala/xml/) + * [Parser Combinators API](https://www.scala-lang.org/api/2.12.19/scala-parser-combinators/scala/util/parsing/) + * [Swing API](https://www.scala-lang.org/api/2.12.19/scala-swing/scala/swing/) * Scala 2.12.18 * [Library API](https://www.scala-lang.org/api/2.12.18/) * [Compiler API](https://www.scala-lang.org/api/2.12.18/scala-compiler/scala/) diff --git a/index.md b/index.md index fc46058513..22cbc790cc 100644 --- a/index.md +++ b/index.md @@ -17,7 +17,7 @@ sections: - title: "Getting Started" description: "Install Scala on your computer and start writing some Scala code!" icon: "fa fa-rocket" - link: /getting-started.html + link: /getting-started/install-scala.html - title: "Tour of Scala" description: "Bite-sized introductions to core language features." icon: "fa fa-flag" diff --git a/resources/images/getting-started/IntelliJScala.png b/resources/images/getting-started/IntelliJScala.png new file mode 100644 index 0000000000..c567da38df Binary files /dev/null and b/resources/images/getting-started/IntelliJScala.png differ diff --git a/resources/images/getting-started/VSCodeMetals.png b/resources/images/getting-started/VSCodeMetals.png new file mode 100644 index 0000000000..bdc5090726 Binary files /dev/null and b/resources/images/getting-started/VSCodeMetals.png differ diff --git a/scripts/run-mdoc.sh b/scripts/run-mdoc.sh index 4f3a7da6ea..6d729df4f1 100755 --- a/scripts/run-mdoc.sh +++ b/scripts/run-mdoc.sh @@ -1,11 +1,11 @@ #!/bin/bash set -eux -cs launch --scala-version 2.13.14 org.scalameta::mdoc:2.3.3 -- \ +cs launch --scala-version 2.13.15 org.scalameta::mdoc:2.3.3 -- \ --in . \ --out /tmp/mdoc-out/ \ --classpath \ - $(cs fetch --scala-version 2.13.14 -p \ + $(cs fetch --scala-version 2.13.15 -p \ com.chuusai::shapeless:2.3.10 \ org.scala-lang::toolkit:0.1.7 \ org.scala-lang::toolkit-test:0.1.7 \