Skip to content

Suggestions on the Scala 3 book (part 5) #2088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 39 additions & 24 deletions _overviews/scala3-book/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines -58 to +59
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is important to not say things like “we run a Future” because that maintains a confusion on the purpose of Future (which only holds a future result, unlike a “task”, which is a computation that we can run, cancel, etc.)




Expand All @@ -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
```
Expand All @@ -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(<not completed>)
scala> val eventualInt = Future(longRunningAlgorithm())
eventualInt: scala.concurrent.Future[Int] = Future(<not completed>)
```

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(<not completed>)
```

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.


Expand All @@ -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(<not completed>)
```

Expand All @@ -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
Expand Down Expand Up @@ -180,38 +181,39 @@ 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


### 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
Expand Down Expand Up @@ -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

Expand Down
11 changes: 5 additions & 6 deletions _overviews/scala3-book/interacting-with-java.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:

Expand All @@ -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
Expand Down Expand Up @@ -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`:

Expand Down
23 changes: 14 additions & 9 deletions _overviews/scala3-book/scala-for-java-devs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the comma is not necessary

- 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
Expand Down Expand Up @@ -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.

<table>
<tbody>
<tr>
<td class="java-block">
<code>class Person {
<br>&nbsp; private String firstName;
<br>&nbsp; private String lastName;
<br>&nbsp; private int age;
<br>&nbsp; public String firstName;
<br>&nbsp; public String lastName;
<br>&nbsp; public int age;
<br>&nbsp; public Person(
<br>&nbsp;&nbsp;&nbsp; String firstName,
<br>&nbsp;&nbsp;&nbsp; String lastName, int age
Expand Down Expand Up @@ -160,9 +165,9 @@ This section provides comparisons of features related to OOP-style classes and m
<tr>
<td class="java-block">
<code>public class Person {
<br>&nbsp; private String firstName;
<br>&nbsp; private String lastName;
<br>&nbsp; private int age;
<br>&nbsp; public String firstName;
<br>&nbsp; public String lastName;
<br>&nbsp; public int age;
<br>
<br>&nbsp; // primary constructor
<br>&nbsp; public Person(
Expand Down Expand Up @@ -424,7 +429,7 @@ This section compares Java interfaces to Scala traits, including how classes ext
<tbody>
<tr>
<td class="java-block">
<code>class Dog extends Animal, HasLegs, HasTail</code>
<code>class Dog extends Animal implements HasLegs, HasTail</code>
</td>
</tr>
<tr>
Expand Down Expand Up @@ -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`:
Expand Down Expand Up @@ -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]
Expand Down
7 changes: 3 additions & 4 deletions _overviews/scala3-book/scala-for-javascript-devs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -441,7 +441,7 @@ In both JavaScript and Scala, functions are objects, so their functionality is s
</tr>
<tr>
<td class="scala-block">
<code>// technically this is a method, not a function
<code>// technically this is a method, not a function
<br>def add(a: Int, b: Int) = a + b
<br>add(2, 2)&nbsp;&nbsp; // 4</code>
</td>
Expand Down Expand Up @@ -474,7 +474,7 @@ In both JavaScript and Scala, functions are objects, so their functionality is s
</table>

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.



Expand Down Expand Up @@ -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 %}

</div>

34 changes: 15 additions & 19 deletions _overviews/scala3-book/scala-for-python-devs.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ These examples demonstrate how to create variables in Python and Scala.
</tbody>
</table>

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
Expand Down Expand Up @@ -631,18 +631,14 @@ Scala also has `match` expressions.
<tbody>
<tr>
<td class="python-block">
<code>x = [i*10 for i in range(1,4)]
<br># x: [10,20,30]</code>
<code>xs = [i * 10 for i in range(1, 4)]
<br># xs: [10,20,30]</code>
</td>
</tr>
<tr>
<td class="scala-block">
<code>val x =
<br>&nbsp; for
<br>&nbsp;&nbsp;&nbsp; i &lt;- 1 to 3
<br>&nbsp; yield
<br>&nbsp;&nbsp;&nbsp; i * 10
<br>// x: Vector(10, 20, 30)</code>
<code>val xs = for i &lt;- 1 to 3 yield i * 10
<br>// xs: Vector(10, 20, 30)</code>
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -894,9 +890,7 @@ However, the default Scala map is _immutable_, and has a number of transformatio
</tr>
<tr>
<td class="scala-block">
<code>for
<br>&nbsp; (key,value) &lt;- myMap
<br>do
<code>for (key,value) &lt;- myMap do
<br>&nbsp; println(key)
<br>&nbsp; println(value)</code>
</td>
Expand Down Expand Up @@ -934,7 +928,7 @@ The Python set is similar to the _mutable_ Scala `Set` class.
<tr>
<td class="python-block">
<code>set = {1,2,1}
<br># set: {1,2}</code></td>
<br># set: {1,2}</code>
</td>
</tr>
<tr>
Expand Down Expand Up @@ -1012,7 +1006,7 @@ Those lists are used in the following table, that shows how to apply mapping and
<tbody>
<tr>
<td class="python-block">
<code>x = [i*10 for i in numbers]</code>
<code>x = [i * 10 for i in numbers]</code>
</td>
</tr>
<tr>
Expand All @@ -1034,7 +1028,9 @@ Those lists are used in the following table, that shows how to apply mapping and
</tr>
<tr>
<td class="scala-block">
<code>val evens = numbers.filter(_ % 2 == 0)</code>
<code>val evens = numbers.filter(_ % 2 == 0)
<br>// or
<br>val evens = for i <- numbers if i % 2 == 0 yield i</code>
</td>
</tr>
</tbody>
Expand All @@ -1051,8 +1047,9 @@ Those lists are used in the following table, that shows how to apply mapping and
</tr>
<tr>
<td class="scala-block">
<code>val x = numbers.filter(_ % 2 == 0)
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(_ * 10)</code>
<code>val x = numbers.filter(_ % 2 == 0).map(_ * 10)
<br>// or
<br>val x = for i <- numbers if i % 2 == 0 yield i * 10</code>
</td>
</tr>
</tbody>
Expand All @@ -1064,8 +1061,7 @@ Those lists are used in the following table, that shows how to apply mapping and
<tbody>
<tr>
<td class="python-block">
<code>def times_10(n): return n * 10
<br>x = map(lambda x: x * 10, numbers)</code>
<code>x = map(lambda x: x * 10, numbers)</code>
</td>
</tr>
<tr>
Expand Down
Loading