diff --git a/_overviews/scala3-book/ca-context-bounds.md b/_overviews/scala3-book/ca-context-bounds.md index fd435eb68f..462774a2d4 100644 --- a/_overviews/scala3-book/ca-context-bounds.md +++ b/_overviews/scala3-book/ca-context-bounds.md @@ -13,7 +13,7 @@ next-page: ca-given-imports - TODO: define "synthesized" and "synthesized arguments" {% endcomment %} -In many situations the name of a _context parameter_ doesn’t have to be mentioned explicitly, since it’s only used in synthesized arguments for other context parameters. +In many situations the name of a _context parameter_ doesn’t have to be mentioned explicitly, since it’s only used by the compiler in synthesized arguments for other context parameters. In that case you don’t have to define a parameter name, and can just provide the parameter type. diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index dd1b4ccc1b..39af385316 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -47,7 +47,7 @@ The Scala compiler thus performs **term inference**. In our call to `renderWidget(List("cart"))` the Scala compiler will see that there is a term of type `Config` in scope (the `c`) and automatically provide it to `renderWidget`. So the program is equivalent to the one above. -In fact, since we do not need to refer to `c` in our implementation of `renderWebsite` anymore, we can even omit it in the signature: +In fact, since we do not need to refer to `c` in our implementation of `renderWebsite` anymore, we can even omit its name in the signature: ```scala // no need to come up with a parameter name diff --git a/_overviews/scala3-book/ca-implicit-conversions.md b/_overviews/scala3-book/ca-implicit-conversions.md index 4a421da97b..d04cefa104 100644 --- a/_overviews/scala3-book/ca-implicit-conversions.md +++ b/_overviews/scala3-book/ca-implicit-conversions.md @@ -8,8 +8,8 @@ next-page: ca-summary --- -Implicit conversions are defined by `given` instances of the _scala.Conversion_ class. -For example, not accounting for possible conversion errors, this code defines an an implicit conversion from `String` to `Int`: +Implicit conversions are defined by `given` instances of the `scala.Conversion` class. +For example, not accounting for possible conversion errors, this code defines an implicit conversion from `String` to `Int`: ```scala given Conversion[String, Int] with @@ -34,10 +34,13 @@ def plus1(i: Int) = i + 1 plus1("1") ``` +> Note the clause `import scala.language.implicitConversions` at the beginning, +> to enable implicit conversions in the file. + ## Discussion -The Predef package contains “auto-boxing” conversions that map primitive number types to subclasses of _java.lang.Number_. -For instance, the conversion from `Int` to _java.lang.Integer_ can be defined as follows: +The Predef package contains “auto-boxing” conversions that map primitive number types to subclasses of `java.lang.Number`. +For instance, the conversion from `Int` to `java.lang.Integer` can be defined as follows: ```scala given int2Integer: Conversion[Int, java.lang.Integer] = diff --git a/_overviews/scala3-book/collections-classes.md b/_overviews/scala3-book/collections-classes.md index 7226398406..e9b7adec45 100644 --- a/_overviews/scala3-book/collections-classes.md +++ b/_overviews/scala3-book/collections-classes.md @@ -28,9 +28,9 @@ When you need more flexibility, see these pages at the end of this section for m Looking at Scala collections from a high level, there are three main categories to choose from: -- **Sequences** are a linear collection of elements and may be _indexed_ (like an array) or _linear_ (like a linked list) +- **Sequences** are a sequential collection of elements and may be _indexed_ (like an array) or _linear_ (like a linked list) - **Maps** contain a collection of key/value pairs, like a Java `Map`, Python dictionary, or Ruby `Hash` -- **Sets** are an unordered sequence of unique elements +- **Sets** are an unordered collection of unique elements All of those are basic types, and have subtypes for specific purposes, such as concurrency, caching, and streaming. In addition to those three main categories, there are other useful collection types, including ranges, stacks, and queues. @@ -74,7 +74,7 @@ The main collections you’ll use on a regular basis are: | `LazyList` | ✓ | | A lazy immutable linked list, its elements are computed only when they’re needed; Good for large or infinite sequences. | | `ArrayBuffer` | | ✓ | The go-to type for a mutable, indexed sequence | | `ListBuffer` | | ✓ | Used when you want a mutable `List`; typically converted to a `List` | -| `Map` | ✓ | ✓ | An iterable sequence that consists of pairs of keys and values. | +| `Map` | ✓ | ✓ | An iterable collection that consists of pairs of keys and values. | | `Set` | ✓ | ✓ | An iterable collection with no duplicate elements | As shown, `Map` and `Set` come in both immutable and mutable versions. @@ -109,7 +109,7 @@ For example, if you need an immutable, indexed collection, in general you should Conversely, if you need a mutable, indexed collection, use an `ArrayBuffer`. > `List` and `Vector` are often used when writing code in a functional style. -> `ArrayBuffer` is commonly used when writing code in a mutable style. +> `ArrayBuffer` is commonly used when writing code in an imperative style. > `ListBuffer` is used when you’re mixing styles, such as building a list. The next several sections briefly demonstrate the `List`, `Vector`, and `ArrayBuffer` types. @@ -291,7 +291,7 @@ val nums = Vector(1, 2, 3, 4, 5) val strings = Vector("one", "two") -case class Person(val name: String) +case class Person(name: String) val people = Vector( Person("Bert"), Person("Ernie"), @@ -383,10 +383,9 @@ Or if you prefer methods with textual names you can also use `append`, `appendAl Here are some examples of `+=` and `++=`: ```scala -var nums = ArrayBuffer(1, 2, 3) // ArrayBuffer(1, 2, 3) +val nums = ArrayBuffer(1, 2, 3) // ArrayBuffer(1, 2, 3) nums += 4 // ArrayBuffer(1, 2, 3, 4) -nums += (5, 6) // ArrayBuffer(1, 2, 3, 4, 5, 6) -nums ++= List(7, 8) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8) +nums ++= List(5, 6) // ArrayBuffer(1, 2, 3, 4, 5, 6) ``` ### Removing elements from an ArrayBuffer @@ -415,7 +414,7 @@ a.update(0, 10) // ArrayBuffer(10, 2, 50, 4) ## Maps -A `Map` is an iterable sequence that consists of pairs of keys and values. +A `Map` is an iterable collection that consists of pairs of keys and values. Scala has both mutable and immutable `Map` types, and this section demonstrates how to use the _immutable_ `Map`. ### Creating an immutable Map @@ -433,13 +432,13 @@ val states = Map( Once you have a `Map` you can traverse its elements in a `for` loop like this: ```scala -for ((k,v) <- states) println(s"key: $k, value: $v") +for (k, v) <- states do println(s"key: $k, value: $v") ``` The REPL shows how this works: ```` -scala> for ((k,v) <- states) println(s"key: $k, value: $v") +scala> for (k, v) <- states do println(s"key: $k, value: $v") key: AK, value: Alaska key: AL, value: Alabama key: AZ, value: Arizona @@ -463,7 +462,7 @@ Add elements to an immutable map using `+` and `++`, remembering to assign the r ```scala val a = Map(1 -> "one") // a: Map(1 -> one) val b = a + (2 -> "two") // b: Map(1 -> one, 2 -> two) -val c = b + ( +val c = b ++ Seq( 3 -> "three", 4 -> "four" ) @@ -482,13 +481,13 @@ val a = Map( 4 -> "four" ) -a - 4 // Map(1 -> one, 2 -> two, 3 -> three) -a - 4 - 3 // Map(1 -> one, 2 -> two) +val b = a - 4 // b: Map(1 -> one, 2 -> two, 3 -> three) +val c = a - 4 - 3 // c: Map(1 -> one, 2 -> two) ``` ### Updating Map elements -To update elements in an immutable map, use the `updated` method while assigning the result to a new variable: +To update elements in an immutable map, use the `updated` method (or the `+` operator) while assigning the result to a new variable: ```scala val a = Map( @@ -497,7 +496,8 @@ val a = Map( 3 -> "three" ) -val b = a.updated(3, "THREE!") // Map(1 -> one, 2 -> two, 3 -> THREE!) +val b = a.updated(3, "THREE!") // b: Map(1 -> one, 2 -> two, 3 -> THREE!) +val c = a + (2 -> "TWO...") // c: Map(1 -> one, 2 -> TWO..., 3 -> three) ``` ### Traversing a Map @@ -511,7 +511,7 @@ val states = Map( "AZ" -> "Arizona" ) -for ((k,v) <- states) println(s"key: $k, value: $v") +for (k, v) <- states do println(s"key: $k, value: $v") ``` That being said, there are _many_ ways to work with the keys and values in a map. @@ -558,6 +558,8 @@ val c = b ++ Seq(4, 1, 5, 5) // HashSet(5, 1, 2, 3, 4) Notice that when you attempt to add duplicate elements, they’re quietly dropped. +Also notice that the order of iteration of the elements is arbitrary. + ### Deleting elements from a Set diff --git a/_overviews/scala3-book/collections-methods.md b/_overviews/scala3-book/collections-methods.md index 3e6c208a21..4316dea761 100644 --- a/_overviews/scala3-book/collections-methods.md +++ b/_overviews/scala3-book/collections-methods.md @@ -34,7 +34,7 @@ The following methods work on all of the sequence types, including `List`, `Vect ## Examples of common methods To give you an overview of what you’ll see in the following sections, these examples show some of the most commonly used collections methods. -First, here are some methods don’t use lambdas: +First, here are some methods that don’t use lambdas: ```scala val a = List(10, 20, 30, 40, 10) // List(10, 20, 30, 40, 10) @@ -103,7 +103,7 @@ a.map(double(_)) a.map(double) ``` -In the last example, when an anonymous function consists of one statement that takes a single argument, you don’t have to name the argument, so even `-` isn’t required. +In the last example, when an anonymous function consists of one function call that takes a single argument, you don’t have to name the argument, so even `_` isn’t required. Finally, you can combine HOFs as desired to solve problems: @@ -217,10 +217,10 @@ david ## `head` The `head` method comes from Lisp and other earlier functional programming languages. -It’s used to print the first element (the head element) of a list: +It’s used to access the first element (the head element) of a list: ```scala -oneToTen.head // Int = 1 +oneToTen.head // 1 names.head // adam ``` @@ -228,8 +228,8 @@ Because a `String` can be seen as a sequence of characters, you can also treat i This is how `head` works on these strings: ```scala -"foo".head // Char = 'f' -"bar".head // Char = 'b' +"foo".head // 'f' +"bar".head // 'b' ``` `head` is a great method to work with, but as a word of caution it can also throw an exception when called on an empty collection: @@ -242,7 +242,7 @@ emptyList.head // java.util.NoSuchElementException: head of empty Because of this you may want to use `headOption` instead of `head`, especially when programming in a functional style: ```scala -emptyList.headOption // Option[Int] = None +emptyList.headOption // None ``` As shown, it doesn’t throw an exception, it simply returns the type `Option` that has the value `None`. @@ -256,7 +256,7 @@ The `tail` method also comes from Lisp, and it’s used to print every element i A few examples demonstrate this: ```scala -oneToTen.head // Int = 1 +oneToTen.head // 1 oneToTen.tail // List(2, 3, 4, 5, 6, 7, 8, 9, 10) names.head // adam diff --git a/_overviews/scala3-book/fp-functional-error-handling.md b/_overviews/scala3-book/fp-functional-error-handling.md index ee33cffe11..d56dfff3a6 100644 --- a/_overviews/scala3-book/fp-functional-error-handling.md +++ b/_overviews/scala3-book/fp-functional-error-handling.md @@ -106,7 +106,7 @@ makeInt(x) match case None => println("That didn’t work.") ``` -In this example, if `x` can be converted to an `Int`, the first `case` statement is executed; if `x` can’t be converted to an `Int`, the second `case` statement is executed. +In this example, if `x` can be converted to an `Int`, the expression on the right-hand side of the first `case` clause is evaluated; if `x` can’t be converted to an `Int`, the expression on the right-hand side of the second `case` clause is evaluated. @@ -240,19 +240,20 @@ makeInt(x) match Getting back to `null` values, a place where a `null` value can silently creep into your code is with a class like this: ```scala -class Address: +class Address( var street1: String, var street2: String, - var city: String, - var state: String, + var city: String, + var state: String, var zip: String +) ``` While every address on Earth has a `street1` value, the `street2` value is optional. As a result, the `street2` field can be assigned a `null` value: ```scala -val santa = new Address( +val santa = Address( "1 Main Street", null, // <-- D’oh! A null value! "North Pole", @@ -265,18 +266,19 @@ Historically, developers have used blank strings and null values in this situati In Scala---and other modern languages---the correct solution is to declare up front that `street2` is optional: ```scala -class Address: +class Address( var street1: String, var street2: Option[String], // an optional value var city: String, var state: String, var zip: String +) ``` Now developers can write more accurate code like this: ```scala -val santa = new Address( +val santa = Address( "1 Main Street", None, // 'street2' has no value "North Pole", @@ -288,7 +290,7 @@ val santa = new Address( or this: ```scala -val santa = new Address( +val santa = Address( "123 Main Street", Some("Apt. 2B"), "Talkeetna", diff --git a/_overviews/scala3-book/types-inferred.md b/_overviews/scala3-book/types-inferred.md index fab485fecc..d56bafb629 100644 --- a/_overviews/scala3-book/types-inferred.md +++ b/_overviews/scala3-book/types-inferred.md @@ -12,7 +12,7 @@ As with other statically typed programming languages, in Scala you can _declare_ ```scala val x: Int = 1 -val x: Double = 1 +val y: Double = 1 ``` In those examples the types are _explicitly_ declared to be `Int` and `Double`, respectively. diff --git a/_overviews/scala3-book/types-type-classes.md b/_overviews/scala3-book/types-type-classes.md index 239866ecb7..24619309d1 100644 --- a/_overviews/scala3-book/types-type-classes.md +++ b/_overviews/scala3-book/types-type-classes.md @@ -23,7 +23,7 @@ In Scala 3, _type classes_ are just _traits_ with one or more type parameters, l trait Show[A]: def show(a: A): String ``` -Instances of `Show` for a particular type `A` witness that `A` we can show an instance of type `A`. +Instances of `Show` for a particular type `A` witness that we can show (i.e., produce a text representation of) an instance of type `A`. For example, let’s look at the following `Show` instance for `Int` values: ```scala @@ -43,7 +43,7 @@ toHtml(42)(ShowInt()) // results in "

The number is 42!

" ``` -#### Automatically passing Type Class Instances +#### Automatically passing type class instances Since type classes are a very important way to structure software, Scala 3 offers additional features that make working with them very convenient. We discuss these additional features (which fall into the category of *Contextual Abstractions*) in a [later chapter][typeclasses-chapter] of this book. diff --git a/_overviews/scala3-book/types-variance.md b/_overviews/scala3-book/types-variance.md index 5eec37b647..90ee0d04ba 100644 --- a/_overviews/scala3-book/types-variance.md +++ b/_overviews/scala3-book/types-variance.md @@ -22,11 +22,11 @@ Let us also assume the following parameterized types: trait Pipeline[T]: def process(t: T): T -// an example of an covariant type +// an example of a covariant type trait Producer[+T]: def make: T -// an example of an contravariant type +// an example of a contravariant type trait Consumer[-T]: def take(t: T): Unit ``` @@ -73,7 +73,7 @@ In contrast to `Pipeline`, which is invariant, the type `Producer` is marked as This is valid, since the type parameter is only used in a _return position_. Marking it as covariant means that we can pass (or return) a `Producer[Book]` where a `Producer[Buyable]` is expected. -And in fact, this is sound: The type of `Producer[Buyable].make` only promises to _return_ a `Buyable`. +And in fact, this is sound. The type of `Producer[Buyable].make` only promises to _return_ a `Buyable`. As a caller of `make`, we will be happy to also accept a `Book`, which is a subtype of `Buyable`---that is, it is _at least_ a `Buyable`. This is illustrated by the following example, where the function `makeTwo` expects a `Producer[Buyable]`: @@ -108,12 +108,12 @@ They have an additional ISBN method in our example, but you are free to ignore t In contrast to the type `Producer`, which is marked as covariant, the type `Consumer` is marked as **contravariant** by prefixing the type parameter with a `-`. This is valid, since the type parameter is only used in an _argument position_. -Marking it as contravariant means that we can pass (or return) a `Producer[Item]` where a `Producer[Buyable]` is expected. -That is, we have the subtyping relationship `Producer[Item] <: Producer[Buyable]`. -Remember, for type `Consumer`, it was the other way around, and we had `Consumer[Buyable] <: Consumer[Item]`. +Marking it as contravariant means that we can pass (or return) a `Consumer[Item]` where a `Consumer[Buyable]` is expected. +That is, we have the subtyping relationship `Consumer[Item] <: Consumer[Buyable]`. +Remember, for type `Producer`, it was the other way around, and we had `Producer[Buyable] <: Producer[Item]`. -And in fact, this is sound: The type of `Producer[Buyable].make` only promises us to _return_ a `Buyable`. -As a caller of `make`, we will be happy to also accept a `Book`, which is a subtype of `Buyable`---that is, it is _at least_ a `Buyable`. +And in fact, this is sound. The method `Consumer[Item].take` accepts an `Item`. +As a caller of `take`, we can also supply a `Buyable`, which will be happily accepted by the `Consumer[Item]` since `Buyable` is a subtype of `Item`---that is, it is _at least_ an `Item`. #### Contravariant Types for Consumers