Skip to content

Suggestions in the Scala book (3) #2076

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 14, 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
4 changes: 2 additions & 2 deletions _overviews/scala3-book/fun-anonymous-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ In other examples, you can simplify your anonymous functions further.
For instance, beginning with the most explicit form, you can print each element in `ints` using this anonymous function with the `List` class `foreach` method:

```scala
ints.foreach((i:Int) => println(i))
ints.foreach((i: Int) => println(i))
```

As before, the `Int` declaration isn’t required, and because there’s only one argument, the parentheses around `i` aren’t needed:
Expand All @@ -131,7 +131,7 @@ Because `i` is used only once in the body of the function, the expression can be
ints.foreach(println(_))
```

Finally, if an anonymous function consists of one statement that takes a single argument, you don’t need to explicitly name and specify the argument, so the statement can finally be reduced to this:
Finally, if an anonymous function consists of one method call that takes a single argument, you don’t need to explicitly name and specify the argument, so you can finally write only the name of the method (here, `println`):

```scala
ints.foreach(println)
Expand Down
32 changes: 16 additions & 16 deletions _overviews/scala3-book/fun-hofs.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ As a beneficial side effect of this discussion, once you’re comfortable with t

To understand how higher-order functions work, it helps to dig into an example.
For instance, you can understand the type of functions `filter` accepts by looking at its Scaladoc.
Here’s the `filter` definition in the `List` class:
Here’s the `filter` definition in the `List[A]` class:

```scala
def filter(p: (A) => Boolean): List[A]
Expand Down Expand Up @@ -102,7 +102,7 @@ Here’s how this works:
It’s just like naming a `String` parameter `s` or an `Int` parameter `i`.
- The type signature of `f` specifies the *type* of the functions this method will accept.
- The `()` portion of `f`’s signature (on the left side of the `=>` symbol) states that `f` takes no input parameters.
- The `Unit` portion of the signature (on the right side of the `=>` symbol) indicates that `f` should return nothing.
- The `Unit` portion of the signature (on the right side of the `=>` symbol) indicates that `f` should not return a meaningful result.
- Looking back at the body of the `sayHello` method (on the right side of the `=` symbol), the `f()` statement there invokes the function that’s passed in.

Now that we’ve defined `sayHello`, let’s create a function to match `f`’s signature so we can test it.
Expand Down Expand Up @@ -148,7 +148,7 @@ The only thing to do now is see a few more examples of how to define different t
In this method:

```scala
def sayHello(f: () => Unit)
def sayHello(f: () => Unit): Unit
```

We noted that the type signature for `f` is:
Expand All @@ -157,7 +157,7 @@ We noted that the type signature for `f` is:
() => Unit
```

We know that this means, “a function that takes no input parameters and returns nothing (given by `Unit`).”
We know that this means, “a function that takes no input parameters and returns nothing meaningful (given by `Unit`).”

To demonstrate more type signature examples, here’s a function that takes a `String` parameter and returns an `Int`:

Expand Down Expand Up @@ -255,40 +255,40 @@ executeAndPrint(multiply, 3, 9) // prints 27

## Function type signature consistency

A great thing about learning about Scala’s function type signatures is that the syntax you use to define function input parameters is the same syntax you use to write anonymous functions and function variables.
A great thing about learning about Scala’s function type signatures is that the syntax you use to define function input parameters is the same syntax you use to write function literals.

For instance, if you were to write an anonymous function that calculates the sum of two integers, you’d write it like this:
For instance, if you were to write a function that calculates the sum of two integers, you’d write it like this:

```scala
(Int, Int) => Int = (a, b) => a + b
val f: (Int, Int) => Int = (a, b) => a + b
```

That code consists of the type signature:

````
(Int, Int) => Int = (a, b) => a + b
-----------------
val f: (Int, Int) => Int = (a, b) => a + b
-----------------
````

The input parameters:

````
(Int, Int) => Int = (a, b) => a + b
------
val f: (Int, Int) => Int = (a, b) => a + b
------
````

and the body of the function:

````
(Int, Int) => Int = (a, b) => a + b
-----
val f: (Int, Int) => Int = (a, b) => a + b
-----
````

Scala’s consistency is shown here, where this anonymous function type signature:
Scala’s consistency is shown here, where this function type:

````
(Int, Int) => Int = (a, b) => a + b
-----------------
val f: (Int, Int) => Int = (a, b) => a + b
-----------------
````

is the same as the type signature you use to define a function input parameter:
Expand Down
4 changes: 2 additions & 2 deletions _overviews/scala3-book/fun-write-map-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ As a bonus, notice that the `for` expression doesn’t do anything that depends
Therefore, you can replace `Int` in the type signature with the generic type parameter `B`:

```scala
def map[A,B](f: (B) => A, xs: List[B]): List[A] =
def map[A, B](f: (B) => A, xs: List[B]): List[A] =
for x <- xs yield f(x)
```

Expand All @@ -76,7 +76,7 @@ These examples demonstrate that `map` works as desired:

```scala
def double(i : Int) = i * 2
map(double, List(1, 2, 3)) // List(2,4,6)
map(double, List(1, 2, 3)) // List(2, 4, 6)

def strlen(s: String) = s.length
map(strlen, List("a", "bb", "ccc")) // List(1, 2, 3)
Expand Down
2 changes: 1 addition & 1 deletion _overviews/scala3-book/methods-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ next-page: methods-most


In Scala 2, _methods_ can be defined inside classes, traits, objects, case classes, and case objects.
But it gets better: In Scala 3 they can also be defined _outside_ any of those constructs with a new feature named Toplevel definitions.
But it gets better: In Scala 3 they can also be defined _outside_ any of those constructs; we say that they are "top-level" definitions, since they are not nested in another definition.
In short, methods can now be defined anywhere.

Many features of methods are demonstrated in the next section.
Expand Down
4 changes: 2 additions & 2 deletions _overviews/scala3-book/methods-main-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ next-page: methods-summary
Scala 3 offers a new way to define programs that can be invoked from the command line: Adding a `@main` annotation to a method turns it into entry point of an executable program:

```scala
@main def hello = println("Hello, world")
@main def hello() = println("Hello, world")
```

Just save that line of code in a file named something like *Hello.scala*---the filename doesn’t have to match the method name---and compile it with `scalac`:
Expand Down Expand Up @@ -81,7 +81,7 @@ Illegal command line: java.lang.NumberFormatException: For input string: "sixty"
The Scala compiler generates a program from an `@main` method `f` as follows:

- It creates a class named `f` in the package where the `@main` method was found.
- The class has a static method `main` with the usual signature: It takes an `Array[String]` as argument and returns `Unit`.
- The class has a static method `main` with the usual signature of a Java `main` method: it takes an `Array[String]` as argument and returns `Unit`.
- The generated `main` method calls method `f` with arguments converted using methods in the `scala.util.CommandLineParser` object.

For instance, the `happyBirthday` method above generates additional code equivalent to the following class:
Expand Down
25 changes: 13 additions & 12 deletions _overviews/scala3-book/methods-most.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ previous-page: methods-intro
next-page: methods-main-methods
---

This section introduces the various aspects of how to define and call methods in Scala 2.
This section introduces the various aspects of how to define and call methods in Scala 3.

## Defining Methods

Scala methods have many features, including these:

- Generic (type) parameters
- Automatically provided `using` parameters
- Default parameter values
- Multiple parameter groups
- Context-provided parameters
- By-name parameters
- ...

Expand Down Expand Up @@ -63,7 +63,7 @@ The Scala collections classes have dozens of built-in methods.
These examples show how to call them:

```scala
val x = List(1,2,3)
val x = List(1, 2, 3)

x.size // 3
x.contains(1) // true
Expand Down Expand Up @@ -102,7 +102,7 @@ res0: Int = 4
```

Notice that there’s no need for a `return` statement at the end of the method.
Because almost everything in Scala is an _expression_---meaning that each line of code returns (or _evaluates to) a value---there’s no need to use `return`.
Because almost everything in Scala is an _expression_---meaning that each line of code returns (or _evaluates to_) a value---there’s no need to use `return`.

This becomes more clear when you condense that method and write it on one line:

Expand All @@ -118,6 +118,7 @@ The body of a method can use all the different features of the language:
- `for` loops and `for` expressions
- Variable assignments
- Calls to other methods
- Definitions of other methods

As an example of a real-world multiline method, this `getStackTraceAsString` method converts its `Throwable` input parameter into a well-formatted `String`:

Expand Down Expand Up @@ -202,10 +203,10 @@ When a method takes no parameters, it’s said to have an _arity_ level of _arit
Similarly, when a method takes one parameter it’s an _arity-1_ method.
When you create arity-0 methods:

- If the method has side effects, such as calling `println`, declare the method with empty parentheses
- If the method does not have side effects---such as getting the size of a collection, which is similar to accessing a field on the collection---leave the parentheses off
- If the method performs side effects, such as calling `println`, declare the method with empty parentheses
- If the method does not perform side effects---such as getting the size of a collection, which is similar to accessing a field on the collection---leave the parentheses off

For example, this method has a side effect, so it’s declared with empty parentheses:
For example, this method performs a side effect, so it’s declared with empty parentheses:

```scala
def speak() = println("hi")
Expand All @@ -218,7 +219,7 @@ speak // error: "method speak must be called with () argument"
speak() // prints "hi"
```

While this is just a convention, following it dramatically improves code readability: It makes it easier to understand at a glance that an arity-0 method has side effects.
While this is just a convention, following it dramatically improves code readability: It makes it easier to understand at a glance that an arity-0 method performs side effects.

{% comment %}
Some of that wording comes from this page: https://docs.scala-lang.org/style/method-invocation.html
Expand All @@ -233,7 +234,7 @@ Here’s a method named `isTruthy` that implements the Perl definitions of `true

```scala
def isTruthy(a: Any) =
if a == 0 || a == "" then
if a == 0 || a == "" || a == false then
Copy link
Contributor

Choose a reason for hiding this comment

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

Wow, thanks, this is a great catch! No telling how many times I’ve written that wrong.

false
else
true
Expand All @@ -257,7 +258,7 @@ Here’s another version of `isTruthy`, written with a `match` expression :

```scala
def isTruthy(a: Matchable) = a match
case 0 | "" => false
case 0 | "" | false => false
case _ => true
```

Expand All @@ -279,7 +280,7 @@ d.speak() // prints "Woof"
```

Methods can also be marked as `private`.
This makes them private to the current class, and they can’t be overridden in subclasses:
This makes them private to the current class, so they can’t be called nor overridden in subclasses:

```scala
class Animal:
Expand All @@ -290,7 +291,7 @@ class Cat extends Animal:
override def breathe() = println("Yo, I’m totally breathing")
```

If you want to make a method private to the current class and also allow subclasses to override it, mark the method as `protected`, as shown with the `speak` method in this example:
If you want to make a method private to the current class and also allow subclasses to call it or override it, mark the method as `protected`, as shown with the `speak` method in this example:

```scala
class Animal:
Expand Down
51 changes: 29 additions & 22 deletions _overviews/scala3-book/packaging-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,33 @@ package com.acme.myapp.model
class Person ...
```

By convention, package names should be all lower case, and the formal naming convention is *<top-level-domain>.<domain-name>.<project-name>.<module-name>*.
By convention, package names should be all lower case, and the formal naming convention is *\<top-level-domain>.\<domain-name>.\<project-name>.\<module-name>*.

Although it’s not required, package names typically follow directory structure names, so if you follow this convention, a `Person` class in this project will be found in a *MyApp/src/main/scala/com/acme/myapp/model/Person.scala* file.


### Curly brace packaging style
### Using multiple packages in the same file

The other way to declare packages in Scala is by using the curly brace namespace notation used in languages like C, C++, and C#:
The syntax shown above applies to the entire source file: all the definitions in the file
`Person.scala` belong to package `com.acme.myapp.model`, according to the package clause
at the beginning of the file.

Alternatively, it is possible to write package clauses that apply only to the definitions
they contain:

```scala
package users {
package administrators {
class AdminUser
}
package normalusers {
class NormalUser
}
}
package users:

package administrators: // the full name of this package is users.administrators
class AdminUser // the full name of this class is users.administrators.AdminUser

package normalusers: // the full name of this package is users.normalusers
class NormalUser // the full name of this class is users.normalusers.NormalUser
```

Note that the package names are followed by a colon, and that the defininitions within
a package are indented.

The advantages of this approach are that it allows for package nesting, and provides more obvious control of scope and encapsulation, especially within the same file.


Expand Down Expand Up @@ -92,27 +99,27 @@ A note before moving on:
In Scala you can import one member from a package like this:

```scala
import java.io.File
import scala.concurrent.Future
```

and multiple members like this:

```scala
import java.io.File
import java.io.IOException
import java.io.FileNotFoundException
import scala.concurrent.Future
import scala.concurrent.Promise
import scala.concurrent.blocking
```

When importing multiple members, you can import them more concisely like this:

```scala
import java.io.{File, IOException, FileNotFoundException}
import scala.concurrent.{Future, Promise, blocking}
```

When you want to import everything from the *java.io* package, use this syntax:
When you want to import everything from the *scala.concurrent* package, use this syntax:

```scala
import java.io.*
import scala.concurrent.*
```


Expand Down Expand Up @@ -170,13 +177,13 @@ val res0: java.util.ArrayList[String] = []
Because those Java classes are hidden, you can also use the Scala `List`, `Set`, and `Map` classes without having a naming collision:

```scala
scala> val a = List(1,2,3)
scala> val a = List(1, 2, 3)
val a: List[Int] = List(1, 2, 3)

scala> val b = Set(1,2,3)
scala> val b = Set(1, 2, 3)
val b: Set[Int] = Set(1, 2, 3)

scala> val c = Map(1->1, 2->2)
scala> val c = Map(1 -> 1, 2 -> 2)
val c: Map[Int, Int] = Map(1 -> 1, 2 -> 2)
```

Expand Down Expand Up @@ -241,7 +248,7 @@ Two packages are implicitly imported into the scope of all of your source code f
- java.lang.*
- scala.*

The Scala `Predef` object is also imported by default.
The members of the Scala object `Predef` are also imported by default.

> If you ever wondered why you can use classes like `List`, `Vector`, `Map`, etc., without importing them, they’re available because of definitions in the `Predef` object.

Expand Down