diff --git a/_overviews/scala3-book/concurrency.md b/_overviews/scala3-book/concurrency.md index 59b86b0815..bda65f21a9 100644 --- a/_overviews/scala3-book/concurrency.md +++ b/_overviews/scala3-book/concurrency.md @@ -55,7 +55,8 @@ You’ll also see examples of methods that are used to handle the value in a fut > When you think about futures, it’s important to know that they’re intended as a one-shot, “Handle this relatively slow computation on some other thread, and call me back with a result when you’re done” construct. > As a point of contrast, [Akka](https://akka.io) actors are intended to run for a long time and respond to many requests during their lifetime. -> While an actor may live forever, a future is intended to be run only once. +> While an actor may live forever, a future eventually contains the result +> of a computation that ran only once. @@ -77,7 +78,7 @@ Now you’re ready to create a future. For this example, first define a long-running, single-threaded algorithm: ```scala -def longRunningAlgorithm = +def longRunningAlgorithm() = Thread.sleep(10_000) 42 ``` @@ -86,29 +87,29 @@ That fancy algorithm returns the integer value `42` after a ten second delay. Now call that algorithm by wrapping it into the `Future` constructor, and assigning the result to a variable: ```scala -scala> val f = Future(longRunningAlgorithm) -f: scala.concurrent.Future[Int] = Future() +scala> val eventualInt = Future(longRunningAlgorithm()) +eventualInt: scala.concurrent.Future[Int] = Future() ``` -Right away your future begins running. -If you immediately check the value of the variable `f`, you see that the future hasn’t completed yet: +Right away, your computation---the call to `longRunningAlgorithm()`---begins running. +If you immediately check the value of the variable `eventualInt`, you see that the future hasn’t been completed yet: ```scala -scala> f +scala> eventualInt val res1: scala.concurrent.Future[Int] = Future() ``` -But if you check again after ten seconds, you’ll see that it completes successfully: +But if you check again after ten seconds, you’ll see that it is completed successfully: ```scala -scala> f +scala> eventualInt val res2: scala.concurrent.Future[Int] = Future(Success(42)) ``` While that’s a relatively simple example, it shows the basic approach: Just construct a new `Future` with your long-running algorithm. One thing to notice is that the `42` you expected is wrapped in a `Success`, which is further wrapped in a `Future`. -This is a key concept to understand: the value in a `Future` is always an instance of one of the *scala.util.Try* types: `Success` or `Failure`. +This is a key concept to understand: the value in a `Future` is always an instance of one of the `scala.util.Try` types: `Success` or `Failure`. Therefore, when you work with the result of a future, you use the usual `Try`-handling techniques. @@ -118,7 +119,7 @@ Therefore, when you work with the result of a future, you use the usual `Try`-ha This is what the result looks like when you call `map` right after creating the variable `f`: ```scala -scala> val a = f.map(_ * 2) +scala> val a = eventualInt.map(_ * 2) a: scala.concurrent.Future[Int] = Future() ``` @@ -139,13 +140,13 @@ In addition to higher-order functions like `map`, you can also use callback meth One commonly used callback method is `onComplete`, which takes a *partial function* in which you handle the `Success` and `Failure` cases: ```scala -f.onComplete { +eventualInt.onComplete { case Success(value) => println(s"Got the callback, value = $value") case Failure(e) => e.printStackTrace } ``` -When you paste that code in the REPL you’ll see the result: +When you paste that code in the REPL you’ll eventually see the result: ```scala Got the callback, value = 42 @@ -180,10 +181,11 @@ See the [Futures and Promises][futures] page for a discussion of additional meth ## Running multiple futures and joining their results -To run multiple futures in parallel and join their results when all of the futures complete, use a `for` expression. +To run multiple computations in parallel and join their results when all of the futures have been completed, use a `for` expression. + The correct approach is: -1. Create the futures +1. Start the computations that return `Future` results 2. Merge their results in a `for` expression 3. Extract the merged result using `onComplete` or a similar technique @@ -191,27 +193,27 @@ The correct approach is: ### An example The three steps of the correct approach are shown in the following example. -A key is that you first create the futures and then join them in the `for` expression: +A key is that you first start the computations that return futures, and then join them in the `for` expression: ```scala import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import scala.util.{Failure, Success} -val startTime = System.currentTimeMillis -def delta() = System.currentTimeMillis - startTime +val startTime = System.currentTimeMillis() +def delta() = System.currentTimeMillis() - startTime def sleep(millis: Long) = Thread.sleep(millis) @main def multipleFutures1 = println(s"creating the futures: ${delta()}") - // (1) create the futures + // (1) start the computations that return futures val f1 = Future { sleep(800); 1 } // eventually returns 1 val f2 = Future { sleep(200); 2 } // eventually returns 2 val f3 = Future { sleep(400); 3 } // eventually returns 3 - // (2) run them simultaneously in a `for` expression + // (2) join the futures in a `for` expression val result = for r1 <- f1 @@ -251,10 +253,23 @@ All of that code is run on the JVM’s main thread. Then, at 806 ms, the three futures complete and the code in the `yield` block is run. Then the code immediately goes to the `Success` case in the `onComplete` method. -The 806 ms output is a key to seeing that the three futures are run in parallel. -If they were run sequentially, the total time would be about 1,400 ms---the sum of the sleep times of the three futures. -But because they’re run in parallel, the total time is just slightly longer than the longest-running future: `f1`, which is 800 ms. - +The 806 ms output is a key to seeing that the three computations are run in parallel. +If they were run sequentially, the total time would be about 1,400 ms---the sum of the sleep times of the three computations. +But because they’re run in parallel, the total time is just slightly longer than the longest-running computation: `f1`, which is 800 ms. + +> Notice that if the computations were run within the `for` expression, they +> would be executed sequentially, not in parallel: +> ~~~ +> // Sequential execution (no parallelism!) +> for +> r1 <- Future { sleep(800); 1 } +> r2 <- Future { sleep(200); 2 } +> r3 <- Future { sleep(400); 3 } +> yield +> r1 + r2 + r3 +> ~~~ +> So, if you want the computations to be possibly run in parallel, remember +> to run them outside of the `for` expression. ### A method that returns a future diff --git a/_overviews/scala3-book/interacting-with-java.md b/_overviews/scala3-book/interacting-with-java.md index 36ceb7fe58..8ccb38e156 100644 --- a/_overviews/scala3-book/interacting-with-java.md +++ b/_overviews/scala3-book/interacting-with-java.md @@ -58,9 +58,8 @@ You can convert that Java list to a Scala `Seq`, using the conversion utilities ```scala // scala import scala.jdk.CollectionConverters.* -import java.util.List -def testList = +def testList() = println("Using a Java List in Scala") val javaList: java.util.List[String] = JavaClass.getStrings() val scalaSeq: Seq[String] = javaList.asScala.toSeq @@ -156,7 +155,7 @@ class Dog extends Animal, Wagging, Running: ## How to use Scala collections in Java -When you need to use a Scala collection class in your Java code, use the methods of Scala’s _scala.jdk.javaapi.CollectionConverters_ object in your Java code to make the conversions work. +When you need to use a Scala collection class in your Java code, use the methods of Scala’s `scala.jdk.javaapi.CollectionConverters` object in your Java code to make the conversions work. For example, if you have a `List[String]` like this in a Scala class: ```scala @@ -192,7 +191,7 @@ Here are a few things to notice in that code: ## How to use Scala `Option` in Java -When you need to use a Scala `Option` in your Java code, you can convert the `Option` to a Java `Optional` value using the `toJava` method of the Scala _scala.jdk.javaapi.OptionConverters_ object. +When you need to use a Scala `Option` in your Java code, you can convert the `Option` to a Java `Optional` value using the `toJava` method of the Scala `scala.jdk.javaapi.OptionConverters` object. To demonstrate this, create a Scala class with two `Option[String]` values, one containing a string and the other one empty: @@ -203,7 +202,7 @@ object ScalaObject: val noneString: Option[String] = None ``` -Then in your Java code, convert those `Option[String]` values into `java.util.Optional[String]` using the `toJava` method from the _scala.jdk.javaapi.OptionConverters_ object: +Then in your Java code, convert those `Option[String]` values into `java.util.Optional[String]` using the `toJava` method from the `scala.jdk.javaapi.OptionConverters` object: ```java // java @@ -258,7 +257,7 @@ System.out.println(jm.multiply(3,4)); // 12 ## How to handle Scala methods that throw exceptions in Java code When you’re writing Scala code using Scala programming idioms, you’ll never write a method that throws an exception. -But if for some reason you have a Scala method that does throw an exception, and you want Java developers to be able to use that method, add the `@throws` annotation to your Scala method so Java consumers will know the exceptions they can throw. +But if for some reason you have a Scala method that does throw an exception, and you want Java developers to be able to use that method, add the `@throws` annotation to your Scala method so Java consumers will know the exceptions it can throw. For example, this Scala `exceptionThrower` method is annotated to declare that it throws an `Exception`: diff --git a/_overviews/scala3-book/scala-for-java-devs.md b/_overviews/scala3-book/scala-for-java-devs.md index 50d1777677..be54f5fd25 100644 --- a/_overviews/scala3-book/scala-for-java-devs.md +++ b/_overviews/scala3-book/scala-for-java-devs.md @@ -49,6 +49,7 @@ Also at a high level, the differences between Java and Scala are: - Scala has a full suite of immutable collections, including `List`, `Vector`, and immutable `Map` and `Set` implementations - Everything in Scala is an _expression_: constructs like `if` statements, `for` loops, `match` expressions, and even `try`/`catch` expressions all have return values - Scala idioms favor immutability by default: you’re encouraged to use immutable (`final`) variables and immutable collections +- Idiomatic Scala code does not use `null`, and thus does not suffer from `NullPointerException` - The Scala ecosystem has other [build tools][tools] in sbt, Mill, and others - In addition to running on the JVM, the [Scala.js](https://www.scala-js.org) project lets you use Scala as a JavaScript replacement - The [Scala Native](http://www.scala-native.org) project adds low-level constructs to let you write “systems” level code, and also compiles to native executables @@ -117,14 +118,18 @@ This section provides comparisons of features related to OOP-style classes and m ### OOP style class, primary constructor: +Scala doesn’t follow the JavaBeans standard, so instead of showing Java +code written in the JavaBeans style, here we show Java code that is +equivalent to the Scala code that follows it. + @@ -976,7 +981,7 @@ val a = Array("a", "b") as being backed by this Java `String[]`: ```scala -String[] a = ["a", "b"] +String[] a = ["a", "b"]; ``` However, a Scala `Array` also has all of the functional methods you expect in a Scala collection, including `map` and `filter`: @@ -1275,7 +1280,7 @@ For more information on dealing with errors and exceptions in Scala, see the [Fu That concludes are comparison of the Java and Scala languages. -Currently there are other concepts in Scala which currently have no equal in Java 11. +There are other concepts in Scala which currently have no equal in Java 11. This includes: - Everything related to Scala’s [contextual abstractions][contextual] diff --git a/_overviews/scala3-book/scala-for-javascript-devs.md b/_overviews/scala3-book/scala-for-javascript-devs.md index 0237e0d63c..a577837e56 100644 --- a/_overviews/scala3-book/scala-for-javascript-devs.md +++ b/_overviews/scala3-book/scala-for-javascript-devs.md @@ -36,7 +36,7 @@ At a high level, Scala shares these similarities with JavaScript: - Both languages have similar `if` statements, `while` loops, and `for` loops - Starting [at this Scala.js page](https://www.scala-js.org/libraries/index.html), you’ll find dozens of libraries to support React, Angular, jQuery, and many other JavaScript and Scala libraries - JavaScript objects are mutable; Scala objects _can_ be mutable when writing in an imperative style -- Both JavaScript and Scala support _promises_ as a way of running asynchronous computations ([Scala concurrency][concurrency] uses futures and promises) +- Both JavaScript and Scala support _promises_ as a way of handling the result of asynchronous computations ([Scala concurrency][concurrency] uses futures and promises) ### High-level differences @@ -441,7 +441,7 @@ In both JavaScript and Scala, functions are objects, so their functionality is s @@ -474,7 +474,7 @@ In both JavaScript and Scala, functions are objects, so their functionality is s
class Person { -
  private String firstName; -
  private String lastName; -
  private int age; +
  public String firstName; +
  public String lastName; +
  public int age;
  public Person(
    String firstName,
    String lastName, int age @@ -160,9 +165,9 @@ This section provides comparisons of features related to OOP-style classes and m
public class Person { -
  private String firstName; -
  private String lastName; -
  private int age; +
  public String firstName; +
  public String lastName; +
  public int age;

  // primary constructor
  public Person( @@ -424,7 +429,7 @@ This section compares Java interfaces to Scala traits, including how classes ext
- class Dog extends Animal, HasLegs, HasTail + class Dog extends Animal implements HasLegs, HasTail
- // technically this is a “method,” not a function + // technically this is a method, not a function
def add(a: Int, b: Int) = a + b
add(2, 2)   // 4
In Scala, showing the `Int` return type is optional. -It’s _not_ shown in the `add` example and _is_ shown in the `addThenDouble` example, so you can see both approaches. +It’s _not_ shown in the `add` example and _is_ shown in the `addAndDouble` example, so you can see both approaches. @@ -1374,4 +1374,3 @@ There are other concepts in Scala which currently have no equivalent in JavaScri [union-types]: {% link _overviews/scala3-book/types-union.md %} - diff --git a/_overviews/scala3-book/scala-for-python-devs.md b/_overviews/scala3-book/scala-for-python-devs.md index 0934989d4e..395d7c450d 100644 --- a/_overviews/scala3-book/scala-for-python-devs.md +++ b/_overviews/scala3-book/scala-for-python-devs.md @@ -230,7 +230,7 @@ These examples demonstrate how to create variables in Python and Scala. -If a Scala field is going to be mutable, use `var` instead of `val` for variable assignment: +If a Scala field is going to be mutable, use `var` instead of `val` for variable definition: ```scala var x = 1 @@ -631,18 +631,14 @@ Scala also has `match` expressions. - x = [i*10 for i in range(1,4)] -
# x: [10,20,30]
+ xs = [i * 10 for i in range(1, 4)] +
# xs: [10,20,30]
- val x = -
  for -
    i <- 1 to 3 -
  yield -
    i * 10 -
// x: Vector(10, 20, 30)
+ val xs = for i <- 1 to 3 yield i * 10 +
// xs: Vector(10, 20, 30)
@@ -894,9 +890,7 @@ However, the default Scala map is _immutable_, and has a number of transformatio - for -
  (key,value) <- myMap -
do + for (key,value) <- myMap do
  println(key)
  println(value)
@@ -934,7 +928,7 @@ The Python set is similar to the _mutable_ Scala `Set` class. set = {1,2,1} -
# set: {1,2}
+
# set: {1,2}
@@ -1012,7 +1006,7 @@ Those lists are used in the following table, that shows how to apply mapping and - x = [i*10 for i in numbers] + x = [i * 10 for i in numbers] @@ -1034,7 +1028,9 @@ Those lists are used in the following table, that shows how to apply mapping and - val evens = numbers.filter(_ % 2 == 0) + val evens = numbers.filter(_ % 2 == 0) +
// or +
val evens = for i <- numbers if i % 2 == 0 yield i
@@ -1051,8 +1047,9 @@ Those lists are used in the following table, that shows how to apply mapping and - val x = numbers.filter(_ % 2 == 0) -
          .map(_ * 10)
+ val x = numbers.filter(_ % 2 == 0).map(_ * 10) +
// or +
val x = for i <- numbers if i % 2 == 0 yield i * 10
@@ -1064,8 +1061,7 @@ Those lists are used in the following table, that shows how to apply mapping and - def times_10(n): return n * 10 -
x = map(lambda x: x * 10, numbers)
+ x = map(lambda x: x * 10, numbers) diff --git a/_overviews/scala3-book/scala-tools.md b/_overviews/scala3-book/scala-tools.md index 81f7177515..75bb1f2c0d 100644 --- a/_overviews/scala3-book/scala-tools.md +++ b/_overviews/scala3-book/scala-tools.md @@ -22,7 +22,7 @@ We’ll start by showing how to use sbt to build your Scala projects, and then w ## Building Scala projects with sbt You can use several different tools to build your Scala projects, including Ant, Maven, Gradle, Mill, and more. -But a tool named _sbt_ was the first build tool that was specifically created for Scala, and these days it’s supported by [Lightbend](https://www.lightbend.com), the company that also maintains [Akka](https://akka.io), the [Play framework](https://www.playframework.com), the [Lagom framework](https://www.lagomframework.com), and more. +But a tool named _sbt_ was the first build tool that was specifically created for Scala. > To install sbt, see [its download page](https://www.scala-sbt.org/download.html) or our [Getting Started][getting_started] page. @@ -38,7 +38,20 @@ $ mkdir hello $ cd hello ``` -Then create a file named _build.sbt_ that contains this line: +In the directory `hello`, create a subdirectory `project`: + +```bash +$ mkdir project +``` + +Create a file named _build.properties_ in the directory `project`, with +the following content: + +```text +sbt.version=1.5.4 +``` + +Then create a file named _build.sbt_ in the project root directory that contains this line: ```scala scalaVersion := "{{ site.scala-3-version }}" @@ -51,6 +64,18 @@ Now create a file named something like _Hello.scala_---the first part of the nam ``` That’s all you have to do. + +You should have a project structure like the following: + +~~~ bash +$ tree +. +├── build.sbt +├── Hello.scala +└── project + └── build.properties +~~~ + Now run the project with this `sbt` command: ```bash @@ -61,7 +86,7 @@ You should see output that looks like this, including the `"Hello, world"` from ```bash $ sbt run -[info] welcome to sbt 1.4.4 (AdoptOpenJDK Java 11.x) +[info] welcome to sbt 1.5.4 (AdoptOpenJDK Java 11.x) [info] loading project definition from project ... [info] loading settings for project from build.sbt ... [info] compiling 1 Scala source to target/scala-3.0.0/classes ... @@ -70,7 +95,9 @@ Hello, world [success] Total time: 2 s ``` -When you look at your directory, you’ll see that sbt has created two directories named _project_ and _target_. +The sbt launcher---the `sbt` command-line tool---loads the version of sbt set in the file _project/build.properties_, which loads the version of the Scala compiler set in the file _build.sbt_, compiles the code in the file _Hello.scala_, and runs the resulting bytecode. + +When you look at your directory, you’ll see that sbt has a directory named _target_. These are working directories that sbt uses. As you can see, creating and running a little Scala project with sbt takes just a few simple steps. @@ -92,31 +119,34 @@ A nice benefit of that is that once you’re comfortable with its structure, it The first thing to know is that underneath the root directory of your project, sbt expects a directory structure that looks like this: -```bash -build.sbt -project/ -src/ --- main/ - |-- java/ - |-- resources/ - |-- scala/ -|-- test/ - |-- java/ - |-- resources/ - |-- scala/ -target/ +```text +. +├── build.sbt +├── project/ +│ └── build.properties +├── src/ +│ ├── main/ +│ │ ├── java/ +│ │ ├── resources/ +│ │ └── scala/ +│ └── test/ +│ ├── java/ +│ ├── resources/ +│ └── scala/ +└── target/ ``` You can also add a _lib_ directory under the root directory if you want to add unmanaged dependencies---JAR files---to your project. If you’re going to create a project that has Scala source code files and tests, but won’t be using any Java source code files, and doesn’t need any “resources”---such as embedded images, configuration files, etc.---this is all you really need under the _src_ directory: -```bash -src/ --- main/ - |-- scala/ -|-- test/ - |-- scala/ +```text +. +└── src/ + ├── main/ + │ └── scala/ + └── test/ + └── scala/ ``` @@ -284,11 +314,18 @@ As with the previous lesson, create an sbt project directory structure for a pro $ mkdir HelloScalaTest $ cd HelloScalaTest $ mkdir -p src/{main,test}/scala -$ mkdir project target +$ mkdir project ``` -### Creating the build.sbt file +### Creating the build.properties and build.sbt files + +Next, create a _build.properties_ file in the _project/_ subdirectory of your project +with this line: + +```text +sbt.version=1.5.4 +``` Next, create a _build.sbt_ file in the root directory of your project with these contents: @@ -298,7 +335,7 @@ version := "0.1" scalaVersion := "{{site.scala-3-version}}" libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % "3.3.0-SNAP3" % Test + "org.scalatest" %% "scalatest" % "3.2.9" % Test ) ```