From c6b25b5e5408bb27424993042a8162e17aacce94 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Wed, 23 Sep 2020 10:50:35 -0600 Subject: [PATCH 001/169] Added empty initial files. --- _overviews/overview/a-taste-of-scala.md | 17 +++++++++++++++++ .../overview/collections-classes-methods.md | 0 _overviews/overview/concurrency.md | 0 _overviews/overview/contextual-abstractions.md | 0 _overviews/overview/control-structures.md | 0 _overviews/overview/data-modeling.md | 0 _overviews/overview/first-look-at-types.md | 0 _overviews/overview/functional-programming.md | 0 _overviews/overview/interacting-with-java.md | 0 _overviews/overview/methods-and-functions.md | 0 .../overview/packaging-imports-exports.md | 0 _overviews/overview/scala-3-metaprogramming.md | 0 _overviews/overview/scala-features.md | 0 .../overview/scala-for-javascript-devs.md | 0 _overviews/overview/scala-for-python-devs.md | 0 _overviews/overview/scala-forjava-devs.md | 0 _overviews/overview/scala-tools.md | 0 _overviews/overview/two-types-of-variables.md | 0 _overviews/overview/types-and-type-system.md | 0 _overviews/overview/why-scala-3.md | 0 20 files changed, 17 insertions(+) create mode 100644 _overviews/overview/a-taste-of-scala.md create mode 100644 _overviews/overview/collections-classes-methods.md create mode 100644 _overviews/overview/concurrency.md create mode 100644 _overviews/overview/contextual-abstractions.md create mode 100644 _overviews/overview/control-structures.md create mode 100644 _overviews/overview/data-modeling.md create mode 100644 _overviews/overview/first-look-at-types.md create mode 100644 _overviews/overview/functional-programming.md create mode 100644 _overviews/overview/interacting-with-java.md create mode 100644 _overviews/overview/methods-and-functions.md create mode 100644 _overviews/overview/packaging-imports-exports.md create mode 100644 _overviews/overview/scala-3-metaprogramming.md create mode 100644 _overviews/overview/scala-features.md create mode 100644 _overviews/overview/scala-for-javascript-devs.md create mode 100644 _overviews/overview/scala-for-python-devs.md create mode 100644 _overviews/overview/scala-forjava-devs.md create mode 100644 _overviews/overview/scala-tools.md create mode 100644 _overviews/overview/two-types-of-variables.md create mode 100644 _overviews/overview/types-and-type-system.md create mode 100644 _overviews/overview/why-scala-3.md diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md new file mode 100644 index 0000000000..0b725618d0 --- /dev/null +++ b/_overviews/overview/a-taste-of-scala.md @@ -0,0 +1,17 @@ +--- +type: chapter +layout: multipage-overview +title: A Taste of Scala +description: This page provides a high-level overview of the main features of Scala 3. +partof: overview +overview-name: Overview +discourse: true +num: 1 +outof: 20 +previous-page: +next-page: preliminaries +--- + +Text coming soon ... + + diff --git a/_overviews/overview/collections-classes-methods.md b/_overviews/overview/collections-classes-methods.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/concurrency.md b/_overviews/overview/concurrency.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/contextual-abstractions.md b/_overviews/overview/contextual-abstractions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/control-structures.md b/_overviews/overview/control-structures.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/data-modeling.md b/_overviews/overview/data-modeling.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/first-look-at-types.md b/_overviews/overview/first-look-at-types.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/functional-programming.md b/_overviews/overview/functional-programming.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/interacting-with-java.md b/_overviews/overview/interacting-with-java.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/methods-and-functions.md b/_overviews/overview/methods-and-functions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/packaging-imports-exports.md b/_overviews/overview/packaging-imports-exports.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/scala-3-metaprogramming.md b/_overviews/overview/scala-3-metaprogramming.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/scala-features.md b/_overviews/overview/scala-features.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/scala-for-javascript-devs.md b/_overviews/overview/scala-for-javascript-devs.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/scala-for-python-devs.md b/_overviews/overview/scala-for-python-devs.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/scala-forjava-devs.md b/_overviews/overview/scala-forjava-devs.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/scala-tools.md b/_overviews/overview/scala-tools.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/two-types-of-variables.md b/_overviews/overview/two-types-of-variables.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/types-and-type-system.md b/_overviews/overview/types-and-type-system.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_overviews/overview/why-scala-3.md b/_overviews/overview/why-scala-3.md new file mode 100644 index 0000000000..e69de29bb2 From b39b7a450f337bc15d4c35fed05346d74f67f4eb Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Thu, 24 Sep 2020 21:28:55 -0600 Subject: [PATCH 002/169] This is an early draft to demonstrate my writing style. --- _overviews/overview/a-taste-of-scala.md | 1170 ++++++++++++++++++++++- 1 file changed, 1160 insertions(+), 10 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index 0b725618d0..3d4a85700f 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -1,17 +1,1167 @@ --- -type: chapter -layout: multipage-overview title: A Taste of Scala description: This page provides a high-level overview of the main features of Scala 3. -partof: overview -overview-name: Overview -discourse: true -num: 1 -outof: 20 -previous-page: -next-page: preliminaries --- -Text coming soon ... + + + + + +Our hope in this Overview documentation is to demonstrate that Scala is a beautiful, expressive programming language, with a clean, modern syntax. To help with that demonstration, this “Taste of Scala” section provides a whirlwind tour of Scala’s main features. After the initial tour in this section, the rest of the Overview will provide a few more details on the these features, and the Reference documentation will provide *many* more details. + +>In this Overview it’s assumed that you’ve used a language like Java before, and you’re ready to see a series of Scala examples to get a feel for what the language looks like. You’ll also be able to test many of the examples directly on this page, and in addition to that, you can also test anything you’d like on [ScalaFiddle.io](https://scalafiddle.io), [Scastie](https://scastie.scala-lang.org), in Worksheets with the VS Code or Intellij IDEA editors, or in the Scala REPL, which will be demonstrated shortly. + + + +## Hello, world + + + +Ever since the book, *C Programming Language*, it’s been a tradition to begin programming books with a “Hello, world” example, and not to disappoint, this is one way to write that example in Scala: + +```scala +@main def hello = println("Hello, world") +``` + +To see how this works, put that line of code in a file named *Hello.scala*, and then compile it with `scalac`: + +```sh +$ scalac Hello.scala +``` + +If you’re coming to Scala from Java, `scalac` is just like `javac`, so that command creates several files: + + +```sh +$ ls -1 +Hello$package$.class +Hello$package.class +Hello$package.tasty +Hello.scala +hello.class +hello.tasty +``` + + +Just like Java, the *.class* files are bytecode files, and they’re ready to run in the JVM. Now you can run the main `hello` method with the `scala` command: + +```sh +$ scala hello +Hello, world +``` + +Assuming that worked, congratulations, you just compiled and ran your first Scala application. + + + + +## The Scala REPL + +The Scala REPL (“Read-Evaluate-Print-Loop”) is a command-line interpreter that you use as a “playground” area to test your Scala code. We introduce it early here so you can use it with the code examples that follow. + +Assuming that you’ve downloaded and installed Scala — such as from the [Scala download page](https://www.scala-lang.org/download) — you start a REPL session by running the `scala` command at your operating system command line. When you do this you’ll see a “welcome” prompt that looks like this: + + +```scala +$ scala +Welcome to Scala 3.0.0 (OpenJDK 64-Bit Server VM, Java 11.0.7). +Type in expressions for evaluation. Or try :help. + +scala> _ +``` + +Because the REPL is a command-line interpreter, it sits there waiting for you to type something. Inside the REPL you can type Scala expressions to see how they work: + +```scala +scala> val x = 1 +x: Int = 1 + +scala> val y = x + 1 +y: Int = 2 +``` + +As those examples show, after you type your expressions in the REPL, it shows the result of each expression on the line following the prompt. + +As mentioned earlier, if you prefer a browser-based testing environment you can also use [ScalaFiddle.io](https://scalafiddle.io) or [Scastie.scala-lang.org](https://scastie.scala-lang.org). + + + +## Two types of variables + +When you create a new variable in Scala, you declare whether the variable is immutable or mutable: + +- `val` is an *immutable* variable — like `final` in Java. We recommend always creating a variable with `val`, unless there’s a specific reason you need a mutable variable. +- `var` creates a *mutable* variable, and should only be used when you have a variable whose contents will be modified over time. + +These examples show how to create `val` and `var` variables: + +```scala +val x = 1 // immutable +var y = 0 // mutable +``` + +In an application, once you create a `val` field, it can’t be reassigned. The second line of code here creates an error message: + +```scala +val msg = "Hello, world" +msg = "Hello" // "reassignment to val" error; this won’t compile +``` + +Conversely, a `var` field can be reassigned: + +```scala +var msg = "Hello, world" +msg = "Hello" // this compiles because a var can be reassigned +``` + +>Inside the REPL playground area you can reassign a `val` field, but this is just for convenience. In the real world a `val` field can’t be reassigned. + + + +## Declaring variable types + +In Scala you can declare variables without explicitly declaring their type: + +```scala +val x = 1 +val s = "a string" +val p = Person("Richard") +``` + +When you do this, the Scala compiler can usually infer the data type for you, as shown in the output of these REPL examples: + +```scala +scala> val x = 1 +val x: Int = 1 + +scala> val s = "a string" +val s: String = a string + +scala> val nums = List(1,2,3) +val nums: List[Int] = List(1, 2, 3) +``` + +This feature is known as *type inference*, and it’s a great way to help keep this type of code concise. You can also *explicitly* declare a variable’s type: + +```scala +val x: Int = 1 +val s: String = "a string" +val p: Person = new Person("Richard") +``` + +However, as you can see, in simple situations this isn’t needed, and tends to feel more verbose than necessary. + + + +## Built-in data types + +Scala comes with the standard numeric data types you’d expect, and all of these data types are full-blown instances of classes. In Scala, everything is an object. + +These examples show how to declare variables of the basic numeric types: + +```scala +val b: Byte = 1 +val x: Int = 1 +val l: Long = 1 +val s: Short = 1 +val d: Double = 2.0 +val f: Float = 3.0 +``` + +In the first four examples, if you don’t explicitly specify a type, the number `1` will default to an `Int`, so if you want one of the other data types — `Byte`, `Long`, or `Short` — you need to explicitly declare those types, as shown. Numbers with a decimal (like 2.0) will default to a `Double`, so if you want a `Float` you need to declare a `Float`, as shown in the last example. + +Because `Int` and `Double` are the default numeric types, you typically create them without explicitly declaring the data type: + +```scala +val i = 123 // defaults to Int +val x = 1.0 // defaults to Double +``` + + + +### BigInt and BigDecimal + +When you need really large numbers, Scala also includes the types `BigInt` and `BigDecimal`: + +```scala +var b = BigInt(1234567890) +var b = BigDecimal(123456.789) +``` + +A great thing about `BigInt` and `BigDecimal` is that they support all the operators you’re used to using with numeric types: + +```scala +scala> var b = BigInt(1234567890) +b: scala.math.BigInt = 1234567890 + +scala> b + b +res0: scala.math.BigInt = 2469135780 + +scala> b * b +res1: scala.math.BigInt = 1524157875019052100 + +scala> b += 1 + +scala> println(b) +1234567891 +``` + +While you can think of `+`, `*`, and `+=` as being “operators,” because everything in Scala is an object, they’re really methods on their classes. + + +### String and Char + +Scala also has `String` and `Char` data types, which you can generally declare with the implicit form: + +```scala +val name = "Bill" +val c = 'a' +``` + +However, you can use the explicit form, if you prefer: + +```scala +val name: String = "Bill" +val c: Char = 'a' +``` + +As shown, strings are enclosed in double-quotes — or triple-quotes, as you’re about to see — and a character is enclosed in single-quotes. + + + +## Two notes about strings + +Scala strings are similar to Java strings, but they have at least two great additional features: + +- They support string interpolation +- You can create multiline strings + + +### String interpolation + +In its most basic form, string interpolation provides a great way to use variables inside strings. For instance, given these three variables: + +```scala +val firstName = "John" +val mi = 'C' +val lastName = "Doe" +``` + +string interpolation lets you combine those variables in a string like this: + +```scala +val name = s"$firstName $mi $lastName" +``` + +This lets you embed variables inside strings in a very readable way: + +```scala +println(s"Name: $firstName $mi $lastName") +``` + +As shown, all you have to do is to precede the string with the letter `s`, and then put a `$` symbol before your variable names inside the string. + +It’s important to know that string interpolation goes well beyond this basic use. For instance, you can also access class fields and use equations inside the interpolated string by enclosing those references inside curly braces. This example shows how to reference a class field (or method) inside an interpolated string: + +```scala +class Person(var name: String) +val p = Person("Margaret Hamilton") +println(s"Name: ${p.name}") +``` + +This example shows how to use an equation: + +```scala +println(s"2 + 2 = ${2 + 2}") // prints '4' +``` + +#### Other interpolators + +“Why use the `s` in front of the string,” you may ask. The answer is that the `s` that precedes the strings in these examples is just one possible interpolator. You can also use the letter `f` in front of a string to use `printf`-style string formatting: + +```scala +val name = "Fred" +val age = 33 +val weight = 200.5 +println(f"$name is $age years old, and weighs $weight%.1f pounds.") +``` + + + +Very importantly, because `s` and `f` are really just methods, other developers can write their own interpolators. For instance, Scala database libraries can create an `sql` interpolator that can have special features to support SQL: + +```scala +val query = sql"select * from $table" +``` + + +>For more details about interpolators, see the Scala Reference documentation. + + + + +### Multiline strings + +A second great feature of Scala strings is that you can create multiline strings by including the string inside three double-quotes: + +```scala +val speech = """Four score and + seven years ago + our fathers ...""" +``` + + +That’s very helpful for when you need to work with multiline strings. One drawback of this basic approach is that the lines after the first line are indented, as you can see in the REPL: + +```scala +scala> val speech = """Four score and + | seven years ago + | our fathers ...""" +speech: String = +Four score and + seven years ago + our fathers ... +``` + +A simple way to correct this problem is to put a `|` symbol in front of all lines after the first line, and call the `stripMargin` method after the string: + +```scala +val speech = """Four score and + |seven years ago + |our fathers ...""".stripMargin +``` + +The REPL shows that when you do this, all of the lines are left-justified: + +```scala +scala> val speech = """Four score and + | |seven years ago + | |our fathers ...""".stripMargin +speech: String = +Four score and +seven years ago +our fathers ... +``` + +Because this is what you generally want, this is a common way to create multiline strings. + + + +## Control structures + + +Scala has all of the basic programming language control structures you find in other languages, and also has powerful `for` expressions and `match` expressions: + +- if/else +- `for` loops +- `match` expressions +- while, do/while +- try/catch + +These structures are demonstrated in the following examples. + + +### if/else + +Scala’s if/else control structure is similar to other languages. A multiline statement looks like this: + +```scala +if x < 0 then + "negative" +else if x == 0 + "zero" +else + "positive" +``` + +and a single-line expression looks like this: + +```scala +if x < 0 then -x else x +``` + +Note that this really is an *expression* — not a *statement* — meaning that it returns a value, and can therefore be used as a ternary operator: + +```scala +val x = if (a < b) a else b +``` + +In fact, as you’ll see throughout this Overview and in our Reference documentation, *all* Scala control structures can be used as expressions. + + +### for loops and expressions + + +In its most basic use, the `for` keyword can be used to create a `for` loop. For instance, given this list: + +```scala +// TODO introduce ranges first +val ints = (1 to 10).toList // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val ints = List.range(1,10) +``` + +you can print each element in the list like this: + +```scala +for (i <- ints) println(i) +``` + +You can also use one or more *guards* — `if` expressions — inside a `for` loop. This example prints all of the numbers in `ints` that are greater than `5`: + +```scala +for i <- ints if i > 5 +do println(i) +``` + +That statement can be written like this if you prefer: + +```scala +for + i <- ints + if i > 5 +do + println(i) +``` + +This style is commonly used, because it supports the use of multiple generators and guards. For instance, this example prints all of the even numbers in `ints` that are greater than `2`: + +```scala +// prints 4,6,8, and 10 on different lines +for + i <- ints + if i > 2 + if i % 2 == 0 +do + println(i) +``` + + +Simple Scala `for` loops look like this: + +```scala +for (arg <- args) println(arg) + +// "x to y" syntax +for (i <- 0 to 5) println(i) + +// "x to y by" syntax +for (i <- 0 to 10 by 2) println(i) +``` + +As shown in these examples, `for` loops are used for side effects, such as printing to the console. This use of the `for` keyword is just the beginning. When you add the `yield` keyword to `for` loops, you create powerful `for` *expressions* which are used to calculate and yield results. This REPL example shows how to use the for/yield combination to double each value in the sequence `1` to `5`: + +```scala +scala> val x = for i <- 1 to 5 yield i * 2 +val x: IndexedSeq[Int] = Vector(2, 4, 6, 8, 10) +``` + + + +Here’s another `for` expression that iterates over a list of strings: + + +```scala +val fruits = List("apple", "banana", "lime", "orange") + +val fruitLengths = for + f <- fruits + if f.length > 4 +yield + f.length + +// result: List[Int] = List(5, 6, 6) +``` + +Because Scala code generally just makes sense, you can probably guess how this code works, even if you’ve never seen a for-expression or Scala list until now. + + +`for` loops and expressions are covered in more detail in the Control Structures section. + + + + +### match expressions + +Scala has a `match` expression, which in its most basic use is like a Java `switch` statement: + +```scala +val i = 1 + +// later in the code ... +val result = i match + case 1 => "one" + case 2 => "two" + case _ => "not 1 or 2" +``` + +The `match` expression isn’t limited to just integers, it can be used with any data type, including booleans: + +```scala +val booleanAsString = bool match + case true => "true" + case false => "false" +``` + +Here’s an example of `match` being used as the body of a method, and matching against many different types: + +```scala +def getClassAsString(x: Any): String = x match + case s: String => s + " is a String" + case i: Int => "Int" + case f: Float => "Float" + case l: List[_] => "List" + case p: Person => "Person" + case _ => "Unknown" +``` + +Pattern matching is a significant feature of Scala, and you’ll see it used TODO (more here) + + + + + + + +### try/catch + +Scala’s try/catch control structure lets you catch exceptions. It’s similar to Java, but its syntax is consistent with `match` expressions: + + +```scala +try + writeToFile(text) +catch + case fnfe: FileNotFoundException => println(fnfe) + case ioe: IOException => println(ioe) +``` + + +### while loops + +Scala also has a `while` loop. It’s one-line syntax looks like this: + +```scala +while x >= 0 do x = f(x) +``` + +And it’s multiline syntax looks like this: + +```scala +var x = 1 +while (x < 3) + println(x) + x += 1 +``` + + + + + + +### Create your own control structures! + +- whilst +- doubleIfCOnditions + doubleif(age > 18)(numAccidents == 0) { println("Discount!") } + // two 'if' condition tests + def doubleif(test1: => Boolean)(test2: => Boolean)(codeBlock: => Unit) { if (test1 && test2) { + codeBlock + } } + + + +## Data Modeling + +Scala supports both functional programming (FP) and object-oriented programming (OOP), as well as a fusion of the two paradigms. This section provides an overview of data modeling in OOP and FP. + + +### OOP data modeling (traits and classes) + +When writing code in an OOP style, your two main tools will be *traits* and *classes*. + +#### Traits + + +Traits can be used like interfaces and abstract classes in other languages, and they can also be used as mixins. Traits let you break your code down into small, modular units. Traits are like interfaces in other languages, but they can also contain implemented methods. When you want to create concrete implementations of attributes and behaviors, classes and objects can extend traits. + +To demonstrate some things you can do with traits, here are three traits that define well-organized and modular behaviors for animals like dogs and cats: + +```scala +trait Speaker: + def speak(): String // has no body, so it’s abstract + +trait TailWagger: + def startTail(): Unit = println("tail is wagging") + def stopTail(): Unit = println("tail is stopped") + +trait Runner: + def startRunning(): Unit = println("I’m running") + def stopRunning(): Unit = println("Stopped running") +``` + +Once you have those traits, you can create a `Dog` class that extends all three of those traits while providing a behavior for the abstract `speak` method: + +```scala +class Dog(name: String) extends Speaker with TailWagger with Runner: + def speak(): String = "Woof!" +``` + +Similarly, here’s a `Cat` class that shows how to implement those same traits while also overriding two of the methods it inherits: + +```scala +class Cat extends Speaker with TailWagger with Runner: + def speak(): String = "Meow" + override def startRunning(): Unit = println("Yeah ... I don’t run") + override def stopRunning(): Unit = println("No need to stop") +``` + + +If that code makes sense — great, you’re comfortable with traits as interfaces. If not, don’t worry, they’re explained in more detail in the Data Modeling section of the Reference documentation. + + + + +#### Classes + + + +Scala classes are used in OOP-style programming. Here’s an example of a Scala class that models a “person,” where the person’s first and last names can both be modified (mutated): + +```scala +class Person(var firstName: String, var lastName: String): + def printFullName() = println(s"$firstName $lastName") +``` + +This is how you use that class: + +```scala +// TODO: update +val p = Person("Julia", "Kern") +println(p.firstName) +p.lastName = "Manes" +p.printFullName() +``` + + +Notice that the first and last names can be mutated because they’re declared as `var` fields. Also, there’s no need to create “get” and “set” methods to access the fields in the class. You’ll see more details about classes in the Data Modeling section. + +As a more complicated example, here’s a `Pizza` class that you’ll see later in the book: + +```scala +class Pizza ( + var crustSize: CrustSize, + var crustType: CrustType, + val toppings: ArrayBuffer[Topping] +) { + def addTopping(t: Topping): Unit = toppings += t + def removeTopping(t: Topping): Unit = toppings -= t + def removeAllToppings(): Unit = toppings.clear() +} +``` + +In that code, an `ArrayBuffer` is a mutable sequence, like Java’s `ArrayList`. The `CrustSize`, `CrustType`, and `Topping` classes aren’t shown, but you can probably understand how that code works without needing to see those classes. + + +### FP data modeling (TODO) + +When writing code in an FP style, you’ll use these constructs: + +- Traits +- Enums to define ADTs +- Case classes + + +#### Enums + + + + +```scala +enum CrustSize: + case Small, Medium, Large + +enum CrustType: + case Thin, Thick, Regular + +enum Topping: + case Cheese, Pepperoni, Olives +``` + + +```scala +case class Pizza: + crustSize: CrustSize, + crustType: CrustType, + toppings: Seq[Topping] + +@main def pizzaDemo = + import CrustSize._ + import CrustType._ + import Topping._ + + // passing enums as method parameters + val smallThinCheesePizza = Pizza( + Small, Thin, Seq(Cheese) + ) + + val largeThickWorks = Pizza( + Large, Thick, Seq(Cheese, Pepperoni, Olives) + ) + + println(smallThinCheesePizza) + println(largeThickWorks) + + printCrustSize(Small) + + // using an enum as a method parameter and in a match expression + def printCrustSize(cs: CrustSize) = cs match + case Small => println("small") + case Medium => println("medium") + case Large => println("large") +``` + + +##### Enums (WORKING) + +Enums can take parameters: + +```scala +// named values +enum Color: + case Red, Green, Blue + +// parameterized +enum Color(val rgb: Int): + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) + case Mix(mix: Int) extends Color(mix) + +//TODO show usage +val red = Color.Red +Color.valueOf("Blue") // res0: Color = Blue +Color.values // Array(Red, Green, Blue) + +// user-defined members +enum Planet(mass: Double, radius: Double): + private final val G = 6.67300E-11 + def surfaceGravity = G * mass / (radius * radius) + def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity + + case Mercury extends Planet(3.303e+23, 2.4397e6) + case Venus extends Planet(4.869e+24, 6.0518e6) + case Earth extends Planet(5.976e+24, 6.37814e6) + // more ... +} +``` + + + + + + + + + +#### Case classes + +A *case class* is different than the base Scala class. Case classes provide support for functional programming, and they have all of the functionality of a regular class, and more. When the compiler sees the `case` keyword in front of a `class`, it generates code for you, with the following benefits: + +- Case class constructor parameters are public `val` fields by default, so accessor methods are generated for each parameter. +- An `apply` method is created in the companion object of the class, so you don’t need to use the `new` keyword to create a new instance of the class. +- An `unapply` method is generated, which lets you use case classes in more ways in `match` expressions. +- A `copy` method is generated in the class. You may not use this feature in Scala/OOP code, but it’s used all the time in Scala/FP. +- `equals` and `hashCode` methods are generated, which let you compare objects and easily use them as keys in maps. +- A default `toString` method is generated, which is helpful for debugging. + +// BASIC +case class Person(name: String, relation: String) +val christina = Person("Christina", "niece") +christina.name +christina.name = "Fred" // error + +// MORE +trait Person: + def name: String +case class Student(name: String, year: Int) extends Person +case class Teacher(name: String, specialty: String) extends Person + +def getPrintableString(p: Person): String = p match { + case Student(name, year) => + s"$name is a student in Year $year." + case Teacher(name, whatTheyTeach) => + s"$name teaches $whatTheyTeach." +} + +val s = Student("Al", 1) +val t = Teacher("Bob Donnan", "Mathematics") +getPrintableString(s) +getPrintableString(t) + + +// `copy`: “update as you copy” +case class BaseballTeam(name: String, lastWorldSeriesWin: Int) +val cubs1908 = BaseballTeam("Chicago Cubs", 1908) +val cubs2016 = cubs1908.copy(lastWorldSeriesWin = 2016) + +// `equals` and `hashCode` +case class Person(name: String, relation: String) +val christina = Person("Christina", "niece") +val hannah = Person("Hannah", "niece") +christina == hannah + + + + + +## Scala methods + +Scala classes, case classes, and objects can all contain methods. This is what the Scala method syntax looks like: + +```scala +def sum(a: Int, b: Int): Int = a + b +def concatenate(s1: String, s2: String): String = s1 + s2 +``` + +You don’t have to declare a method’s return type, so it’s perfectly legal to write those two methods like this, if you prefer: + +```scala +def sum(a: Int, b: Int) = a + b +def concatenate(s1: String, s2: String) = s1 + s2 +``` + +This is how you call those methods: + +```scala +val x = sum(1, 2) +val y = concatenate("foo", "bar") +``` + +There are more things you can do with methods, such as providing default values for method parameters and using named parameters when calling methods, and those are shown in TODO + + + + + +## First-class functions (TODO) + +- top-level functions +- functions in objects +- lambdas / function literals / anonymous functions +- HOFs +- HOFs in the standard library + + + + +## Collections classes + +If you’re coming to Scala from Java and you’re ready to really jump in and learn Scala, it’s possible to use the Java collections classes in Scala, and some people do so for several weeks or months while getting comfortable with Scala. But it’s highly recommended that you learn the basic Scala collections classes — `List`, `ListBuffer`, `Vector`, `ArrayBuffer`, `Map`, and `Set` — as soon as possible. A great benefit of the Scala collections classes is that they offer many powerful methods that you’ll want to start using as soon as possible to simplify your code. + + + + +### Populating lists + +There are times when it’s helpful to create sample lists that are populated with data, and Scala offers many ways to populate lists. Here are just a few: + +```scala +val nums = List.range(0, 10) +val nums = (1 to 10 by 2).toList +val letters = ('a' to 'f').toList +val letters = ('a' to 'f' by 2).toList +``` + + +### Sequence methods + +While there are many sequential collections classes you can use — `Array`, `ArrayBuffer`, `Vector`, `List`, and more — let’s look at some examples of what you can do with the `List` class. Given these two lists: + +```scala +val nums = (1 to 10).toList +val names = List("joel", "ed", "chris", "maurice") +``` + +This is the `foreach` method: + +```scala +scala> names.foreach(println) +joel +ed +chris +maurice +``` + +Here’s the `filter` method, followed by `foreach`: + +```scala +scala> nums.filter(_ < 4).foreach(println) +1 +2 +3 +``` + +Here are some examples of the `map` method: + +```scala +scala> val doubles = nums.map(_ * 2) +doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) + +scala> val capNames = names.map(_.capitalize) +capNames: List[String] = List(Joel, Ed, Chris, Maurice) + +scala> val lessThanFive = nums.map(_ < 5) +lessThanFive: List[Boolean] = List(true, true, true, true, false, false, false, false, false, false) +``` + +Even without any explanation you can see how `map` works: It applies an algorithm you supply to every element in the collection, returning a new, transformed value for each element. + +If you’re ready to see one of the most powerful collections methods, here’s `foldLeft`: + +```scala +scala> nums.foldLeft(0)(_ + _) +res0: Int = 55 + +scala> nums.foldLeft(1)(_ * _) +res1: Int = 3628800 +``` + +Once you know that the first parameter to `foldLeft` is a *seed* value, you can guess that the first example yields the *sum* of the numbers in `nums`, and the second example returns the *product* of all those numbers. + +There are many (many!) more methods available to Scala collections classes, and many of them will be demonstrated in the collections lessons that follow, but hopefully this gives you an idea of their power. + +>For more details, jump to [the Scala Book collections lessons]({{site.baseurl}}/overviews/scala-book/collections-101.html), or see [the Mutable and Immutable collections overview]({{site.baseurl}}/overviews/collections-2.13/overview.html) for more details and examples. + + + +## Tuples (TODO: update this section) + +The Scala *tuple* is a class that lets you easily put a collection of different types in the same container. This is how you create a tuple that contains an `Int`, a `String`, and a `Boolean` value: + +```scala +val t = (11, "eleven", true) +``` + +You can see the type in the REPL: + +```` +scala> val t = (11, "eleven", true) +TODO: UPDATE +```` + +You can access the tuple values by number: + +```scala +t._1 +t._2 +t._3 +``` + +TODO: other methods: + +t.head +t.tail +t.drop(1) +t.take(1) +t.size +t.map ... + +Or assign the tuple fields to variables: + +```scala +val (num, string, good) = (11, "eleven", true) +``` + +```` +scala> val (num, string, person) = (11, "eleven", true) +TODO: UPDATE +```` + +Tuples are nice for those times when you need to put a little “bag” of things together for a little while. + + + +## Changes from Scala 2 +## New control syntax +## Optional braces, indentation + + + +## TODO: Other possible topics not in the outline + +- Multiversal equality +- Advanced types +- Ranges +```scala +for (arg <- args) println(arg) + +// "x to y" syntax +for (i <- 0 to 5) println(i) + +// "x to y by" syntax +for (i <- 0 to 10 by 2) println(i) +``` + + +## A small, sample application + +- “Putting it all together” + +- show an sbt example +- show several features +- @main + + + + + + + + + + From 4af19bbff092c40197b7fb86c83206cc4f43263d Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Thu, 24 Sep 2020 21:31:52 -0600 Subject: [PATCH 003/169] This is an early draft to demonstrate my writing style. --- _overviews/overview/a-taste-of-scala.md | 26 +------------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index 3d4a85700f..0944d3c1ef 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -8,31 +8,7 @@ description: This page provides a high-level overview of the main features of Sc - test with jekyll on my system --> - - - + Our hope in this Overview documentation is to demonstrate that Scala is a beautiful, expressive programming language, with a clean, modern syntax. To help with that demonstration, this “Taste of Scala” section provides a whirlwind tour of Scala’s main features. After the initial tour in this section, the rest of the Overview will provide a few more details on the these features, and the Reference documentation will provide *many* more details. >In this Overview it’s assumed that you’ve used a language like Java before, and you’re ready to see a series of Scala examples to get a feel for what the language looks like. You’ll also be able to test many of the examples directly on this page, and in addition to that, you can also test anything you’d like on [ScalaFiddle.io](https://scalafiddle.io), [Scastie](https://scastie.scala-lang.org), in Worksheets with the VS Code or Intellij IDEA editors, or in the Scala REPL, which will be demonstrated shortly. From 61e26ed7ea7e186ed38cb60f79a0b494b04e1ad9 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Mon, 28 Sep 2020 00:07:47 -0600 Subject: [PATCH 004/169] Added more topics while reducing the descriptive text. --- _overviews/overview/a-taste-of-scala.md | 1248 +++++++++++++---------- 1 file changed, 717 insertions(+), 531 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index 0944d3c1ef..cb83bb5b36 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -3,15 +3,18 @@ title: A Taste of Scala description: This page provides a high-level overview of the main features of Scala 3. --- - - -Our hope in this Overview documentation is to demonstrate that Scala is a beautiful, expressive programming language, with a clean, modern syntax. To help with that demonstration, this “Taste of Scala” section provides a whirlwind tour of Scala’s main features. After the initial tour in this section, the rest of the Overview will provide a few more details on the these features, and the Reference documentation will provide *many* more details. +This “Taste of Scala” section provides a whirlwind tour of Scala’s main features. After the initial tour in this section, the rest of the Overview will provide a few more details on the these features, and the Reference documentation will provide *many* more details. ->In this Overview it’s assumed that you’ve used a language like Java before, and you’re ready to see a series of Scala examples to get a feel for what the language looks like. You’ll also be able to test many of the examples directly on this page, and in addition to that, you can also test anything you’d like on [ScalaFiddle.io](https://scalafiddle.io), [Scastie](https://scastie.scala-lang.org), in Worksheets with the VS Code or Intellij IDEA editors, or in the Scala REPL, which will be demonstrated shortly. +>Throughout this Overview you’ll also be able to test many of the examples directly on this page. In addition to that, you can also test anything you’d like on [ScalaFiddle.io](https://scalafiddle.io), [Scastie](https://scastie.scala-lang.org), or in the Scala REPL, which is demonstrated shortly. @@ -19,13 +22,13 @@ Our hope in this Overview documentation is to demonstrate that Scala is a beauti -Ever since the book, *C Programming Language*, it’s been a tradition to begin programming books with a “Hello, world” example, and not to disappoint, this is one way to write that example in Scala: +A “Hello, world” example in Scala goes as follows. First, Put this code in a file named *Hello.scala*: ```scala @main def hello = println("Hello, world") ``` -To see how this works, put that line of code in a file named *Hello.scala*, and then compile it with `scalac`: +Next, compile the code with `scalac`: ```sh $ scalac Hello.scala @@ -44,8 +47,7 @@ hello.class hello.tasty ``` - -Just like Java, the *.class* files are bytecode files, and they’re ready to run in the JVM. Now you can run the main `hello` method with the `scala` command: +Like Java, the *.class* files are bytecode files, and they’re ready to run in the JVM. Now you can run the main `hello` method with the `scala` command: ```sh $ scala hello @@ -53,15 +55,12 @@ Hello, world ``` Assuming that worked, congratulations, you just compiled and ran your first Scala application. - ## The Scala REPL -The Scala REPL (“Read-Evaluate-Print-Loop”) is a command-line interpreter that you use as a “playground” area to test your Scala code. We introduce it early here so you can use it with the code examples that follow. - -Assuming that you’ve downloaded and installed Scala — such as from the [Scala download page](https://www.scala-lang.org/download) — you start a REPL session by running the `scala` command at your operating system command line. When you do this you’ll see a “welcome” prompt that looks like this: +The Scala REPL (“Read-Evaluate-Print-Loop”) is a command-line interpreter that you use as a “playground” area to test your Scala code. You start a REPL session by running the `scala` command at your operating system command line. When you do so, you’ll see a “welcome” prompt like this: ```scala @@ -72,19 +71,36 @@ Type in expressions for evaluation. Or try :help. scala> _ ``` -Because the REPL is a command-line interpreter, it sits there waiting for you to type something. Inside the REPL you can type Scala expressions to see how they work: +The REPL is a command-line interpreter, so it sits there waiting for you to type something. Now you can type Scala expressions to see how they work: -```scala -scala> val x = 1 -x: Int = 1 +```` +scala> 1 + 1 +val res0: Int = 2 -scala> val y = x + 1 -y: Int = 2 -``` +scala> 2 + 2 +val res1: Int = 4 +```` -As those examples show, after you type your expressions in the REPL, it shows the result of each expression on the line following the prompt. +As shown in the output, if you don’t assign a variable to the result of an expression, the REPL creates variables named `res0`, `res1`, etc., for you. You can use these variable names in future expressions: -As mentioned earlier, if you prefer a browser-based testing environment you can also use [ScalaFiddle.io](https://scalafiddle.io) or [Scastie.scala-lang.org](https://scastie.scala-lang.org). +```` +scala> val x = res0 * 10 +val x: Int = 20 +```` + +Notice that the REPL output also shows the result of your expressions. + +You can run all sorts of experiments in the REPL. This example shows how to create and then call a `sum` function: + +```` +scala> def sum(a: Int, b: Int): Int = a + b +def sum(a: Int, b: Int): Int + +scala> sum(2, 2) +val res2: Int = 4 +```` + +As mentioned earlier, if you prefer a browser-based playground environment, you can also use [ScalaFiddle.io](https://scalafiddle.io) or [Scastie.scala-lang.org](https://scastie.scala-lang.org). @@ -92,17 +108,20 @@ As mentioned earlier, if you prefer a browser-based testing environment you can When you create a new variable in Scala, you declare whether the variable is immutable or mutable: -- `val` is an *immutable* variable — like `final` in Java. We recommend always creating a variable with `val`, unless there’s a specific reason you need a mutable variable. -- `var` creates a *mutable* variable, and should only be used when you have a variable whose contents will be modified over time. +- `val` creates an *immutable* variable — like `final` in Java. You should always create a variable with `val`, unless there’s a reason you need a mutable variable. +- `var` creates a *mutable* variable, and should only be used when a variable’s contents will change over time. These examples show how to create `val` and `var` variables: ```scala -val x = 1 // immutable -var y = 0 // mutable +val a = 0 // immutable +val b = "Hello" + +var c = 1 // mutable +var d = "world" ``` -In an application, once you create a `val` field, it can’t be reassigned. The second line of code here creates an error message: +In an application, a `val` field can’t be reassigned. You’ll generate a compiler error if you try to reassign one: ```scala val msg = "Hello, world" @@ -116,21 +135,20 @@ var msg = "Hello, world" msg = "Hello" // this compiles because a var can be reassigned ``` ->Inside the REPL playground area you can reassign a `val` field, but this is just for convenience. In the real world a `val` field can’t be reassigned. +>Inside the REPL playground area you can reassign a `val` field, but this is just for convenience. ## Declaring variable types -In Scala you can declare variables without explicitly declaring their type: +You can declare variables by explicitly declaring their type, or by letting the compiler infer the type: ```scala -val x = 1 -val s = "a string" -val p = Person("Richard") +val x: Int = 1 // explicit +val x = 1 // implicit; the compiler infers the type ``` -When you do this, the Scala compiler can usually infer the data type for you, as shown in the output of these REPL examples: +The second form is known as *type inference*, and it’s a great way to help keep this type of code concise. The Scala compiler can usually infer the data type for you, as shown in the output of these examples: ```scala scala> val x = 1 @@ -143,7 +161,7 @@ scala> val nums = List(1,2,3) val nums: List[Int] = List(1, 2, 3) ``` -This feature is known as *type inference*, and it’s a great way to help keep this type of code concise. You can also *explicitly* declare a variable’s type: +You can always explicitly declare a variable’s type if you prefer, but in simple assignments like these it isn’t necessary: ```scala val x: Int = 1 @@ -151,15 +169,15 @@ val s: String = "a string" val p: Person = new Person("Richard") ``` -However, as you can see, in simple situations this isn’t needed, and tends to feel more verbose than necessary. +Notice that with this approach, the code feels more verbose than necessary. ## Built-in data types -Scala comes with the standard numeric data types you’d expect, and all of these data types are full-blown instances of classes. In Scala, everything is an object. +Scala comes with the standard numeric data types you’d expect, and they’re all full-blown instances of classes. In Scala, everything is an object. -These examples show how to declare variables of the basic numeric types: +These examples show how to declare variables of the numeric types: ```scala val b: Byte = 1 @@ -170,78 +188,48 @@ val d: Double = 2.0 val f: Float = 3.0 ``` -In the first four examples, if you don’t explicitly specify a type, the number `1` will default to an `Int`, so if you want one of the other data types — `Byte`, `Long`, or `Short` — you need to explicitly declare those types, as shown. Numbers with a decimal (like 2.0) will default to a `Double`, so if you want a `Float` you need to declare a `Float`, as shown in the last example. - Because `Int` and `Double` are the default numeric types, you typically create them without explicitly declaring the data type: ```scala val i = 123 // defaults to Int -val x = 1.0 // defaults to Double +val j = 1.0 // defaults to Double ``` - - - -### BigInt and BigDecimal -When you need really large numbers, Scala also includes the types `BigInt` and `BigDecimal`: +In your code you can also use the characters `L`, `D`, and `F` (and their lowercase equivalents) to specify `Long`, `Double`, and `Float` fields: ```scala -var b = BigInt(1234567890) -var b = BigDecimal(123456.789) +val x = 1_000L // val x: Long = 1000 +val y = 2.2D // val y: Double = 2.2 +val z = 3.3F // val z: Float = 3.3 ``` -A great thing about `BigInt` and `BigDecimal` is that they support all the operators you’re used to using with numeric types: +When you need really large numbers, use the `BigInt` and `BigDecimal` types: ```scala -scala> var b = BigInt(1234567890) -b: scala.math.BigInt = 1234567890 - -scala> b + b -res0: scala.math.BigInt = 2469135780 - -scala> b * b -res1: scala.math.BigInt = 1524157875019052100 - -scala> b += 1 - -scala> println(b) -1234567891 -``` - -While you can think of `+`, `*`, and `+=` as being “operators,” because everything in Scala is an object, they’re really methods on their classes. - - -### String and Char - -Scala also has `String` and `Char` data types, which you can generally declare with the implicit form: - -```scala -val name = "Bill" -val c = 'a' +var a = BigInt(1_234_567_890_987_654_321L) +var b = BigDecimal(123_456.789) ``` -However, you can use the explicit form, if you prefer: +Scala also has `String` and `Char` data types: ```scala -val name: String = "Bill" -val c: Char = 'a' +val name = "Bill" // String +val c = 'a' // Char ``` -As shown, strings are enclosed in double-quotes — or triple-quotes, as you’re about to see — and a character is enclosed in single-quotes. - ## Two notes about strings -Scala strings are similar to Java strings, but they have at least two great additional features: +Scala strings are similar to Java strings, but they have two great additional features: - They support string interpolation -- You can create multiline strings +- It’s easy to create multiline strings ### String interpolation -In its most basic form, string interpolation provides a great way to use variables inside strings. For instance, given these three variables: +String interpolation provides a very readable way to use variables inside strings. For instance, given these three variables: ```scala val firstName = "John" @@ -249,32 +237,25 @@ val mi = 'C' val lastName = "Doe" ``` -string interpolation lets you combine those variables in a string like this: +String interpolation lets you combine those variables in a string like this: ```scala -val name = s"$firstName $mi $lastName" +println(s"Name: $firstName $mi $lastName") // "Name: John C Doe" ``` -This lets you embed variables inside strings in a very readable way: +Just precede the string with the letter `s`, and then put a `$` symbol before your variable names inside the string. -```scala -println(s"Name: $firstName $mi $lastName") -``` - -As shown, all you have to do is to precede the string with the letter `s`, and then put a `$` symbol before your variable names inside the string. - -It’s important to know that string interpolation goes well beyond this basic use. For instance, you can also access class fields and use equations inside the interpolated string by enclosing those references inside curly braces. This example shows how to reference a class field (or method) inside an interpolated string: +To enclose class fields, class methods, or equations inside a string, enclose them in curly braces: ```scala class Person(var name: String) val p = Person("Margaret Hamilton") -println(s"Name: ${p.name}") -``` -This example shows how to use an equation: +// a class field or method +println(s"Name: ${p.name}") // "Name: Margaret Hamilton" -```scala -println(s"2 + 2 = ${2 + 2}") // prints '4' +// an equation +println(s"2 + 2 = ${2 + 2}") // prints "2 + 2 = 4" ``` #### Other interpolators @@ -285,77 +266,57 @@ println(s"2 + 2 = ${2 + 2}") // prints '4' val name = "Fred" val age = 33 val weight = 200.5 + println(f"$name is $age years old, and weighs $weight%.1f pounds.") ``` + - - -Very importantly, because `s` and `f` are really just methods, other developers can write their own interpolators. For instance, Scala database libraries can create an `sql` interpolator that can have special features to support SQL: +Beyond that, because `s` and `f` are really just methods, developers can write their own interpolators. For instance, Scala database libraries can create an `sql` interpolator that can have special features to support SQL: ```scala val query = sql"select * from $table" ``` - ->For more details about interpolators, see the Scala Reference documentation. - - - ### Multiline strings -A second great feature of Scala strings is that you can create multiline strings by including the string inside three double-quotes: +Multiline strings are created by including the string inside three double-quotes: ```scala -val speech = """Four score and - seven years ago - our fathers ...""" +val quote = """The essence of Scala: + Fusion of functional and object-oriented + programming in a typed setting.""" ``` - -That’s very helpful for when you need to work with multiline strings. One drawback of this basic approach is that the lines after the first line are indented, as you can see in the REPL: +One drawback of this basic approach is that the lines after the first line are indented, and look like this: ```scala -scala> val speech = """Four score and - | seven years ago - | our fathers ...""" -speech: String = -Four score and - seven years ago - our fathers ... +"The essence of Scala: + Fusion of functional and object-oriented + programming in a typed setting." ``` A simple way to correct this problem is to put a `|` symbol in front of all lines after the first line, and call the `stripMargin` method after the string: ```scala -val speech = """Four score and - |seven years ago - |our fathers ...""".stripMargin +val quote = """The essence of Scala: + |Fusion of functional and object-oriented + |programming in a typed setting.""".stripMargin ``` -The REPL shows that when you do this, all of the lines are left-justified: +Now all of the lines are left-justified inside the string: ```scala -scala> val speech = """Four score and - | |seven years ago - | |our fathers ...""".stripMargin -speech: String = -Four score and -seven years ago -our fathers ... +"The essence of Scala: +Fusion of functional and object-oriented +programming in a typed setting." ``` -Because this is what you generally want, this is a common way to create multiline strings. - ## Control structures -Scala has all of the basic programming language control structures you find in other languages, and also has powerful `for` expressions and `match` expressions: +Scala has the programming language control structures you find in other languages, and also has powerful `for` expressions and `match` expressions: -- if/else -- `for` loops +- `if`/`else` +- `for` loops and expressions - `match` expressions -- while, do/while -- try/catch +- `while` loops +- `try`/`catch` These structures are demonstrated in the following examples. ### if/else -Scala’s if/else control structure is similar to other languages. A multiline statement looks like this: +Scala’s if/else control structure is similar to other languages: ```scala if x < 0 then - "negative" + println("negative") else if x == 0 - "zero" + println("zero") else - "positive" + println("positive") ``` -and a single-line expression looks like this: - -```scala -if x < 0 then -x else x -``` - -Note that this really is an *expression* — not a *statement* — meaning that it returns a value, and can therefore be used as a ternary operator: +Note that this really is an *expression* — not a *statement* — meaning that it returns a value, so you can assign the result to a variable: ```scala val x = if (a < b) a else b ``` -In fact, as you’ll see throughout this Overview and in our Reference documentation, *all* Scala control structures can be used as expressions. +As you’ll see throughout this Overview and in our Reference documentation, *all* Scala control structures can be used as expressions. + +>An expression returns a result, while a statement does not. Statements are typically used for their side-effects, such as using `println` to print to the console. ### for loops and expressions @@ -412,88 +369,77 @@ In fact, as you’ll see throughout this Overview and in our Reference documenta - for/yield --> -In its most basic use, the `for` keyword can be used to create a `for` loop. For instance, given this list: +The `for` keyword can be used to create a `for` loop. This example shows how to print every element in a `List`: ```scala -// TODO introduce ranges first -val ints = (1 to 10).toList // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) -val ints = List.range(1,10) -``` +val ints = List(1,2,3,4,5) -you can print each element in the list like this: - -```scala for (i <- ints) println(i) ``` -You can also use one or more *guards* — `if` expressions — inside a `for` loop. This example prints all of the numbers in `ints` that are greater than `5`: + -```scala -for i <- ints if i > 5 -do println(i) -``` +#### Guards -That statement can be written like this if you prefer: +You can also use one or more `if` expressions inside a `for` loop. These are referred to as *guards*. This example prints all of the numbers in `ints` that are greater than `2`: + + ```scala +val ints = List(1,2,3,4,5) + for i <- ints - if i > 5 + if i > 2 do println(i) ``` -This style is commonly used, because it supports the use of multiple generators and guards. For instance, this example prints all of the even numbers in `ints` that are greater than `2`: +You can use multiple generators and guards. This loop iterates over the numbers `1` to `3`, and for each number it also iterates over the characters `a` to `c`. However, it also has two guards, so the only time the print statement is called is when `i` has the value `2` and `j` is the character `b`: -```scala -// prints 4,6,8, and 10 on different lines -for - i <- ints - if i > 2 - if i % 2 == 0 -do - println(i) -``` - +```tut for i <- 1 to 3 j <- 'a' to 'c' if i == 2 if j == 'b' do - println(s"i = $i, j = $j") ---> + println(s"i = $i, j = $j") // prints: "i = 2, j = b" +``` -Simple Scala `for` loops look like this: +We encourage you to make changes to that code to be sure you understand how it works. -```scala -for (arg <- args) println(arg) -// "x to y" syntax -for (i <- 0 to 5) println(i) +#### Using `for` as an expression -// "x to y by" syntax -for (i <- 0 to 10 by 2) println(i) -``` +The `for` keyword has even more power: When you add the `yield` keyword to `for` loops, you create powerful `for` *expressions* which are used to calculate and yield results. -As shown in these examples, `for` loops are used for side effects, such as printing to the console. This use of the `for` keyword is just the beginning. When you add the `yield` keyword to `for` loops, you create powerful `for` *expressions* which are used to calculate and yield results. This REPL example shows how to use the for/yield combination to double each value in the sequence `1` to `5`: +A few examples demonstrate this. Given this list: ```scala -scala> val x = for i <- 1 to 5 yield i * 2 -val x: IndexedSeq[Int] = Vector(2, 4, 6, 8, 10) +val nums = List(1,2,3,4,5) ``` - +This code creates a new list where each element in the new list is twice the amount of the elements in the original list: + +```` +scala> val doubles = for i <- nums yield i * 2 +val doubles: List[Int] = List(2, 4, 6, 8, 10) +```` + +This example shows how to capitalize the first character in each string in the list: -Here’s another `for` expression that iterates over a list of strings: +```scala +val names = List("chris", "ed", "maurice") +val capNames = for name <- names yield name.capitalize +``` + +Finally, this `for` expression iterates over a list of strings, and returns the length of each string, but only if that length is greater than `4`: - + ```scala val fruits = List("apple", "banana", "lime", "orange") @@ -508,18 +454,8 @@ yield Because Scala code generally just makes sense, you can probably guess how this code works, even if you’ve never seen a for-expression or Scala list until now. - -`for` loops and expressions are covered in more detail in the Control Structures section. - - + +`for` loops and expressions are covered in more detail in the Control Structures sections of this Overview and in the Reference documentation. ### match expressions @@ -530,76 +466,68 @@ Scala has a `match` expression, which in its most basic use is like a Java `swit val i = 1 // later in the code ... +i match + case 1 => println("one") + case 2 => println("two") + case _ => println("other") +``` + +However, a `match` expression really is an expression, meaning that it returns a result based on the pattern match: + +```scala val result = i match - case 1 => "one" - case 2 => "two" - case _ => "not 1 or 2" + case 1 => "one" + case 2 => "two" + case _ => "other" ``` The `match` expression isn’t limited to just integers, it can be used with any data type, including booleans: ```scala val booleanAsString = bool match - case true => "true" - case false => "false" + case true => "true" + case false => "false" ``` -Here’s an example of `match` being used as the body of a method, and matching against many different types: +In fact, a `match` expression can be used to test a variable against multiple patterns. This example shows (a) how to use a `match` expression as the body of a method, and (b) how to match all the different types shown: + ```scala def getClassAsString(x: Any): String = x match - case s: String => s + " is a String" - case i: Int => "Int" - case f: Float => "Float" - case l: List[_] => "List" - case p: Person => "Person" - case _ => "Unknown" -``` - -Pattern matching is a significant feature of Scala, and you’ll see it used TODO (more here) - - - + case s: String => s"'$s' is a String" + case i: Int => "Int" + case d: Double => "Double" + case l: List[_] => "List" + case _ => "Unknown" +// examples +getClassAsString(1) // Int +getClassAsString("hello") // 'hello' is a String +getClassAsString(List(1,2,3)) // List +``` +Pattern matches can get as complicated as you need, and you’ll see more examples of it in the Control Structures sections of this Overview and in the Reference documentation. -### try/catch +### try/catch/finally -Scala’s try/catch control structure lets you catch exceptions. It’s similar to Java, but its syntax is consistent with `match` expressions: +Scala’s `try`/`catch` control structure lets you catch exceptions. It’s similar to Java, but its syntax is consistent with `match` expressions: - + ```scala try - writeToFile(text) + writeToFile(text) catch - case fnfe: FileNotFoundException => println(fnfe) - case ioe: IOException => println(ioe) + case fnfe: FileNotFoundException => println(fnfe) + case ioe: IOException => println(ioe) ``` ### while loops -Scala also has a `while` loop. It’s one-line syntax looks like this: + + +Scala also has a `while` loop construct. It’s one-line syntax looks like this: ```scala while x >= 0 do x = f(x) @@ -609,39 +537,32 @@ And it’s multiline syntax looks like this: ```scala var x = 1 + +// parentheses while (x < 3) - println(x) - x += 1 -``` + println(x) + x += 1 - - - - + println(x) + x += 1 +``` ### Create your own control structures! -- whilst -- doubleIfCOnditions - doubleif(age > 18)(numAccidents == 0) { println("Discount!") } - // two 'if' condition tests - def doubleif(test1: => Boolean)(test2: => Boolean)(codeBlock: => Unit) { if (test1 && test2) { - codeBlock - } } +In Scala you can also create code that works just like a control structure. You can learn more about this in the Control Structures chapter in the Reference documentation. + ## Data Modeling -Scala supports both functional programming (FP) and object-oriented programming (OOP), as well as a fusion of the two paradigms. This section provides an overview of data modeling in OOP and FP. +Scala supports both functional programming (FP) and object-oriented programming (OOP), as well as a fusion of the two paradigms. This section provides a quick overview of data modeling in OOP and FP. ### OOP data modeling (traits and classes) @@ -656,49 +577,63 @@ When writing code in an OOP style, your two main tools will be *traits* and *cla - traits as mixins --> -Traits can be used like interfaces and abstract classes in other languages, and they can also be used as mixins. Traits let you break your code down into small, modular units. Traits are like interfaces in other languages, but they can also contain implemented methods. When you want to create concrete implementations of attributes and behaviors, classes and objects can extend traits. +Traits are like interfaces in other languages, but they can also contain implemented methods. They provide a great way for you to organize behaviors into small, modular units. When you want to create concrete implementations of attributes and behaviors, classes and objects can extend traits, mixing in as many traits as needed to achieve the desired behavior. -To demonstrate some things you can do with traits, here are three traits that define well-organized and modular behaviors for animals like dogs and cats: +Here are three traits that define well-organized and modular behaviors for animals like dogs and cats: ```scala trait Speaker: - def speak(): String // has no body, so it’s abstract + def speak(): String // has no body, so it’s abstract trait TailWagger: - def startTail(): Unit = println("tail is wagging") - def stopTail(): Unit = println("tail is stopped") + def startTail(): Unit = println("tail is wagging") + def stopTail(): Unit = println("tail is stopped") trait Runner: - def startRunning(): Unit = println("I’m running") - def stopRunning(): Unit = println("Stopped running") + def startRunning(): Unit = println("I’m running") + def stopRunning(): Unit = println("Stopped running") ``` -Once you have those traits, you can create a `Dog` class that extends all three of those traits while providing a behavior for the abstract `speak` method: +Given those traits, here’s a `Dog` class that extends all of those traits while providing a behavior for the abstract `speak` method: ```scala class Dog(name: String) extends Speaker with TailWagger with Runner: - def speak(): String = "Woof!" + def speak(): String = "Woof!" +``` + +Notice how the class extends the traits with the `extends` and `with` keywords. + +Similarly, here’s a `Cat` class that implements those same traits while also overriding two of the concrete methods it inherits: + +```scala +class Cat(name: String) extends Speaker with TailWagger with Runner: + def speak(): String = "Meow" + override def startRunning(): Unit = println("Yeah ... I don’t run") + override def stopRunning(): Unit = println("No need to stop") ``` -Similarly, here’s a `Cat` class that shows how to implement those same traits while also overriding two of the methods it inherits: +These examples show how those classes are used: ```scala -class Cat extends Speaker with TailWagger with Runner: - def speak(): String = "Meow" - override def startRunning(): Unit = println("Yeah ... I don’t run") - override def stopRunning(): Unit = println("No need to stop") +val d = Dog("Rover") +d.speak() // prints "Woof!" + +val c = Cat("Morris") +c.speak() // "Meow" +c.startRunning() // "Yeah ... I don’t run" +c.stopRunning() // "No need to stop" ``` -If that code makes sense — great, you’re comfortable with traits as interfaces. If not, don’t worry, they’re explained in more detail in the Data Modeling section of the Reference documentation. +If that code makes sense — great, you’re comfortable with traits as interfaces. If not, don’t worry, they’re explained in more detail in the Data Modeling sections of this Overview and the Reference documentation. - + #### Classes -Scala classes are used in OOP-style programming. Here’s an example of a Scala class that models a “person,” where the person’s first and last names can both be modified (mutated): +Scala *classes* are used in OOP-style programming. Here’s an example of a class that models a “person.” The first and last names can both be modified, so they’re declared as `var` parameters: ```scala class Person(var firstName: String, var lastName: String): - def printFullName() = println(s"$firstName $lastName") -``` + def printFullName() = println(s"$firstName $lastName") -This is how you use that class: - -```scala -// TODO: update val p = Person("Julia", "Kern") -println(p.firstName) +println(p.firstName) // "Julia" p.lastName = "Manes" -p.printFullName() -``` - - -Notice that the first and last names can be mutated because they’re declared as `var` fields. Also, there’s no need to create “get” and “set” methods to access the fields in the class. You’ll see more details about classes in the Data Modeling section. - -As a more complicated example, here’s a `Pizza` class that you’ll see later in the book: - -```scala -class Pizza ( - var crustSize: CrustSize, - var crustType: CrustType, - val toppings: ArrayBuffer[Topping] -) { - def addTopping(t: Topping): Unit = toppings += t - def removeTopping(t: Topping): Unit = toppings -= t - def removeAllToppings(): Unit = toppings.clear() -} +p.printFullName() // "Julia Manes" ``` -In that code, an `ArrayBuffer` is a mutable sequence, like Java’s `ArrayList`. The `CrustSize`, `CrustType`, and `Topping` classes aren’t shown, but you can probably understand how that code works without needing to see those classes. - -### FP data modeling (TODO) +### FP data modeling When writing code in an FP style, you’ll use these constructs: -- Traits - Enums to define ADTs - Case classes +- Traits #### Enums @@ -767,182 +678,119 @@ When writing code in an FP style, you’ll use these constructs: - make compatible with java by extending java.lang.Enum --> - +The `enum` construct is a great way to model algebraic data types (ADTs) in Scala 3. For instance, a pizza has three main attributes: + +- Crust size +- Crust type +- Toppings + +These are concisely modeled with enums: ```scala enum CrustSize: - case Small, Medium, Large + case Small, Medium, Large enum CrustType: - case Thin, Thick, Regular + case Thin, Thick, Regular enum Topping: - case Cheese, Pepperoni, Olives + case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions ``` +Once you have an enum you can use it in all of the ways you normally use a trait, class, or object: ```scala -case class Pizza: - crustSize: CrustSize, - crustType: CrustType, - toppings: Seq[Topping] +import CrustSize._ +val currentCrustSize = Small -@main def pizzaDemo = - import CrustSize._ - import CrustType._ - import Topping._ - - // passing enums as method parameters - val smallThinCheesePizza = Pizza( - Small, Thin, Seq(Cheese) - ) +// enums in a `match` expression +currentCrustSize match + case Small => println("Small crust size") + case Medium => println("Medium crust size") + case Large => println("Large crust size") - val largeThickWorks = Pizza( - Large, Thick, Seq(Cheese, Pepperoni, Olives) - ) - - println(smallThinCheesePizza) - println(largeThickWorks) - - printCrustSize(Small) - - // using an enum as a method parameter and in a match expression - def printCrustSize(cs: CrustSize) = cs match - case Small => println("small") - case Medium => println("medium") - case Large => println("large") +// enums in an `if` statement +if currentCrustSize == Small then println("Small crust size") ``` - -##### Enums (WORKING) - -Enums can take parameters: +Enums can also take parameters and have user-defined members like fields and methods. Here’s a sneak-peek of those capabilities: ```scala -// named values -enum Color: - case Red, Green, Blue - -// parameterized -enum Color(val rgb: Int): - case Red extends Color(0xFF0000) - case Green extends Color(0x00FF00) - case Blue extends Color(0x0000FF) - case Mix(mix: Int) extends Color(mix) - -//TODO show usage -val red = Color.Red -Color.valueOf("Blue") // res0: Color = Blue -Color.values // Array(Red, Green, Blue) - -// user-defined members enum Planet(mass: Double, radius: Double): private final val G = 6.67300E-11 def surfaceGravity = G * mass / (radius * radius) - def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity case Mercury extends Planet(3.303e+23, 2.4397e6) case Venus extends Planet(4.869e+24, 6.0518e6) - case Earth extends Planet(5.976e+24, 6.37814e6) // more ... } ``` - - - - - - +Enums are covered in detail in the Data Modeling section of this Overview, and in the Reference documentation. #### Case classes -A *case class* is different than the base Scala class. Case classes provide support for functional programming, and they have all of the functionality of a regular class, and more. When the compiler sees the `case` keyword in front of a `class`, it generates code for you, with the following benefits: +A *case class* is an extension of the base Scala class. Case classes provide features that make them useful for functional programming. They have all of the functionality of a regular class, and more. When the compiler sees the `case` keyword in front of a `class`, it generates code for you, with these benefits: - Case class constructor parameters are public `val` fields by default, so accessor methods are generated for each parameter. - An `apply` method is created in the companion object of the class, so you don’t need to use the `new` keyword to create a new instance of the class. - An `unapply` method is generated, which lets you use case classes in more ways in `match` expressions. -- A `copy` method is generated in the class. You may not use this feature in Scala/OOP code, but it’s used all the time in Scala/FP. -- `equals` and `hashCode` methods are generated, which let you compare objects and easily use them as keys in maps. +- A `copy` method is generated in the class. This provides a way to clone an object while making updates to its values during the cloning process. +- `equals` and `hashCode` methods are generated. - A default `toString` method is generated, which is helpful for debugging. -// BASIC -case class Person(name: String, relation: String) -val christina = Person("Christina", "niece") -christina.name -christina.name = "Fred" // error - -// MORE -trait Person: - def name: String -case class Student(name: String, year: Int) extends Person -case class Teacher(name: String, specialty: String) extends Person - -def getPrintableString(p: Person): String = p match { - case Student(name, year) => - s"$name is a student in Year $year." - case Teacher(name, whatTheyTeach) => - s"$name teaches $whatTheyTeach." -} +This code demonstrates several case class features: + +```scala +// define a case class +case class Person( + name: String, + vocation: String +) -val s = Student("Al", 1) -val t = Teacher("Bob Donnan", "Mathematics") -getPrintableString(s) -getPrintableString(t) +// create an instance of the case class +val p = Person("Reginald Kenneth Dwight", "Singer") +// a good default toString method +p // Person = Person(Reginald Kenneth Dwight,Singer) -// `copy`: “update as you copy” -case class BaseballTeam(name: String, lastWorldSeriesWin: Int) -val cubs1908 = BaseballTeam("Chicago Cubs", 1908) -val cubs2016 = cubs1908.copy(lastWorldSeriesWin = 2016) +// can access its fields, but they are immutable +p.name // "Reginald Kenneth Dwight" +p.name = "Joe" // error: can’t reassign a val field -// `equals` and `hashCode` -case class Person(name: String, relation: String) -val christina = Person("Christina", "niece") -val hannah = Person("Hannah", "niece") -christina == hannah +// when you need to make a change, use the `copy` method +// to “update as you copy” +val p2 = p.copy(name = "Elton John") +p2 // Person = Person(Elton John,Singer) +``` - +See the Data Modeling sections of this Overview and the Reference documentation for many more details on case classes. ## Scala methods -Scala classes, case classes, and objects can all contain methods. This is what the Scala method syntax looks like: + + +Scala classes, case classes, traits, enums, and objects can all contain methods. The general method syntax looks like this: + +```scala +def methodName(param1: Type1, param2: Type2): ReturnType = + // the method body + // goes here +``` + +Here are a few examples of that syntax: ```scala def sum(a: Int, b: Int): Int = a + b def concatenate(s1: String, s2: String): String = s1 + s2 ``` -You don’t have to declare a method’s return type, so it’s perfectly legal to write those two methods like this, if you prefer: +You don’t have to declare a method’s return type, so you can write those methods like this, if you prefer: ```scala def sum(a: Int, b: Int) = a + b @@ -956,172 +804,510 @@ val x = sum(1, 2) val y = concatenate("foo", "bar") ``` -There are more things you can do with methods, such as providing default values for method parameters and using named parameters when calling methods, and those are shown in TODO +Here’s an example of a multiline method: - +```scala +def getStackTraceAsString(t: Throwable): String = + val sw = new StringWriter + t.printStackTrace(new PrintWriter(sw)) + sw.toString +``` +There are more things you can do with methods, such as providing default values for method parameters and using named parameters when calling methods. Those featurs are shown in the Data Modeling section of this Overview and in the Reference documentation. -## First-class functions (TODO) -- top-level functions -- functions in objects -- lambdas / function literals / anonymous functions -- HOFs -- HOFs in the standard library +## First-class functions +Scala has first-class support for functional programming, and included in that is first-class support for everything related to functions: +- Top-level functions +- Functions in objects +- Lambdas +- Higher-order functions (HOFs) + + -## Collections classes -If you’re coming to Scala from Java and you’re ready to really jump in and learn Scala, it’s possible to use the Java collections classes in Scala, and some people do so for several weeks or months while getting comfortable with Scala. But it’s highly recommended that you learn the basic Scala collections classes — `List`, `ListBuffer`, `Vector`, `ArrayBuffer`, `Map`, and `Set` — as soon as possible. A great benefit of the Scala collections classes is that they offer many powerful methods that you’ll want to start using as soon as possible to simplify your code. +### Top-level functions - +Functions don’t have to be in the body of a class, enum, or object. They can also exist on their own as a *top-level function*. For instance, when this code is saved in a file, the functions `add` and `double` would be called top-level functions because they exist outside the scope of a class, enum, or object: +```scala +package foo -### Populating lists +def add(a: Int, b: Int): Int = a + b +def double(a: Int): Int = 2 * a -There are times when it’s helpful to create sample lists that are populated with data, and Scala offers many ways to populate lists. Here are just a few: +@main def topLevelFunctions() = + val x = add(1, 2) + val y = double(x) + println(s"y = $y") +``` + + +### Functions in objects + +Functions can also be placed in objects. Here are a few functions in a “string utilities” object: ```scala -val nums = List.range(0, 10) -val nums = (1 to 10 by 2).toList -val letters = ('a' to 'f').toList -val letters = ('a' to 'f' by 2).toList +object StringUtils: + + // Left-trim a string: `" foo "` becomes `"foo "`. + def leftTrim(s: String) = s.replaceAll("^\\s+", "") + + // Right-trim a string: `" foo "` becomes `" foo"`. + def rightTrim(s: String) = s.replaceAll("\\s+$", "") + + def isNullOrEmpty(s: String): Boolean = + if (s==null || s.trim.equals("")) true else false + +end StringUtils ``` +Those functions can be called like this: -### Sequence methods +```scala +StringUtils.leftTrim(" hi ") // "hi " +``` -While there are many sequential collections classes you can use — `Array`, `ArrayBuffer`, `Vector`, `List`, and more — let’s look at some examples of what you can do with the `List` class. Given these two lists: +or this: ```scala -val nums = (1 to 10).toList -val names = List("joel", "ed", "chris", "maurice") +import StringUtils._ +leftTrim(" hi ") // "hi " ``` -This is the `foreach` method: + +### Lambdas + +Lambdas — also known as *anonymous functions* — are a big part of keeping your code concise but readable. These two examples are equivalent, and show how to multiply each number in a list by `2` by passing a lambda into the `map` method: ```scala -scala> names.foreach(println) -joel -ed -chris -maurice +val a = List(1,2,3).map(i => i * 2) // List(2,4,6) +val b = List(1,2,3).map(_ * 2) // List(2,4,6) ``` -Here’s the `filter` method, followed by `foreach`: +Those examples are also equivalent to that code, but they use a `double` method inside of `map` instead of a lambda: ```scala -scala> nums.filter(_ < 4).foreach(println) -1 -2 -3 +def double(i: Int): Int = i * 2 +val a = List(1,2,3).map(i => double(i)) +val b = List(1,2,3).map(double) ``` -Here are some examples of the `map` method: +>If you haven’t seen the `map` method before, it applies a given function to every element in a list, yielding a new list that contains the new values. + + +### Higher-order functions + +A “higher order function” (HOF) is a function that can take a function as an input parameter, or returns a function as a return value. HOFs are extremely common in Scala, such as being used throughout the standard libraries. These examples show how to pass methods and anonymous functions into the functional methods of Scala collections: ```scala -scala> val doubles = nums.map(_ * 2) -doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) +// a sample list +val nums = (1 to 10).toList // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) -scala> val capNames = names.map(_.capitalize) -capNames: List[String] = List(Joel, Ed, Chris, Maurice) +// sample methods +def isEven(i: Int): Boolean = i % 2 == 0 +def double(i: Int): Int = i * 2 -scala> val lessThanFive = nums.map(_ < 5) -lessThanFive: List[Boolean] = List(true, true, true, true, false, false, false, false, false, false) +// pass in a method or function +val a = nums.filter(isEven) // List(2, 4, 6, 8, 10) +val b = nums.map(double) // List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) + +// pass in a lambda +val c = nums.filter(_ % 2 == 0) // List(2, 4, 6, 8, 10) +val d = nums.map(_ * 2) // List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) + +// methods can be chained as needed +val e = nums.filter(_ > 3) // List(40, 50, 60) + .filter(_ < 7) + .map(_ * 10) ``` -Even without any explanation you can see how `map` works: It applies an algorithm you supply to every element in the collection, returning a new, transformed value for each element. +In addition to HOFs used all throughout the standard library, you can easily create your own HOFs. This is shown in the Reference documentation. + + -If you’re ready to see one of the most powerful collections methods, here’s `foldLeft`: +## Extension methods + + +*Extension methods* let you add new methods to closed classes. For instance, if you want to add a `hello` method to the `String` class, just create an extension method: + + ```scala -scala> nums.foldLeft(0)(_ + _) -res0: Int = 55 +def (s: String) hello: String = s"Hello, ${s.capitalize}" -scala> nums.foldLeft(1)(_ * _) -res1: Int = 3628800 +"world".hello // "Hello, world" +"friend".hello // "Hello, friend" ``` -Once you know that the first parameter to `foldLeft` is a *seed* value, you can guess that the first example yields the *sum* of the numbers in `nums`, and the second example returns the *product* of all those numbers. +The comments in this code explain the extension method syntax: + +```` +def (s: String) hello: String = s"Hello, ${s.capitalize}" + ---------- ----- ------ ------------------------- + ^ ^ ^ ^ + add method method return method body + to String name type + class +```` + +This next example shows how to add a `makeInt` method to the `String` class. Here, `makeInt` takes a parameter named `radix`. The code doesn’t account for possible string-to-integer conversion errors, but skipping that detail, the examples show how it works: -There are many (many!) more methods available to Scala collections classes, and many of them will be demonstrated in the collections lessons that follow, but hopefully this gives you an idea of their power. + +```scala +def (s: String) makeInt(radix: Int): Int = Integer.parseInt(s, radix) +"1".makeInt(2) // Int = 1 +"10".makeInt(2) // Int = 2 +"100".makeInt(2) // Int = 4 +``` ->For more details, jump to [the Scala Book collections lessons]({{site.baseurl}}/overviews/scala-book/collections-101.html), or see [the Mutable and Immutable collections overview]({{site.baseurl}}/overviews/collections-2.13/overview.html) for more details and examples. +## Collections classes + +Scala has a rich set of collections classes, and those classes have a rich set of methods. Collections classes are available in both immutable and mutable forms. In alphabetical order, the basic collections classes you’ll use on a regular basis are: -## Tuples (TODO: update this section) +| Class | Mutable | Immutable | Description | +| ------------- | :-----: | :-------: | ----------- | +| `ArrayBuffer` | √ | | an indexed, mutable sequence | +| `List` | | √ | a linear (linked list), immutable sequence | +| `Map` | √ | √ | the base `Map` (key/value pairs) class | +| `Set` | √ | √ | the base `Set` class | +| `Vector` | | √ | an indexed, immutable sequence | -The Scala *tuple* is a class that lets you easily put a collection of different types in the same container. This is how you create a tuple that contains an `Int`, a `String`, and a `Boolean` value: +Scala also has a `Range` class which lets you create ordered sequences of integers that are equally spaced apart, such as “1, 2, 3,” and “5, 8, 11, 14.” Ranges are often used in `for` loops, and to create other sequences: ```scala -val t = (11, "eleven", true) +// a range in a for-loop +for (i <- 1 to 3) print(i) // 123 + +// use ranges to create other collections +(1 to 5).toList // List(1, 2, 3, 4, 5) +(1 to 10 by 2).toVector // Vector(1, 3, 5, 7, 9) +(1 to 10).toSet // Set[Int] = HashSet(5, 10, 1, 6, 9, 2, 7, 3, 8, 4) + +('a' to 'f').toList // List[Char] = List(a, b, c, d, e, f) +('a' to 'f' by 2).toList // List[Char] = List(a, c, e) + +List.range(1, 5) // List(1, 2, 3, 4) +List.range(1, 5, 2) // List(1, 3) ``` -You can see the type in the REPL: + + -```` -scala> val t = (11, "eleven", true) -TODO: UPDATE -```` +### Immutable collections classes -You can access the tuple values by number: +Some of the most-commonly used *immutable* collections classes are: + +| Class | Description | +| ------------- | ------------- | +| `List` | A finite immutable linked-list. | +| `LazyList` | Like a `List` except that its elements are computed lazily. Because of this, a lazy list can be infinitely long. | +| `Vector` | A sequential collection type that provides good performance for all its operations. | +| `Map` | Like maps in Java, dictionaries in Python, or a `HashMap` in Rust, `Map` is an `Iterable` that contains pairs of keys and values. | +| `Set` | An `Iterable` sequence that contains no duplicate elements. | +| `Stack` | A last-in-first-out sequence. | +| `Queue` | A first-in-first-out sequence. | +In addition to the previous `Range` examples, here are a few more ways to create immutable collections: + + ```scala -t._1 -t._2 -t._3 +val a = List(1,2,3) +val b = Vector("hello", "world") + +// create a List with a Range +val c = (1 to 5).toList + +val m = Map( + 1 -> "one", + 2 -> "two" +) ``` -TODO: other methods: -t.head -t.tail -t.drop(1) -t.take(1) -t.size -t.map ... +### Mutable collections classes + +Mutable Scala collections classes are located in the *scala.collection.mutable* package. These are some of the most commonly-used: + +| Class | Description | +| ------------- | ------------- | +| `ArrayBuffer` | An indexed mutable buffer backed by an array. | +| `ListBuffer` | A mutable buffer backed by a linked list. Use this class if you’ll use it like a `List`, or if you’ll convert it to a `List` once it’s built up. | +| `Map` | A mutable `Map`, contains pairs of keys and values. | +| `Set` | A mutable `Set`, a sequence that contains no duplicate elements. | +| `Stack` | A mutable, last-in-first-out sequence. | +| `Queue` | A mutable, first-in-first-out sequence. | + +These examples show a few ways to use an `ArrayBuffer`: + +```scala +import scala.collection.mutable.ArrayBuffer + +val a = ArrayBuffer(1,2,3) // ArrayBuffer(1, 2, 3) + +val b = ArrayBuffer[String]() // ArrayBuffer[String] = ArrayBuffer() +b += "Hello" // ArrayBuffer(Hello) +b ++= List("world", "it’s", "me") // ArrayBuffer(Hello, world, it’s, me) +``` + +In addition to those classes, Scala has an `Array` class, which is a special kind of collection. An `Array` corresponds one-to-one to a Java `array`, so its elements can be mutated, but its size can’t be changed. In addition to behaving like a Java `array`, (a) it can hold generic types, and (b) it supports all of the usual sequence methods (which you’re about to see). + + + +### Sequence methods + + + +A great benefit of the Scala collections classes is that they offer dozens of powerful functional methods you can use to simplify your code. These are just a few of the common methods available to sequences: + +| Method | Description | +| ------------- | ------------- | +| `map` | Creates a new collection by applying a function to all the elements of the collection. | +| `filter` | Returns all elements from the collection for which a given predicate is true. | +| `foreach` | Applies a function to all elements of the collection to produce a side effect. | +| `take`, `takeWhile` | Returns elements from the beginning of the collection. | +| `drop`, `dropWhile` | Returns all elements in the collection except the first elements that are specified. | +| `flatten` | Converts a sequence of sequences (such as a list of lists) to a single sequence (single list). | +| `foldLeft` | Applies an operation to successive elements, going from left to right, using an initial seed value | +| `reduce` | The same as `foldLeft`, but without an initial seed value. | + +There are dozens of additional methods, which you’ll see in the Collections section of this Overview, and in the Reference documentation. + +>A key to know about all of these methods is that they are *functional*: they don’t modify the existing sequence; they return a new collection by applying the method to the original sequence. + +These examples demonstrate some of the commonly-used sequence methods: + +```scala +// a sample list +val a = List(10, 20, 30, 40, 10) // List(10, 20, 30, 40, 10) + +a.drop(2) // List(30, 40, 10) +a.dropRight(2) // List(10, 20, 30) +a.dropWhile(_ < 25) // List(30, 40, 10) +a.filter(_ < 25) // List(10, 20, 10) +a.find(_ > 20) // Some(30) +a.head // 10 +a.headOption // Some(10) +a.last // 10 +a.lastOption // Some(10) +a.slice(2,4) // List(30, 40) +a.tail // List(20, 30, 40, 10) +a.take(3) // List(10, 20, 30) +a.takeRight(2) // List(40, 10) +a.takeWhile(_ < 30) // List(10, 20) + +val a = List(List(1,2), List(3,4)) +a.flatten // List(1, 2, 3, 4) + +val nums = List("one", "two") +nums.map(_.toUpperCase) // List("ONE", "TWO") +nums.flatMap(_.toUpperCase) // List('O', 'N', 'E', 'T', 'W', 'O') +``` + +These examples show how the “fold” and “reduce” methods are used to sum up every element in a sequence: + +```scala +val firstTen = (1 to 10).toList // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +firstTen.fold(100)(_ + _) // 155 +firstTen.foldLeft(100)(_ + _) // 155 +firstTen.reduce(_ + _) // 55 +firstTen.reduceLeft(_ + _) // 55 +``` + +There are many (many!) more methods available to Scala collections classes, and they’re demonstrated in the Collections sections of this Overview and in the Reference documentation. + + +### Tuples + + + +The Scala *tuple* is a type that lets you easily put a collection of different types in the same container. For example, given this `Person` case class: + +```scala +case class Person(name: String) +``` -Or assign the tuple fields to variables: +This is how you create a tuple that contains an `Int`, a `String`, and a custom `Person` value: ```scala -val (num, string, good) = (11, "eleven", true) +val t = (11, "eleven", Person("Eleven")) ``` +You can access the tuple values by number: + +```scala +t._1 // 11 +t._2 // "eleven" +t._3 // Person("Eleven") +``` + +You can also assign the tuple fields to variables, as shown in the REPL: + ```` -scala> val (num, string, person) = (11, "eleven", true) -TODO: UPDATE +scala> val (num, string, name) = (11, "eleven", Person("Eleven")) +val num: Int = 11 +val string: String = eleven +val good: Boolean = Person(Eleven) ```` -Tuples are nice for those times when you need to put a little “bag” of things together for a little while. +Tuples are nice for those times when you want to put a collection of heterogenous types in a little collection-like structure. + + + + +## Contextual Abstractions + + +Implicits in Scala 2 were a main distinguishing feature. Following Haskell, Scala was the second popular language to have some form of implicits. Other languages have since followed suit. + +Implicits are a fundamental way to abstract over context. They represent a unified paradigm with a great variety of use cases, among them: + +- Implementing type classes +- Establishing context +- Dependency injection +- Expressing capabilities +- Computing new types and proving relationships between them + +Even though different languages use widely different terminology, they’re all variants of the core idea of *term inference*. Given a type, the compiler synthesizes a “canonical” term that has that type. Scala embodies the idea in a purer form than most other languages: An implicit parameter directly leads to an inferred argument term that could also be written down explicitly. + +In Scala 3 the syntax for these constructs has been changed significantly to make their usage more clear. Implicits are defined with the keyword `given`, and in later code they are referenced with the keyword `using`. + + +### Term inference + +Here’s a quick example of how to use term inference in Scala 3. Imagine that you have a situation where a value is passed into a series of function calls, such as using an `ExecutionContext` when you’re working with parallel programming: + +```scala +doX(a, executionContext) +doY(b, c, executionContext) +doZ(d, executionContext) +``` + +Because this type of code is repetitive and makes the code harder to read, you’d prefer to write it like this instead: + +```scala +doX(a) +doY(b, c) +doZ(d) +``` + +The functions still use the `executionContext` variable, but they don’t require you to explicitly state that. As shown, the shorter code is more easy to read. + +When you want a solution like this, Scala 3’s term inference solution is what you’re looking for. The solution involves multiple steps: + +1. Define the code you want the compiler to discover using the Scala 3 `given` keyword. +1. When declaring the “implicit” parameter your function will use, put it in a separate parameter group and define it with the `using` keyword. +1. Make sure your `given` value is in the current context when your function is called. + +The following example demonstrates these steps with the use of an `Adder` trait and two `given` values that implement the `Adder` trait’s `add` method. + + +### Step 1: Define your “given instances” + +In the first step you’ll typically create a parameterized trait: + +```scala +trait Adder[T]: + def add(a: T, b: T): T +``` + +Then you’ll implement the trait using one or more `given` instances, which you define like this: + +```scala +given intAdder as Adder[Int]: + def add(a: Int, b: Int): Int = a + b + +given stringAdder as Adder[String]: + def add(a: String, b: String): String = "" + (a.toInt + b.toInt) +``` +In this example, `intAdder` is an instance of `Adder[Int]`, and defines an `add` method that works with `Int` values. Similarly, `stringAdder` is an instance of `Adder[String]` and provides an `add` method that takes two strings, converts them to `Int` values, adds them together, and returns the sum as a `String`. (To keep things simple the code doesn’t account for potential string-to-integer errors.) + +If you’re familiar with creating implicits in Scala 2, this new approach is similar to that process. The idea is the same, it’s just that the syntax has changed. + + +### Step 2: Declare the parameter your function will use with the `using` keyword + +Next, define your functions that use the `Adder` instances. When doing this, specify the `Adder` parameter with the `using` keyword. Put the parameter in a separate parameter group, as shown here: + +```scala +def genericAdder[A](x: A, y: A)(using adder: Adder[A]): A = + adder.add(x, y) +``` + +The keys here are that the `adder` parameter is defined with the `using` keyword in that separate parameter group: + +```scala +def genericAdder[A](x: A, y: A)(using adder: Adder[A]): A = + ----------------------- +``` + +Also notice that `genericAdder` declares the generic type `A`. This function doesn’t know if it will be used to add two integers or two strings — it just calls the `add` method of the `adder` parameter. + +>In Scala 2, parameters like this were declared using the `implicit` keyword, but now, as the entire programming industry has various implementations of this concept, this parameter is known in Scala 3 as a *context parameter*. + + +### Step 3: Make sure everything is in the current context + +Finally, assuming that `intAdder`, `stringAdder`, and `genericAdder` are all in scope, your code can call the `genericAdder` function with `Int` and `String` values, without having to pass instances of `intAdder` and `stringAdder` into `genericAdder`: + +```scala +println(genericAdder(1, 1)) // 2 +println(genericAdder("2", "2")) // 4 +``` + +The Scala compiler is smart enough to know that `intAdder` should be used in the first instance, and `stringAdder` should be used in the second instance. This is because the first example uses two `Int` parameters and the second example uses two `String` values. + + + +## Even more! + +Scala has even more features that weren’t covered in this whirlwind tour. See the remainder of this Overview and the Reference documentation for many more details. + + + + + From 8a1d6443a2708ab7d1e1149bbd3eb7c61bc0d0b8 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Tue, 29 Sep 2020 15:10:57 -0600 Subject: [PATCH 005/169] Worked on the first half of Jonathan's comments. --- _overviews/overview/a-taste-of-scala.md | 90 ++++++++++++------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index cb83bb5b36..c0eba4bdb3 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -10,6 +10,10 @@ description: This page provides a high-level overview of the main features of Sc - show a method with default parameters - show package "exports" - cover Option/Try/Either (functional error handling) + - do a search/replace on the word “field,” make sure it’s only used in classes, + objects, traits + - don’t use "field," do use "variables," "binders," or maybe something + like "assignment" --> This “Taste of Scala” section provides a whirlwind tour of Scala’s main features. After the initial tour in this section, the rest of the Overview will provide a few more details on the these features, and the Reference documentation will provide *many* more details. @@ -22,7 +26,7 @@ This “Taste of Scala” section provides a whirlwind tour of Scala’s main fe -A “Hello, world” example in Scala goes as follows. First, Put this code in a file named *Hello.scala*: +A “Hello, world” example in Scala goes as follows. First, put this code in a file named *Hello.scala*: ```scala @main def hello = println("Hello, world") @@ -81,7 +85,7 @@ scala> 2 + 2 val res1: Int = 4 ```` -As shown in the output, if you don’t assign a variable to the result of an expression, the REPL creates variables named `res0`, `res1`, etc., for you. You can use these variable names in future expressions: +As shown in the output, if you don’t assign a variable to the result of an expression, the REPL creates variables named `res0`, `res1`, etc., for you. You can use these variable names in subsequent expressions: ```` scala> val x = res0 * 10 @@ -121,22 +125,20 @@ var c = 1 // mutable var d = "world" ``` -In an application, a `val` field can’t be reassigned. You’ll generate a compiler error if you try to reassign one: +In an application, a `val` can’t be reassigned. You’ll generate a compiler error if you try to reassign one: ```scala val msg = "Hello, world" msg = "Hello" // "reassignment to val" error; this won’t compile ``` -Conversely, a `var` field can be reassigned: +Conversely, a `var` can be reassigned: ```scala var msg = "Hello, world" msg = "Hello" // this compiles because a var can be reassigned ``` ->Inside the REPL playground area you can reassign a `val` field, but this is just for convenience. - ## Declaring variable types @@ -245,16 +247,9 @@ println(s"Name: $firstName $mi $lastName") // "Name: John C Doe" Just precede the string with the letter `s`, and then put a `$` symbol before your variable names inside the string. -To enclose class fields, class methods, or equations inside a string, enclose them in curly braces: +For expressions more complex than a single identifier, enclose the expression in curly braces: ```scala -class Person(var name: String) -val p = Person("Margaret Hamilton") - -// a class field or method -println(s"Name: ${p.name}") // "Name: Margaret Hamilton" - -// an equation println(s"2 + 2 = ${2 + 2}") // prints "2 + 2 = 4" ``` @@ -417,19 +412,21 @@ We encourage you to make changes to that code to be sure you understand how it w The `for` keyword has even more power: When you add the `yield` keyword to `for` loops, you create powerful `for` *expressions* which are used to calculate and yield results. -A few examples demonstrate this. Given this list: - -```scala -val nums = List(1,2,3,4,5) -``` - -This code creates a new list where each element in the new list is twice the amount of the elements in the original list: +A few examples demonstrate this. Using the same `ints` list as the previous example, this code creates a new list, where the value of each element in the new list is twice the value of the elements in the original list: ```` -scala> val doubles = for i <- nums yield i * 2 +scala> val doubles = for (i <- ints) yield i * 2 val doubles: List[Int] = List(2, 4, 6, 8, 10) ```` +Note that Scala’s syntax is flexible, and that `for` expression can be written in several different ways to make your code more readable: + +```scala +val doubles = for i <- ints yield i * 2 +val doubles = for (i <- ints) yield i * 2 +val doubles = for (i <- ints) yield (i * 2) +``` + This example shows how to capitalize the first character in each string in the list: ```scala @@ -506,7 +503,7 @@ getClassAsString("hello") // 'hello' is a String getClassAsString(List(1,2,3)) // List ``` -Pattern matches can get as complicated as you need, and you’ll see more examples of it in the Control Structures sections of this Overview and in the Reference documentation. +There’s much more to pattern matching in Scala. Patterns can be nested, results of patterns can be bound, and pattern matching can even be user-defined. You can find more pattern matching examples in the Control Structures section of this Overview, and in the Reference documentation. ### try/catch/finally @@ -553,10 +550,7 @@ do ### Create your own control structures! -In Scala you can also create code that works just like a control structure. You can learn more about this in the Control Structures chapter in the Reference documentation. - +Thanks to features like by-name parameters, infix notation, fluent interfaces, optional parentheses, extension methods, and higher-order functions, you can also create code that works just like a control structure. You’ll learn more about this in the Control Structures chapter in the Reference documentation. @@ -645,7 +639,7 @@ If that code makes sense — great, you’re comfortable with traits as interfac - Traits or abstract classes are always open, so open is redundant for them. --> -Scala *classes* are used in OOP-style programming. Here’s an example of a class that models a “person.” The first and last names can both be modified, so they’re declared as `var` parameters: +Scala *classes* are used in OOP-style programming. Here’s an example of a class that models a “person.” In OOP, fields are typically mutable, so `firstName` and `lastName` are both declared as `var` parameters: ```scala class Person(var firstName: String, var lastName: String): @@ -731,6 +725,11 @@ Enums are covered in detail in the Data Modeling section of this Overview, and i #### Case classes + + A *case class* is an extension of the base Scala class. Case classes provide features that make them useful for functional programming. They have all of the functionality of a regular class, and more. When the compiler sees the `case` keyword in front of a `class`, it generates code for you, with these benefits: - Case class constructor parameters are public `val` fields by default, so accessor methods are generated for each parameter. @@ -939,32 +938,27 @@ In addition to HOFs used all throughout the standard library, you can easily cre ## Extension methods -*Extension methods* let you add new methods to closed classes. For instance, if you want to add a `hello` method to the `String` class, just create an extension method: +*Extension methods* let you add new methods to closed classes. For instance, if you want to add two methods named `hello` and `aloha` to the `String` class, just declare them as extension methods: ```scala -def (s: String) hello: String = s"Hello, ${s.capitalize}" +extension (s: String): + def hello: String = s"Hello, ${s.capitalize}" + def aloha: String = s"Aloha, ${s.capitalize}" -"world".hello // "Hello, world" -"friend".hello // "Hello, friend" +"world".hello // "Hello, World" +"friend".aloha // "Aloha, Friend" ``` -The comments in this code explain the extension method syntax: - -```` -def (s: String) hello: String = s"Hello, ${s.capitalize}" - ---------- ----- ------ ------------------------- - ^ ^ ^ ^ - add method method return method body - to String name type - class -```` +The `extension` keyword declares that you’re about to define one or more extension methods. As shown, the `s` parameter can then be used in your methods. This next example shows how to add a `makeInt` method to the `String` class. Here, `makeInt` takes a parameter named `radix`. The code doesn’t account for possible string-to-integer conversion errors, but skipping that detail, the examples show how it works: ```scala -def (s: String) makeInt(radix: Int): Int = Integer.parseInt(s, radix) +extension (s: String) + def makeInt(radix: Int): Int = Integer.parseInt(s, radix) + "1".makeInt(2) // Int = 1 "10".makeInt(2) // Int = 2 "100".makeInt(2) // Int = 4 @@ -978,11 +972,11 @@ Scala has a rich set of collections classes, and those classes have a rich set o | Class | Mutable | Immutable | Description | | ------------- | :-----: | :-------: | ----------- | -| `ArrayBuffer` | √ | | an indexed, mutable sequence | -| `List` | | √ | a linear (linked list), immutable sequence | -| `Map` | √ | √ | the base `Map` (key/value pairs) class | -| `Set` | √ | √ | the base `Set` class | -| `Vector` | | √ | an indexed, immutable sequence | +| `ArrayBuffer` | ✓ | | an indexed, mutable sequence | +| `List` | | ✓ | a linear (linked list), immutable sequence | +| `Map` | ✓ | ✓ | the base `Map` (key/value pairs) class | +| `Set` | ✓ | ✓ | the base `Set` class | +| `Vector` | | ✓ | an indexed, immutable sequence | Scala also has a `Range` class which lets you create ordered sequences of integers that are equally spaced apart, such as “1, 2, 3,” and “5, 8, 11, 14.” Ranges are often used in `for` loops, and to create other sequences: From e4f23902570b378b73ce251e2f3bd0f4a1cf0637 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Tue, 29 Sep 2020 15:25:00 -0600 Subject: [PATCH 006/169] Update _overviews/overview/a-taste-of-scala.md Co-authored-by: Julien Richard-Foy --- _overviews/overview/a-taste-of-scala.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index c0eba4bdb3..f45d175d67 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -247,11 +247,13 @@ println(s"Name: $firstName $mi $lastName") // "Name: John C Doe" Just precede the string with the letter `s`, and then put a `$` symbol before your variable names inside the string. -For expressions more complex than a single identifier, enclose the expression in curly braces: +To enclose expressions inside a string, enclose them in curly braces: -```scala +~~~ scala println(s"2 + 2 = ${2 + 2}") // prints "2 + 2 = 4" -``` +val x = -1 +println(s"x.abs = ${x.abs}") // prints "x.abs = 1" +~~~ #### Other interpolators @@ -1320,4 +1322,3 @@ Scala has even more features that weren’t covered in this whirlwind tour. See - From 97e96f85c60f22877676b8a956c841d9a74c0242 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Tue, 29 Sep 2020 16:45:35 -0600 Subject: [PATCH 007/169] Update _overviews/overview/a-taste-of-scala.md Co-authored-by: Julien Richard-Foy --- _overviews/overview/a-taste-of-scala.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index f45d175d67..d9339224cd 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -349,7 +349,7 @@ else Note that this really is an *expression* — not a *statement* — meaning that it returns a value, so you can assign the result to a variable: ```scala -val x = if (a < b) a else b +val x = if a < b then a else b ``` As you’ll see throughout this Overview and in our Reference documentation, *all* Scala control structures can be used as expressions. @@ -1321,4 +1321,3 @@ Scala has even more features that weren’t covered in this whirlwind tour. See - From 006dac9f00d4390731ea0c9fd80547c0a7a935ce Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Tue, 29 Sep 2020 16:46:07 -0600 Subject: [PATCH 008/169] Update _overviews/overview/a-taste-of-scala.md Co-authored-by: Julien Richard-Foy --- _overviews/overview/a-taste-of-scala.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index d9339224cd..8b0b4b5b78 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -371,7 +371,7 @@ The `for` keyword can be used to create a `for` loop. This example shows how to ```scala val ints = List(1,2,3,4,5) -for (i <- ints) println(i) +for i <- ints do println(i) ``` -This “Taste of Scala” section provides a whirlwind tour of Scala’s main features. After the initial tour in this section, the rest of the Overview will provide a few more details on the these features, and the Reference documentation will provide *many* more details. ->Throughout this Overview you’ll also be able to test many of the examples directly on this page. In addition to that, you can also test anything you’d like on [ScalaFiddle.io](https://scalafiddle.io), [Scastie](https://scastie.scala-lang.org), or in the Scala REPL, which is demonstrated shortly. +This “Taste of Scala” section provides a whirlwind tour of the main features of the Scala 3 programming language. After the initial tour in this section, the rest of the Overview provides a few more details on these features, and the Reference documentation provides _many_ more details. +>Throughout this Overview you’ll be able to test many of the examples directly on this page. In addition to that, you can also test anything you’d like on [ScalaFiddle.io](https://scalafiddle.io), [Scastie](https://scastie.scala-lang.org), or in the Scala REPL, which is demonstrated shortly. -## Hello, world - +## Hello, world -A “Hello, world” example in Scala goes as follows. First, put this code in a file named *Hello.scala*: +A Scala “Hello, world” example goes as follows. First, put this code in a file named _Hello.scala_: ```scala @main def hello = println("Hello, world") ``` +In this code, `hello` is a method — defined with `def`, and declared to be a “main” method with the `@main` annotation — that invokes the `println` method to write the `"Hello, world"` string to standard output (STDOUT). + Next, compile the code with `scalac`: ```sh @@ -40,7 +37,6 @@ $ scalac Hello.scala If you’re coming to Scala from Java, `scalac` is just like `javac`, so that command creates several files: - ```sh $ ls -1 Hello$package$.class @@ -51,7 +47,7 @@ hello.class hello.tasty ``` -Like Java, the *.class* files are bytecode files, and they’re ready to run in the JVM. Now you can run the main `hello` method with the `scala` command: +Like Java, the _.class_ files are bytecode files, and they’re ready to run in the JVM. Now you can run the main `hello` method with the `scala` command: ```sh $ scala hello @@ -64,7 +60,7 @@ Assuming that worked, congratulations, you just compiled and ran your first Scal ## The Scala REPL -The Scala REPL (“Read-Evaluate-Print-Loop”) is a command-line interpreter that you use as a “playground” area to test your Scala code. You start a REPL session by running the `scala` command at your operating system command line. When you do so, you’ll see a “welcome” prompt like this: +The Scala REPL (“Read-Evaluate-Print-Loop”) is a command-line interpreter that you use as a “playground” area to test your Scala code. You start a REPL session by running the `scala` command at your operating system command line, where you’ll see a “welcome” prompt like this: ```scala @@ -94,6 +90,7 @@ val x: Int = 20 Notice that the REPL output also shows the result of your expressions. + You can run all sorts of experiments in the REPL. This example shows how to create and then call a `sum` function: ```` @@ -112,16 +109,18 @@ As mentioned earlier, if you prefer a browser-based playground environment, you When you create a new variable in Scala, you declare whether the variable is immutable or mutable: -- `val` creates an *immutable* variable — like `final` in Java. You should always create a variable with `val`, unless there’s a reason you need a mutable variable. -- `var` creates a *mutable* variable, and should only be used when a variable’s contents will change over time. +- `val` creates an _immutable_ variable — like `final` in Java. You should always create a variable with `val`, unless there’s a reason you need a mutable variable. +- `var` creates a _mutable_ variable, and should only be used when a variable’s contents will change over time. These examples show how to create `val` and `var` variables: ```scala -val a = 0 // immutable +// immutable +val a = 0 val b = "Hello" -var c = 1 // mutable +// mutable +var c = 1 var d = "world" ``` @@ -143,14 +142,14 @@ msg = "Hello" // this compiles because a var can be reassigned ## Declaring variable types -You can declare variables by explicitly declaring their type, or by letting the compiler infer the type: +When you create a variable you can (a) explicitly declare its type, or (b) let the compiler infer the type: ```scala val x: Int = 1 // explicit val x = 1 // implicit; the compiler infers the type ``` -The second form is known as *type inference*, and it’s a great way to help keep this type of code concise. The Scala compiler can usually infer the data type for you, as shown in the output of these examples: +The second form is known as _type inference_, and it’s a great way to help keep this type of code concise. The Scala compiler can usually infer the data type for you, as shown in the output of these examples: ```scala scala> val x = 1 @@ -168,13 +167,13 @@ You can always explicitly declare a variable’s type if you prefer, but in simp ```scala val x: Int = 1 val s: String = "a string" -val p: Person = new Person("Richard") +val p: Person = Person("Richard") ``` Notice that with this approach, the code feels more verbose than necessary. - + ## Built-in data types Scala comes with the standard numeric data types you’d expect, and they’re all full-blown instances of classes. In Scala, everything is an object. @@ -183,7 +182,7 @@ These examples show how to declare variables of the numeric types: ```scala val b: Byte = 1 -val x: Int = 1 +val i: Int = 1 val l: Long = 1 val s: Short = 1 val d: Double = 2.0 @@ -197,7 +196,7 @@ val i = 123 // defaults to Int val j = 1.0 // defaults to Double ``` -In your code you can also use the characters `L`, `D`, and `F` (and their lowercase equivalents) to specify `Long`, `Double`, and `Float` fields: +In your code you can also append the characters `L`, `D`, and `F` (and their lowercase equivalents) to numbers to specify that they are `Long`, `Double`, or `Float` values: ```scala val x = 1_000L // val x: Long = 1000 @@ -212,6 +211,8 @@ var a = BigInt(1_234_567_890_987_654_321L) var b = BigDecimal(123_456.789) ``` +Where `Double` and `Float` are approximate decimal numbers, `BigDecimal` is used for precise arithmetic. + Scala also has `String` and `Char` data types: ```scala @@ -239,7 +240,7 @@ val mi = 'C' val lastName = "Doe" ``` -String interpolation lets you combine those variables in a string like this: +You can combine those variables in a string like this: ```scala println(s"Name: $firstName $mi $lastName") // "Name: John C Doe" @@ -247,8 +248,9 @@ println(s"Name: $firstName $mi $lastName") // "Name: John C Doe" Just precede the string with the letter `s`, and then put a `$` symbol before your variable names inside the string. -To enclose expressions inside a string, enclose them in curly braces: +To enclose expressions inside a string, put them in curly braces: + ~~~ scala println(s"2 + 2 = ${2 + 2}") // prints "2 + 2 = 4" val x = -1 @@ -257,22 +259,7 @@ println(s"x.abs = ${x.abs}") // prints "x.abs = 1" #### Other interpolators -“Why use the `s` in front of the string,” you may ask. The answer is that the `s` that precedes the strings in these examples is just one possible interpolator. You can also use the letter `f` in front of a string to use `printf`-style string formatting: - -```scala -val name = "Fred" -val age = 33 -val weight = 200.5 - -println(f"$name is $age years old, and weighs $weight%.1f pounds.") -``` - - -Beyond that, because `s` and `f` are really just methods, developers can write their own interpolators. For instance, Scala database libraries can create an `sql` interpolator that can have special features to support SQL: - -```scala -val query = sql"select * from $table" -``` +The `s` that you place before the string is just one possible interpolator. If you use an `f` instead of an `s`, you can use `printf`-style formatting in the string. Furthermore, because `s` and `f` are really just methods, you can write your own interpolators, such as creating a `sql` interpolator for use in a database library. For more details, see the Strings section in this Overview and in the Reference documentation. ### Multiline strings @@ -280,47 +267,39 @@ val query = sql"select * from $table" Multiline strings are created by including the string inside three double-quotes: ```scala -val quote = """The essence of Scala: - Fusion of functional and object-oriented +val quote = """The essence of Scala: + Fusion of functional and object-oriented programming in a typed setting.""" ``` One drawback of this basic approach is that the lines after the first line are indented, and look like this: ```scala -"The essence of Scala: +"The essence of Scala: Fusion of functional and object-oriented programming in a typed setting." ``` -A simple way to correct this problem is to put a `|` symbol in front of all lines after the first line, and call the `stripMargin` method after the string: +When spacing is important, put a `|` symbol in front of all lines after the first line, and call the `stripMargin` method after the string: ```scala -val quote = """The essence of Scala: - |Fusion of functional and object-oriented +val quote = """The essence of Scala: + |Fusion of functional and object-oriented |programming in a typed setting.""".stripMargin ``` Now all of the lines are left-justified inside the string: ```scala -"The essence of Scala: -Fusion of functional and object-oriented +"The essence of Scala: +Fusion of functional and object-oriented programming in a typed setting." ``` + ## Control structures - Scala has the programming language control structures you find in other languages, and also has powerful `for` expressions and `match` expressions: @@ -333,9 +312,9 @@ Scala has the programming language control structures you find in other language These structures are demonstrated in the following examples. -### if/else +### `if`/`else` -Scala’s if/else control structure is similar to other languages: +Scala’s `if`/`else` control structure is similar to other languages: ```scala if x < 0 then @@ -346,27 +325,20 @@ else println("positive") ``` -Note that this really is an *expression* — not a *statement* — meaning that it returns a value, so you can assign the result to a variable: +Note that this really is an _expression_ — not a _statement_. This means that it returns a value, so you can assign the result to a variable: ```scala val x = if a < b then a else b ``` -As you’ll see throughout this Overview and in our Reference documentation, *all* Scala control structures can be used as expressions. +As you’ll see throughout this Overview and in our Reference documentation, _all_ Scala control structures can be used as expressions. >An expression returns a result, while a statement does not. Statements are typically used for their side-effects, such as using `println` to print to the console. -### for loops and expressions - +### `for` loops and expressions -The `for` keyword can be used to create a `for` loop. This example shows how to print every element in a `List`: +The `for` keyword is used to create a `for` loop. This example shows how to print every element in a `List`: ```scala val ints = List(1,2,3,4,5) @@ -374,21 +346,20 @@ val ints = List(1,2,3,4,5) for i <- ints do println(i) ``` - +The code `i <- ints` is referred to as a _generator_, and if you leave the parentheses off of the generator, the `do` keyword is required before the code that follows it. Otherwise you can write the code like this: + +```scala +for (i <- ints) println(i) +``` + #### Guards -You can also use one or more `if` expressions inside a `for` loop. These are referred to as *guards*. This example prints all of the numbers in `ints` that are greater than `2`: +You can also use one or more `if` expressions inside a `for` loop. These are referred to as _guards_. This example prints all of the numbers in `ints` that are greater than `2`: - - ```scala -val ints = List(1,2,3,4,5) - for - i <- ints + i <- ints if i > 2 do println(i) @@ -407,24 +378,21 @@ do println(s"i = $i, j = $j") // prints: "i = 2, j = b" ``` -We encourage you to make changes to that code to be sure you understand how it works. - #### Using `for` as an expression -The `for` keyword has even more power: When you use the `yield` keyword instead of `do`, you create `for` *expressions* which are used to calculate and yield results. +The `for` keyword has even more power: When you use the `yield` keyword instead of `do`, you create `for` _expressions_ which are used to calculate and yield results. A few examples demonstrate this. Using the same `ints` list as the previous example, this code creates a new list, where the value of each element in the new list is twice the value of the elements in the original list: ```` -scala> val doubles = for (i <- ints) yield i * 2 +scala> val doubles = for i <- nums yield i * 2 val doubles: List[Int] = List(2, 4, 6, 8, 10) ```` -Note that Scala’s syntax is flexible, and that `for` expression can be written in several different ways to make your code more readable: +Scala’s control structure syntax is flexible, and that `for` expression can be written in several other ways, depending on your preference: ```scala -val doubles = for i <- ints yield i * 2 val doubles = for (i <- ints) yield i * 2 val doubles = for (i <- ints) yield (i * 2) ``` @@ -438,7 +406,7 @@ val capNames = for name <- names yield name.capitalize Finally, this `for` expression iterates over a list of strings, and returns the length of each string, but only if that length is greater than `4`: - + ```scala val fruits = List("apple", "banana", "lime", "orange") @@ -446,14 +414,13 @@ val fruitLengths = for f <- fruits if f.length > 4 yield + // you can use multiple lines + // of code here f.length // result: List[Int] = List(5, 6, 6) ``` -Because Scala code generally just makes sense, you can probably guess how this code works, even if you’ve never seen a for-expression or Scala list until now. - - `for` loops and expressions are covered in more detail in the Control Structures sections of this Overview and in the Reference documentation. @@ -471,7 +438,7 @@ i match case _ => println("other") ``` -However, a `match` expression really is an expression, meaning that it returns a result based on the pattern match: +However, `match` really is an expression, meaning that it returns a result based on the pattern match, which you can bind to a variable: ```scala val result = i match @@ -480,7 +447,7 @@ val result = i match case _ => "other" ``` -The `match` expression isn’t limited to just integers, it can be used with any data type, including booleans: +`match` isn’t limited to just integers, it can be used with any data type, including booleans: ```scala val booleanAsString = bool match @@ -488,9 +455,9 @@ val booleanAsString = bool match case false => "false" ``` -In fact, a `match` expression can be used to test a variable against multiple patterns. This example shows (a) how to use a `match` expression as the body of a method, and (b) how to match all the different types shown: +In fact, a `match` expression can be used to test a variable against many different types of patterns. This example shows (a) how to use a `match` expression as the body of a method, and (b) how to match all the different types shown: - + ```scala def getClassAsString(x: Any): String = x match case s: String => s"'$s' is a String" @@ -505,26 +472,25 @@ getClassAsString("hello") // 'hello' is a String getClassAsString(List(1,2,3)) // List ``` -There’s much more to pattern matching in Scala. Patterns can be nested, results of patterns can be bound, and pattern matching can even be user-defined. You can find more pattern matching examples in the Control Structures section of this Overview, and in the Reference documentation. +There’s _much_ more to pattern matching in Scala. Patterns can be nested, results of patterns can be bound, and pattern matching can even be user-defined. See the pattern matching examples in the Control Structures sections of this Overview and the Reference documentation for more details. -### try/catch/finally +### `try`/`catch`/`finally` -Scala’s `try`/`catch` control structure lets you catch exceptions. It’s similar to Java, but its syntax is consistent with `match` expressions: +Scala’s `try`/`catch`/`finally` control structure lets you catch exceptions. It’s similar to Java, but its syntax is consistent with `match` expressions: - ```scala try - writeToFile(text) + writeTextToFile(text) catch - case fnfe: FileNotFoundException => println(fnfe) - case ioe: IOException => println(ioe) + case ioe: IOException => println("Got an IOException.") + case nfe: NumberFormatException => println("Got a NumberFormatException.") +finally + println("Clean up your resources here.") ``` -### while loops - - +### `while` loops Scala also has a `while` loop construct. It’s one-line syntax looks like this: @@ -532,50 +498,56 @@ Scala also has a `while` loop construct. It’s one-line syntax looks like this: while x >= 0 do x = f(x) ``` -And it’s multiline syntax looks like this: +If you leave the parentheses off of the test condition, the `do` keyword is required before the code that follows it. Again, Scala’s control structure syntax is flexible, and you can write this code in different ways depending on your preferences: ```scala -var x = 1 +while (x >= 0) do x = f(x) +while (x >= 0) { x = f(x) } +``` -// parentheses -while (x < 3) - println(x) - x += 1 +The `while` loop multiline syntax looks like this: + +```scala +var x = 1 -// no parens +// without parentheses while x < 3 do println(x) x += 1 + +// with parentheses +while (x < 3) + println(x) + x += 1 ``` -### Create your own control structures! -Thanks to features like by-name parameters, infix notation, fluent interfaces, optional parentheses, extension methods, and higher-order functions, you can also create code that works just like a control structure. You’ll learn more about this in the Control Structures chapter in the Reference documentation. +### Create your own control structures + +Thanks to features like by-name parameters, infix notation, fluent interfaces, optional parentheses, extension methods, and higher-order functions, you can also create your own code that works just like a control structure. You’ll learn more about this in the Control Structures chapter in the Reference documentation. ## Data Modeling + Scala supports both functional programming (FP) and object-oriented programming (OOP), as well as a fusion of the two paradigms. This section provides a quick overview of data modeling in OOP and FP. -### OOP data modeling (traits and classes) +### Data Modeling (Object-Oriented Programming Style) -When writing code in an OOP style, your two main tools will be *traits* and *classes*. +When writing code in an OOP style, your two main tools for data encapsulation are _traits_ and _classes_. + + #### Traits - -Traits are like interfaces in other languages, but they can also contain implemented methods. They provide a great way for you to organize behaviors into small, modular units. When you want to create concrete implementations of attributes and behaviors, classes and objects can extend traits, mixing in as many traits as needed to achieve the desired behavior. +Scala traits can be used as simple interfaces, but they can also contain abstract and concrete methods and fields, and they can have parameters, just like classes. They provide a great way for you to organize behaviors into small, modular units. Later, when you want to create concrete implementations of attributes and behaviors, classes and objects can extend traits, mixing in as many traits as needed to achieve the desired behavior. -Here are three traits that define well-organized and modular behaviors for animals like dogs and cats: + +As an example of how to use traits as interfaces, here are three traits that define well-organized and modular behaviors for animals like dogs and cats: ```scala trait Speaker: @@ -620,42 +592,43 @@ c.startRunning() // "Yeah ... I don’t run" c.stopRunning() // "No need to stop" ``` - If that code makes sense — great, you’re comfortable with traits as interfaces. If not, don’t worry, they’re explained in more detail in the Data Modeling sections of this Overview and the Reference documentation. - - #### Classes - - -Scala *classes* are used in OOP-style programming. Here’s an example of a class that models a “person.” In OOP, fields are typically mutable, so `firstName` and `lastName` are both declared as `var` parameters: +Scala _classes_ are used in OOP-style programming. Here’s an example of a class that models a “person.” In OOP fields are typically mutable, so `firstName` and `lastName` are both declared as `var` parameters: ```scala class Person(var firstName: String, var lastName: String): def printFullName() = println(s"$firstName $lastName") -val p = Person("Julia", "Kern") -println(p.firstName) // "Julia" -p.lastName = "Manes" -p.printFullName() // "Julia Manes" +val p = Person("John", "Stephens") +println(p.firstName) // "John" +p.lastName = "Legend" +p.printFullName() // "John Legend" +``` + +Notice that the class declaration creates a constructor: + +```scala +class Person(var firstName: String, var lastName: String): + // more code here + +// this code uses that constructor +val p = Person("John", "Stephens") ``` +Constructors and other class-related topics are covered in the Data Modeling sections in this Overview, and in the Reference documentation. + ### Data Modeling (Functional Programming Style) + + When writing code in an FP style, you’ll use these constructs: - Enums to define ADTs @@ -664,15 +637,6 @@ When writing code in an FP style, you’ll use these constructs: #### Enums - The `enum` construct is a great way to model algebraic data types (ADTs) in Scala 3. For instance, a pizza has three main attributes: @@ -700,7 +664,7 @@ import CrustSize._ val currentCrustSize = Small // enums in a `match` expression -currentCrustSize match +currentCrustSize match case Small => println("Small crust size") case Medium => println("Medium crust size") case Large => println("Large crust size") @@ -709,17 +673,12 @@ currentCrustSize match if currentCrustSize == Small then println("Small crust size") ``` -Enums can also take parameters and have user-defined members like fields and methods. Here’s a sneak-peek of those capabilities: +Here’s another example of how to create and use an ADT with Scala: ```scala -enum Planet(mass: Double, radius: Double): - private final val G = 6.67300E-11 - def surfaceGravity = G * mass / (radius * radius) - - case Mercury extends Planet(3.303e+23, 2.4397e6) - case Venus extends Planet(4.869e+24, 6.0518e6) - // more ... -} +enum Nat: + case Zero + case Succ(pred: Nat) ``` Enums are covered in detail in the Data Modeling section of this Overview, and in the Reference documentation. @@ -727,26 +686,24 @@ Enums are covered in detail in the Data Modeling section of this Overview, and i #### Case classes - - -A *case class* is an extension of the base Scala class. Case classes provide features that make them useful for functional programming. They have all of the functionality of a regular class, and more. When the compiler sees the `case` keyword in front of a `class`, it generates code for you, with these benefits: +The Scala `case` class lets you model concepts with immutable data structures. A `case` class has all of the functionality of a `class`, and also has additional features baked in that make them useful for functional programming. When the compiler sees the `case` keyword in front of a `class` it has these effects and benefits: -- Case class constructor parameters are public `val` fields by default, so accessor methods are generated for each parameter. -- An `apply` method is created in the companion object of the class, so you don’t need to use the `new` keyword to create a new instance of the class. +- Case class constructor parameters are public `val` fields by default, so the fields are immutable, and accessor methods are generated for each parameter. - An `unapply` method is generated, which lets you use case classes in more ways in `match` expressions. -- A `copy` method is generated in the class. This provides a way to clone an object while making updates to its values during the cloning process. +- A `copy` method is generated in the class. This provides a way to clone an object while making updates to its values as the cloned copy is created. In this way the original object can be used as a template, and the cloned copy can have changed fields, as needed. - `equals` and `hashCode` methods are generated. - A default `toString` method is generated, which is helpful for debugging. -This code demonstrates several case class features: + + +You _can_ manually add all of those methods to a class yourself, but since those features are so commonly used in functional programming, using a `case` class is much more convenient. + +This code demonstrates several `case` class features: ```scala // define a case class case class Person( - name: String, + name: String, vocation: String ) @@ -766,20 +723,16 @@ val p2 = p.copy(name = "Elton John") p2 // Person = Person(Elton John,Singer) ``` -See the Data Modeling sections of this Overview and the Reference documentation for many more details on case classes. +See the Data Modeling sections of this Overview and the Reference documentation for many more details on `case` classes. ## Scala methods - - Scala classes, case classes, traits, enums, and objects can all contain methods. The general method syntax looks like this: ```scala -def methodName(param1: Type1, param2: Type2): ReturnType = +def methodName(param1: Type1, param2: Type2): ReturnType = // the method body // goes here ``` @@ -814,135 +767,179 @@ def getStackTraceAsString(t: Throwable): String = sw.toString ``` -There are more things you can do with methods, such as providing default values for method parameters and using named parameters when calling methods. Those featurs are shown in the Data Modeling section of this Overview and in the Reference documentation. - +Method parameters can also have default values. In this example, if the `timeout` parameter isn’t specified, it defaults to `5000`: +```scala +def makeConnection(url: String, timeout: Int = 5000): Unit = + println(s"url=$url, timeout=$timeout") +``` -## First-class functions +Because a default `timeout` value is supplied in the method declaration, the method can be called in these two ways: -Scala has first-class support for functional programming, and included in that is first-class support for everything related to functions: +```scala +makeConnection("https://localhost") // url=http://localhost, timeout=5000 +makeConnection("https://localhost", 2500) // url=http://localhost, timeout=2500 +``` -- Top-level functions -- Functions in objects -- Lambdas -- Higher-order functions (HOFs) +Scala also supports the use of _named parameters_ when calling a method, so you can also call that method like this, if you prefer: - - +```scala +makeConnection( + url = "https://localhost", + timeout = 2500 +) +``` +Named parameters are particularly useful when multiple method parameters have the same type: -### Top-level functions +```scala +engage(true, true, true, false) +``` -Functions don’t have to be in the body of a class, enum, or object. They can also exist on their own as a *top-level function*. For instance, when this code is saved in a file, the functions `add` and `double` would be called top-level functions because they exist outside the scope of a class, enum, or object: +Without help from an IDE that code can be hard to read, but this code is much more obvious: ```scala -package foo +engage( + speedIsSet = true, + directionIsSet = true, + picardSaidMakeItSo = true, + turnedOffParkingBrake = false +) +``` -def add(a: Int, b: Int): Int = a + b -def double(a: Int): Int = 2 * a +Methods are covered in detail in the Data Modeling section of this Overview, and in the Reference documentation. -@main def topLevelFunctions() = - val x = add(1, 2) - val y = double(x) - println(s"y = $y") -``` -### Functions in objects +## Objects -Functions can also be placed in objects. Here are a few functions in a “string utilities” object: +In Scala, the `object` keyword creates a Singleton object. Put another way, an object is a class that has exactly one instance. -```scala -object StringUtils: +Objects have several uses: + +- They are used to create collections of utility methods. +- A _companion object_ is an object that has the same name as the class it shares a file with. In this situation, that class is also called a _companion class_. +- They’re used to _reify_ traits to create _modules_. - // Left-trim a string: `" foo "` becomes `"foo "`. - def leftTrim(s: String) = s.replaceAll("^\\s+", "") +### Creating a collection of utility methods - // Right-trim a string: `" foo "` becomes `" foo"`. - def rightTrim(s: String) = s.replaceAll("\\s+$", "") +Because an `object` is a Singleton, its methods can be accessed like `static` methods in a Java class. For example, this `StringUtils` object contains a small collection of string-related methods: +```scala +object StringUtils: def isNullOrEmpty(s: String): Boolean = if (s==null || s.trim.equals("")) true else false + def leftTrim(s: String): String = s.replaceAll("^\\s+", "") + def rightTrim(s: String): String = s.replaceAll("\\s+$", "") +``` + +Because `StringUtils` is a singleton, its methods can be called directly on the object: -end StringUtils +```scala +val x = StringUtils.isNullOrEmpty("") // true +val x = StringUtils.isNullOrEmpty("a") // false ``` -Those functions can be called like this: +### Creating a companion object + +A companion class or object can access the private members of its companion. Use a companion object for methods and values which aren’t specific to instances of the companion class. + +This example demonstrates how the `area` method in the companion class can access the private `calculateArea` method in its companion object: ```scala -StringUtils.leftTrim(" hi ") // "hi " +import scala.math._ + +class Circle(radius: Double): + import Circle._ + def area: Double = calculateArea(radius) + +object Circle: + private def calculateArea(radius: Double): Double = + Pi * pow(radius, 2.0) + +val circle1 = Circle(5.0) +circle1.area // Double = 78.53981633974483 ``` -or this: + +### Using objects to reify traits + +Objects can also be used to reify traits to create modules. “Reify” means to turn an abstract concept and turn it into something concrete, and the process with traits and objects looks like this: ```scala -import StringUtils._ -leftTrim(" hi ") // "hi " +trait AddService: + def add(a: Int, b: Int) = a + b + +trait MultiplyService: + def multiply(a: Int, b: Int) = a * b + +// reify those traits into a concrete object +object MathService extends AddService, MultiplyService + +// use the object +import MathService._ +println(add(1,1)) // 2 +println(multiply(2,2)) // 4 ``` + +## First-class functions + +Scala has all of the features you’d expect in a functional programming language, including: + +- Lambdas +- Higher-order functions (HOFs) +- Higher-order functions in the standard library + + ### Lambdas -Lambdas — also known as *anonymous functions* — are a big part of keeping your code concise but readable. These two examples are equivalent, and show how to multiply each number in a list by `2` by passing a lambda into the `map` method: + + +Lambdas, also known as _anonymous functions_, are a big part of keeping your code concise but readable. These two examples — which show how to call higher-order functions (HOFs) on a Scala `List` — are equivalent, and show how to multiply each number in a list by `2` by passing a lambda into the `map` method: ```scala val a = List(1,2,3).map(i => i * 2) // List(2,4,6) val b = List(1,2,3).map(_ * 2) // List(2,4,6) ``` -Those examples are also equivalent to that code, but they use a `double` method inside of `map` instead of a lambda: +Those examples are also equivalent to the following code, which uses a `double` method inside of `map` instead of a lambda: ```scala def double(i: Int): Int = i * 2 -val a = List(1,2,3).map(i => double(i)) -val b = List(1,2,3).map(double) -``` ->If you haven’t seen the `map` method before, it applies a given function to every element in a list, yielding a new list that contains the new values. +val a = List(1,2,3).map(i => double(i)) // List(2,4,6) +val b = List(1,2,3).map(double) // List(2,4,6) +``` +>If you haven’t seen the `map` method before, it applies a given function to every element in a list, yielding a new list that contains the resulting values. -### Higher-order functions +Passing lambdas into higher-order functions on collections classes is a part of the Scala experience, something you’ll do every day. -A “higher order function” (HOF) is a function that can take a function as an input parameter, or returns a function as a return value. HOFs are extremely common in Scala, such as being used throughout the standard libraries. These examples show how to pass methods and anonymous functions into the functional methods of Scala collections: +It’s important to know that these functions don’t mutate the collection they’re called on; instead, they return a new collection with the updated data. As a result, it’s also common to chain them together in a “fluent” style to solve problems. This example shows how to filter a collection twice, and then multiply each element in the remaining collection: ```scala // a sample list -val nums = (1 to 10).toList // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - -// sample methods -def isEven(i: Int): Boolean = i % 2 == 0 -def double(i: Int): Int = i * 2 - -// pass in a method or function -val a = nums.filter(isEven) // List(2, 4, 6, 8, 10) -val b = nums.map(double) // List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) +val nums = (1 to 10).toList // List(1,2,3,4,5,6,7,8,9,10) -// pass in a lambda -val c = nums.filter(_ % 2 == 0) // List(2, 4, 6, 8, 10) -val d = nums.map(_ * 2) // List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) - -// methods can be chained as needed -val e = nums.filter(_ > 3) // List(40, 50, 60) +// methods can be chained together as needed +val x = nums.filter(_ > 3) .filter(_ < 7) .map(_ * 10) + +// result: x == List(40, 50, 60) ``` -In addition to HOFs used all throughout the standard library, you can easily create your own HOFs. This is shown in the Reference documentation. +In addition to higher-order functions being used throughout the standard library, you can also create your own. This is shown in the Reference documentation. - ## Extension methods - -*Extension methods* let you add new methods to closed classes. For instance, if you want to add two methods named `hello` and `aloha` to the `String` class, just declare them as extension methods: +_Extension methods_ let you add new methods to closed classes. For instance, if you want to add two methods named `hello` and `aloha` to the `String` class, declare them as extension methods: - + ```scala extension (s: String): def hello: String = s"Hello, ${s.capitalize}" @@ -952,11 +949,11 @@ extension (s: String): "friend".aloha // "Aloha, Friend" ``` -The `extension` keyword declares that you’re about to define one or more extension methods. As shown, the `s` parameter can then be used in your methods. +The `extension` keyword declares that you’re about to define one or more extension methods on the type that’s put in parentheses. As shown with this `String` example, the parameter `s` can then be used in the body of your extension methods. This next example shows how to add a `makeInt` method to the `String` class. Here, `makeInt` takes a parameter named `radix`. The code doesn’t account for possible string-to-integer conversion errors, but skipping that detail, the examples show how it works: - + ```scala extension (s: String) def makeInt(radix: Int): Int = Integer.parseInt(s, radix) @@ -970,170 +967,65 @@ extension (s: String) ## Collections classes -Scala has a rich set of collections classes, and those classes have a rich set of methods. Collections classes are available in both immutable and mutable forms. In alphabetical order, the basic collections classes you’ll use on a regular basis are: - -| Class | Mutable | Immutable | Description | -| ------------- | :-----: | :-------: | ----------- | -| `ArrayBuffer` | ✓ | | an indexed, mutable sequence | -| `List` | | ✓ | a linear (linked list), immutable sequence | -| `Map` | ✓ | ✓ | the base `Map` (key/value pairs) class | -| `Set` | ✓ | ✓ | the base `Set` class | -| `Vector` | | ✓ | an indexed, immutable sequence | - -Scala also has a `Range` class which lets you create ordered sequences of integers that are equally spaced apart, such as “1, 2, 3,” and “5, 8, 11, 14.” Ranges are often used in `for` loops, and to create other sequences: - -```scala -// a range in a for-loop -for (i <- 1 to 3) print(i) // 123 - -// use ranges to create other collections -(1 to 5).toList // List(1, 2, 3, 4, 5) -(1 to 10 by 2).toVector // Vector(1, 3, 5, 7, 9) -(1 to 10).toSet // Set[Int] = HashSet(5, 10, 1, 6, 9, 2, 7, 3, 8, 4) - -('a' to 'f').toList // List[Char] = List(a, b, c, d, e, f) -('a' to 'f' by 2).toList // List[Char] = List(a, c, e) - -List.range(1, 5) // List(1, 2, 3, 4) -List.range(1, 5, 2) // List(1, 3) -``` - - - - -### Immutable collections classes - -Some of the most-commonly used *immutable* collections classes are: - -| Class | Description | -| ------------- | ------------- | -| `List` | A finite immutable linked-list. | -| `LazyList` | Like a `List` except that its elements are computed lazily. Because of this, a lazy list can be infinitely long. | -| `Vector` | A sequential collection type that provides good performance for all its operations. | -| `Map` | Like maps in Java, dictionaries in Python, or a `HashMap` in Rust, `Map` is an `Iterable` that contains pairs of keys and values. | -| `Set` | An `Iterable` sequence that contains no duplicate elements. | -| `Stack` | A last-in-first-out sequence. | -| `Queue` | A first-in-first-out sequence. | - -In addition to the previous `Range` examples, here are a few more ways to create immutable collections: - - -```scala -val a = List(1,2,3) -val b = Vector("hello", "world") - -// create a List with a Range -val c = (1 to 5).toList - -val m = Map( - 1 -> "one", - 2 -> "two" -) -``` - - -### Mutable collections classes - -Mutable Scala collections classes are located in the *scala.collection.mutable* package. These are some of the most commonly-used: - -| Class | Description | -| ------------- | ------------- | -| `ArrayBuffer` | An indexed mutable buffer backed by an array. | -| `ListBuffer` | A mutable buffer backed by a linked list. Use this class if you’ll use it like a `List`, or if you’ll convert it to a `List` once it’s built up. | -| `Map` | A mutable `Map`, contains pairs of keys and values. | -| `Set` | A mutable `Set`, a sequence that contains no duplicate elements. | -| `Stack` | A mutable, last-in-first-out sequence. | -| `Queue` | A mutable, first-in-first-out sequence. | +Scala has a rich set of collections classes, and those classes have a rich set of methods. Collections classes are available in both immutable and mutable forms. -These examples show a few ways to use an `ArrayBuffer`: +To give you a taste of how these work, here are some examples that use the `List` class, which is an immutable, linked-list class. These examples show different ways to create a populated `List`: ```scala -import scala.collection.mutable.ArrayBuffer +val a = List(1,2,3) // a: List[Int] = List(1, 2, 3) -val a = ArrayBuffer(1,2,3) // ArrayBuffer(1, 2, 3) - -val b = ArrayBuffer[String]() // ArrayBuffer[String] = ArrayBuffer() -b += "Hello" // ArrayBuffer(Hello) -b ++= List("world", "it’s", "me") // ArrayBuffer(Hello, world, it’s, me) +// Range methods +val b = (1 to 5).toList // b: List[Int] = List(1, 2, 3, 4, 5) +val c = (1 to 10 by 2).toList // c: List[Int] = List(1, 3, 5, 7, 9) +val e = (1 until 5).toList // e: List[Int] = List(1, 2, 3, 4) +val f = List.range(1, 5) // f: List[Int] = List(1, 2, 3, 4) +val g = List.range(1, 10, 3) // g: List[Int] = List(1, 4, 7) ``` -In addition to those classes, Scala has an `Array` class, which is a special kind of collection. An `Array` corresponds one-to-one to a Java `array`, so its elements can be mutated, but its size can’t be changed. In addition to behaving like a Java `array`, (a) it can hold generic types, and (b) it supports all of the usual sequence methods (which you’re about to see). - - - -### Sequence methods + - - -A great benefit of the Scala collections classes is that they offer dozens of powerful functional methods you can use to simplify your code. These are just a few of the common methods available to sequences: - -| Method | Description | -| ------------- | ------------- | -| `map` | Creates a new collection by applying a function to all the elements of the collection. | -| `filter` | Returns all elements from the collection for which a given predicate is true. | -| `foreach` | Applies a function to all elements of the collection to produce a side effect. | -| `take`, `takeWhile` | Returns elements from the beginning of the collection. | -| `drop`, `dropWhile` | Returns all elements in the collection except the first elements that are specified. | -| `flatten` | Converts a sequence of sequences (such as a list of lists) to a single sequence (single list). | -| `foldLeft` | Applies an operation to successive elements, going from left to right, using an initial seed value | -| `reduce` | The same as `foldLeft`, but without an initial seed value. | - -There are dozens of additional methods, which you’ll see in the Collections section of this Overview, and in the Reference documentation. - ->A key to know about all of these methods is that they are *functional*: they don’t modify the existing sequence; they return a new collection by applying the method to the original sequence. - -These examples demonstrate some of the commonly-used sequence methods: +Once you have a populated list, the following examples show some of the methods you can call on it. Notice that these are all functional methods, meaning that they don’t mutate the collection they’re called on, but instead return a new collection with the updated elements. The result that’s returned by each expression is shown in the comment on each line: ```scala // a sample list val a = List(10, 20, 30, 40, 10) // List(10, 20, 30, 40, 10) a.drop(2) // List(30, 40, 10) -a.dropRight(2) // List(10, 20, 30) a.dropWhile(_ < 25) // List(30, 40, 10) a.filter(_ < 25) // List(10, 20, 10) -a.find(_ > 20) // Some(30) -a.head // 10 -a.headOption // Some(10) -a.last // 10 -a.lastOption // Some(10) a.slice(2,4) // List(30, 40) a.tail // List(20, 30, 40, 10) a.take(3) // List(10, 20, 30) -a.takeRight(2) // List(40, 10) a.takeWhile(_ < 30) // List(10, 20) +// flatten val a = List(List(1,2), List(3,4)) a.flatten // List(1, 2, 3, 4) +// map, flatMap val nums = List("one", "two") -nums.map(_.toUpperCase) // List("ONE", "TWO") -nums.flatMap(_.toUpperCase) // List('O', 'N', 'E', 'T', 'W', 'O') +nums.map(_.toUpperCase) // List("ONE", "TWO") +nums.flatMap(_.toUpperCase) // List('O', 'N', 'E', 'T', 'W', 'O') ``` -These examples show how the “fold” and “reduce” methods are used to sum up every element in a sequence: +These examples show how the “fold” and “reduce” methods are used to sum the values in a sequence of integers: ```scala val firstTen = (1 to 10).toList // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) -firstTen.fold(100)(_ + _) // 155 -firstTen.foldLeft(100)(_ + _) // 155 firstTen.reduce(_ + _) // 55 firstTen.reduceLeft(_ + _) // 55 +firstTen.fold(100)(_ + _) // 155 (100 is a “seed” value) +firstTen.foldLeft(100)(_ + _) // 155 ``` -There are many (many!) more methods available to Scala collections classes, and they’re demonstrated in the Collections sections of this Overview and in the Reference documentation. +There are many more methods available to Scala collections classes, and they’re demonstrated in the Collections sections of this Overview and in the Reference documentation. -### Tuples - +### Tuples -The Scala *tuple* is a type that lets you easily put a collection of different types in the same container. For example, given this `Person` case class: +The Scala _tuple_ is a type that lets you easily put a collection of different types in the same container. For example, given this `Person` case class: ```scala case class Person(name: String) @@ -1145,7 +1037,7 @@ This is how you create a tuple that contains an `Int`, a `String`, and a custom val t = (11, "eleven", Person("Eleven")) ``` -You can access the tuple values by number: +Once you have a tuple, you can access its values by binding them to variables, or access them by number: ```scala t._1 // 11 @@ -1153,126 +1045,22 @@ t._2 // "eleven" t._3 // Person("Eleven") ``` -You can also assign the tuple fields to variables, as shown in the REPL: - -```` -scala> val (num, string, name) = (11, "eleven", Person("Eleven")) -val num: Int = 11 -val string: String = eleven -val good: Boolean = Person(Eleven) -```` - -Tuples are nice for those times when you want to put a collection of heterogenous types in a little collection-like structure. - - +Tuples are nice for those times when you want to put a collection of heterogenous types in a little collection-like structure. See the Reference documentation for more tuple details. ## Contextual Abstractions - - -Implicits in Scala 2 were a main distinguishing feature. Following Haskell, Scala was the second popular language to have some form of implicits. Other languages have since followed suit. - -Implicits are a fundamental way to abstract over context. They represent a unified paradigm with a great variety of use cases, among them: - -- Implementing type classes -- Establishing context -- Dependency injection -- Expressing capabilities -- Computing new types and proving relationships between them - -Even though different languages use widely different terminology, they’re all variants of the core idea of *term inference*. Given a type, the compiler synthesizes a “canonical” term that has that type. Scala embodies the idea in a purer form than most other languages: An implicit parameter directly leads to an inferred argument term that could also be written down explicitly. - -In Scala 3 the syntax for these constructs has been changed significantly to make their usage more clear. Implicits are defined with the keyword `given`, and in later code they are referenced with the keyword `using`. - - -### Term inference - -Here’s a quick example of how to use term inference in Scala 3. Imagine that you have a situation where a value is passed into a series of function calls, such as using an `ExecutionContext` when you’re working with parallel programming: - -```scala -doX(a, executionContext) -doY(b, c, executionContext) -doZ(d, executionContext) -``` - -Because this type of code is repetitive and makes the code harder to read, you’d prefer to write it like this instead: - -```scala -doX(a) -doY(b, c) -doZ(d) -``` - -The functions still use the `executionContext` variable, but they don’t require you to explicitly state that. As shown, the shorter code is more easy to read. - -When you want a solution like this, Scala 3’s term inference solution is what you’re looking for. The solution involves multiple steps: - -1. Define the code you want the compiler to discover using the Scala 3 `given` keyword. -1. When declaring the “implicit” parameter your function will use, put it in a separate parameter group and define it with the `using` keyword. -1. Make sure your `given` value is in the current context when your function is called. - -The following example demonstrates these steps with the use of an `Adder` trait and two `given` values that implement the `Adder` trait’s `add` method. - - -### Step 1: Define your “given instances” - -In the first step you’ll typically create a parameterized trait: - -```scala -trait Adder[T]: - def add(a: T, b: T): T -``` - -Then you’ll implement the trait using one or more `given` instances, which you define like this: +Under certain circumstances, the Scala compiler can “write” some parts of your programs. For instance, consider a program that sorts a list of addresses by two criteria, city name and then street name: ```scala -given intAdder as Adder[Int]: - def add(a: Int, b: Int): Int = a + b - -given stringAdder as Adder[String]: - def add(a: String, b: String): String = "" + (a.toInt + b.toInt) +val addresses: List[Address] = ... +addresses.sortBy(address => (address.city, address.street)) ``` -In this example, `intAdder` is an instance of `Adder[Int]`, and defines an `add` method that works with `Int` values. Similarly, `stringAdder` is an instance of `Adder[String]` and provides an `add` method that takes two strings, converts them to `Int` values, adds them together, and returns the sum as a `String`. (To keep things simple the code doesn’t account for potential string-to-integer errors.) - -If you’re familiar with creating implicits in Scala 2, this new approach is similar to that process. The idea is the same, it’s just that the syntax has changed. - +The sorting algorithm needs to compare addresses by first comparing their city names, and then also their street names when the city names are the same. However, with the use of contextual abstraction, you don’t need to manually define this ordering relation, because the compiler is able to summon it automatically based on an existing ordering relation for comparing string values. -### Step 2: Declare the parameter your function will use with the `using` keyword - -Next, define your functions that use the `Adder` instances. When doing this, specify the `Adder` parameter with the `using` keyword. Put the parameter in a separate parameter group, as shown here: - -```scala -def genericAdder[A](x: A, y: A)(using adder: Adder[A]): A = - adder.add(x, y) -``` - -The keys here are that the `adder` parameter is defined with the `using` keyword in that separate parameter group: - -```scala -def genericAdder[A](x: A, y: A)(using adder: Adder[A]): A = - ----------------------- -``` - -Also notice that `genericAdder` declares the generic type `A`. This function doesn’t know if it will be used to add two integers or two strings — it just calls the `add` method of the `adder` parameter. - ->In Scala 2, parameters like this were declared using the `implicit` keyword, but now, as the entire programming industry has various implementations of this concept, this parameter is known in Scala 3 as a *context parameter*. - - -### Step 3: Make sure everything is in the current context - -Finally, assuming that `intAdder`, `stringAdder`, and `genericAdder` are all in scope, your code can call the `genericAdder` function with `Int` and `String` values, without having to pass instances of `intAdder` and `stringAdder` into `genericAdder`: - -```scala -println(genericAdder(1, 1)) // 2 -println(genericAdder("2", "2")) // 4 -``` - -The Scala compiler is smart enough to know that `intAdder` should be used in the first instance, and `stringAdder` should be used in the second instance. This is because the first example uses two `Int` parameters and the second example uses two `String` values. +For more details, see the Contextual Abstractions section in this Overview, and also in the Reference documentation. @@ -1280,38 +1068,3 @@ The Scala compiler is smart enough to know that `intAdder` should be used in the Scala has even more features that weren’t covered in this whirlwind tour. See the remainder of this Overview and the Reference documentation for many more details. - - - - - - - - - - - diff --git a/_overviews/overview/control-structures.md b/_overviews/overview/control-structures.md index e69de29bb2..fc4bff784d 100644 --- a/_overviews/overview/control-structures.md +++ b/_overviews/overview/control-structures.md @@ -0,0 +1,555 @@ +--- +title: Control Structures +description: This page provides an introduction to Scala's control structures, including if/then/else, 'for' loops, 'for' expressions, 'match' expressions, try/catch/finally, and 'while' loops. +--- + + + +Scala has the control structures you expect to find in a programming language, including: + +- `if`/`then`/`else` +- `for` loops +- `while` loops +- `try`/`catch`/`finally` + +It also has two other powerful constructs that you may not have seen before, depending on your programming background: + +- `for` expressions (also known as _`for` comprehensions_) +- `match` expressions + +These are all demonstrated in the following sections. + + + +## The if/then/else construct + +A one-line Scala `if` statement looks like this: + +```scala +if x == 1 then println(x) +``` + +When you need to run multiple lines of code after an `if` equality comparison, use this syntax: + +```scala +if x == 1 then + println("x is 1, as you can see:") + println(x) +``` + +The `if`/`else` syntax looks like this: + +```scala +if x == 1 then + println("x is 1, as you can see:") + println(x) +else + println("x was not 1") +``` + +And this is the `if`/`else if`/`else` syntax: + +```scala +if x < 0 then + println("negative") +else if x == 0 + println("zero") +else + println("positive") +``` + +You can optionally include an `end if` statement at the end of each expression, if you prefer: + +```scala +if x == 1 then + println("x is 1, as you can see:") + println(x) +end if +``` + + +### `if`/`else` expressions always return a result + +Note that `if`/`else` comparisons form _expressions_, meaning that they return a value which you can assign to a variable. Because of this, there’s no need for a special ternary operator: + +```scala +val minValue = if a < b then a else b +``` + +Because they return a value, you can use `if`/`else` expressions as the body of a method: + +```scala +def compare(a: Int, b: Int): Int = + if a < b then + -1 + else if a == b then + 0 + else + 1 +``` + +### Aside: Expression-oriented programming + +As a brief note about programming in general, when every expression you write returns a value, that style is referred to as _expression-oriented programming_, or EOP. For example, this is an _expression_: + +```scala +val minValue = if a < b then a else b +``` + +Conversely, lines of code that don’t return values are called _statements_, and they’re used for their _side-effects_. For example, these lines of code don’t return values, so they’re used for their side effects: + +```scala +if a == b then doSomething() +println("Hello") +``` + +The first example runs the `doSomething` method as a side effect when `a` is equal to `b`. The second example is used for the side effect of printing a string to STDOUT. As you learn more about Scala you’ll find yourself writing more _expressions_ and fewer _statements_. + + + +## `for` loops + +In its most simple use, a Scala `for` loop can be used to iterate over the elements in a collection. For example, given a sequence of integers, You can loop over its elements and print their values like this: + +```scala +val ints = Seq(1,2,3) +for i <- ints do println(i) +``` + +The code `i <- ints` is referred to as a _generator_, and if you leave the parentheses off of the generator, the `do` keyword is required before the code that follows it. Otherwise you can write the code like this: + +```scala +for (i <- ints) println(i) +``` + +Regardless of which approach you use, this is what the result looks like in the Scala REPL: + +```` +scala> val ints = Seq(1,2,3) +ints: Seq[Int] = List(1, 2, 3) + +scala> for i <- ints do println(i) +1 +2 +3 +```` + +When you need a multiline block of code following the `if` condition, use either of these approaches: + +```scala +// option 1 +for + i <- ints +do + val x = i * 2 + println(s"i = $i, x = $x") + +// option 2 +for (i <- ints) + val x = i * 2 + println(s"i = $i, x = $x") + +// option 3 +for (i <- ints) { + val x = i * 2 + println(s"i = $i, x = $x") +} +``` + +### Multiple generators + +`for` loops can have multiple generators, as shown in this example: + +```scala +for + i <- 1 to 2 + j <- 'a' to 'b' + k <- 1 to 10 by 5 +do + println(s"i = $i, j = $j, k = $k") +``` + +That expression prints this output: + +```` +i = 1, j = a, k = 1 +i = 1, j = a, k = 6 +i = 1, j = b, k = 1 +i = 1, j = b, k = 6 +i = 2, j = a, k = 1 +i = 2, j = a, k = 6 +i = 2, j = b, k = 1 +i = 2, j = b, k = 6 +```` + +### Guards + +`for` loops can also contain `if` statements, which are known as _guards_: + +```scala +for + i <- 1 to 5 + if i % 2 == 0 +do + println(i) +``` + +The output of that loop is: + +```` +2 +4 +```` + +A `for` loop can have as many guards as needed. This example shows one way to print the number `4`: + +```scala +for + i <- 1 to 10 + if i > 3 + if i < 6 + if i % 2 == 0 +do + println(i) +``` + +### Using `for` with Maps + +You can also use `for` loops with a `Map`. For example, given this `Map` of movie names and ratings: + + +```scala +val ratings = Map( + "Lady in the Water" -> 3.0, + "Snakes on a Plane" -> 4.0, + "You, Me and Dupree" -> 3.5 +) +``` + +You can print the movie names and ratings using `for` like this: + +```scala +for (name,rating) <- ratings do println(s"Movie: $name, Rating: $rating") +``` + +Here’s what that looks like in the REPL: + +```scala +scala> for (name,rating) <- ratings do println(s"Movie: $name, Rating: $rating") +Movie: Lady in the Water, Rating: 3.0 +Movie: Snakes on a Plane, Rating: 4.0 +Movie: You, Me and Dupree, Rating: 3.5 +``` + +As the `for` loop iterates over the map, each rating is bound to the variables `name` and `rating`, which are in a tuple: + +```scala +(name,rating) <- ratings +``` + +As the loop runs, the variable `name` is assigned to the current _key_ in the map, and the variable `rating` is assigned to the current map _value_. + + + + + +## `for` expressions + +In the previous `for` loop examples, those loops were all used for _side effects_, specifically to print those values to STDOUT using `println`. + +It’s important to know that you can also create `for` _expressions_ that return values. You create a `for` expression by adding the `yield` keyword and an expression to return, like this: + +```scala +val list = + for + i <- 10 to 12 + yield + i * 2 + +// result: list == Vector(20,22,24) +``` + +After that `for` expression runs, the variable `list` is a `Vector` that contains the values shown. This is how the expression works: + +1. The `for` expression starts to iterate over the values in the range `(10, 11, 12)`. It first works on the value `10`, multiples it by `2`, then _yields_ that result, the value `20`. +2. Next, it works on the `11` — the second value in the range. It multiples it by `2`, then yields the value `22`. You can think of these yielded values as accumulating in a temporary holding place. +3. Finally the loop gets the number `12` from the range, multiplies it by `2`, yielding the number `24`. The loop completes at this point and yields the final result, the `Vector(20,22,24)`. + +`for` expressions can be used any time you need to traverse all of the elements in a collection and apply an algorithm to those elements to create a new list. + +Here’s an example that shows how to use a block of code after the `yield`: + +```scala +val names = List("_adam", "_david", "_frank") + +val capNames = for name <- names yield + // imagine this algorithm requires multiple lines + val nameWithoutUnderscore = name.drop(1) + val capName = nameWithoutUnderscore.capitalize + capName + +// result: List(Adam, David, Frank) +``` + + +### Using a `for` expression as the body of a method + +Because a `for` expression yields a result, it can be used as the body of a method that returns a useful value. This method returns all of the values in a given list of integers that are between `3` and `10`: + +```scala +def between3and10(xs: List[Int]): List[Int] = + for + x <- xs + if x >= 3 + if x <= 10 + yield x + +between3and10(List(1,3,7,11)) // result: List(3, 7) +``` + + + + + +## `while` loops + +Scala `while` loop syntax looks like this: + +```scala +var i = 0 + +while i < 3 do + println(i) + i += 1 +``` + +If you use parentheses around the test condition, it can also be written like this: + +```scala +var i = 0 + +while (i < 3) { + println(i) + i += 1 +} +``` + + + + + +## `match` expressions + +Pattern matching is a major feature of functional programming languages, and Scala includes a `match` expression that has many capabilities. + +In the most simple case you can use a `match` expression like a Java `switch` statement, matching cases based on an integer value. Notice that this really is an expression, as it evaluates to a result: + +```scala +import scala.annotation.switch + +// `i` is an integer +val day = i match + case 0 => "Sunday" + case 1 => "Monday" + case 2 => "Tuesday" + case 3 => "Wednesday" + case 4 => "Thursday" + case 5 => "Friday" + case 6 => "Saturday" + case _ => "invalid day" // the default, catch-all +``` + +In this example the variable `i` is tested against the cases shown. If it’s between `0` and `6`, `day` is bound to a string that represents one of the days of the week. Otherwise, the catch-all case is represented by the `_` character, and `day` is bound to the string, `"invalid day"`. + +>When writing simple `match` expressions like this, it’s recommended to use the `@switch` annotation on the variable `i`. This annotation provides a compile time warning if the switch can’t be compiled to a `tableswitch` or `lookupswitch`, which are better for performance. See the Reference documentation for more details. + + +### Using the default value + +When you need to access the catch-all, default value in a `match` expression, just provide a variable name on the left side of the `case` statement, and then use that variable name on the right side of the statement as needed: + +```scala +i match + case 0 => println("1") + case 1 => println("2") + case what => println(s"You gave me: $what" ) +``` + +In this example the variable is named `what` to show that it can be given any legal name, but it’s more commonly given a name like `default`. + + +### Handling multiple possible matches on one line + +As mentioned, `match` expressions have many capabilities. This example shows how to use multiple possible pattern matches in each `case` statement: + +```scala +val evenOrOdd = i match + case 1 | 3 | 5 | 7 | 9 => println("odd") + case 2 | 4 | 6 | 8 | 10 => println("even") + case _ => println("some other number") +``` + + +### Using `if` expressions in `case` statements + +You can also use `if` expressions in the `case` statements of `match` expressions. In this example the second and third `case` statements both use `if` expressions to match multiple integer values: + +```scala +i match + case 1 => println("one, a lonely number") + case x if x == 2 || x == 3 => println("two’s company, three’s a crowd") + case x if x > 3 => println("4+, that’s a party") + case _ => println("i’m guessing your number is zero or less") +``` + +Here’s another example that shows how you can use `if` expressions in `case` statements. This example shows how to match a given value against ranges of numbers: + +```scala +i match + case a if 0 to 9 contains a => println(s"0-9 range: $a") + case b if 10 to 19 contains b => println(s"10-19 range: $b") + case c if 20 to 29 contains c => println(s"20-29 range: $c") + case _ => println("Hmmm...") +``` + + +#### Case classes and match expressions + +You can also extract fields from `case` classes — and classes that have properly written `apply`/`unapply` methods — and use those in your guard conditions. Here’s an example using a simple `Person` case class: + +```scala +case class Person(name: String) + +def speak(p: Person) = p match + case Person(name) if name == "Fred" => println(s"$name says, Yubba dubba doo") + case Person(name) if name == "Bam Bam" => println(s"$name says, Bam bam!") + case _ => println("Watch the Flintstones!") + +speak(Person("Fred")) // "Fred says, Yubba dubba doo" +speak(Person("Bam Bam")) // "Bam Bam says, Bam bam!" +``` + + +### 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. This method takes a `Boolean` value as an input parameter, and returns a `String`, based on the result of the `match` expression: + +```scala +def isTrue(a: Any) = a match + case 0 | "" => false + case _ => true +``` + +Because the input parameter `a` is defined to be the `Any` type — which is the root of all Scala classes, like `Object` in Java — this method works with any data type that’s passed in. After that, the body of the method is just two `case` statements, one that equates `0` or an empty string to `false`, and a default case that returns `true` for any other value. These examples show how this method works: + +```scala +isTrue(0) // false +isTrue("") // false +isTrue(1) // true +isTrue(" ") // true +isTrue(2F) // true +``` + +Using a `match` expression as the body of a method is a very common use. + + +#### Match expressions support many different types of patterns + +`match` expressions can work with many different types of patterns. The pattern types that can be matched are: + +- Constant +- Sequence +- Tuple +- Constructor +- Typed +- Default/wildcard + +All of these patterns are shown in the following `pattern` method, which takes an input parameter of tye `Any` and returns a `String`: + +```scala +def pattern(x: Any): String = x match + + // constant patterns + case 0 => "zero" + case true => "true" + case "hello" => "you said 'hello'" + case Nil => "an empty List" + + // sequence patterns + case List(0, _, _) => "a 3-element list with 0 as the first element" + case List(1, _*) => "list, starts with 1, has any number of elements" + case Vector(1, _*) => "vector, starts w/ 1, has any number of elements" + + // tuples + case (a, b) => s"got $a and $b" + case (a, b, c) => s"got $a, $b, and $c" + + // constructor patterns + case Person(first, "Alexander") => s"Alexander, first name = $first" + case Dog("Zeus") => "found a dog named Zeus" + + // typed patterns + case s: String => s"got a string: $s" + case i: Int => s"got an int: $i" + case f: Float => s"got a float: $f" + case a: Array[Int] => s"array of int: ${a.mkString(",")}" + case as: Array[String] => s"string array: ${as.mkString(",")}" + case d: Dog => s"dog: ${d.name}" + case list: List[_] => s"got a List: $list" + case m: Map[_, _] => m.toString + + // the default wildcard pattern + case _ => "Unknown" +``` + +`match` expressions and patterns are discussed in more detail in the Reference documentation. + + + +## try/catch/finally + +Like Java, Scala has a `try`/`catch`/`finally` construct to let you catch and manage exceptions. For consistency, Scala uses the same syntax that `match` expressions use: `case` statements to match the different possible exceptions that can occur. + +Here’s an example of Scala’s `try`/`catch`/`finally` syntax. In this example, `openAndReadAFile` is a method that does what its name implies: it opens a file and reads the text in it, assigning the result to the mutable variable `text`: + +```scala +var text = "" +try + text = openAndReadAFile(filename) +catch + case fnf: FileNotFoundException => fnf.printStackTrace() + case ioe: IOException => ioe.printStackTrace() +finally + // close your resources here + println("Came to the 'finally' clause.") +``` + +Assuming that the `openAndReadAFile` method uses the Java _java.io.*_ classes to read a file and doesn’t catch its exceptions, attempting to open and read a file can result in both a `FileNotFoundException` and an `IOException`, and those two exceptions are caught in the `catch` block of this example. + + + +## More information + +That covers the basics of Scala control structures. For more details, see the Reference documentation. + + + + + diff --git a/_overviews/overview/introduction.md b/_overviews/overview/introduction.md new file mode 100644 index 0000000000..eecc4e8670 --- /dev/null +++ b/_overviews/overview/introduction.md @@ -0,0 +1,11 @@ +--- +title: Introduction +description: This page begins the overview documentation of the Scala 3 language. +--- + +Welcome to the _Overview_ documentation for Scala 3. The goal of this documentation is to provide an informal introduction to the Scala language. It touches on all Scala topics, but as its name implies, generally just provides an overview of each feature. If at any time while you’re reading the Overview and you want more information on a specific feature, you’ll find links to our _Reference_ documentation, which covers all of the language features in detail. + +Our hope in these pages is to demonstrate that Scala is a beautiful, expressive programming language, with a clean, modern syntax, and supports functional programming (FP), object-oriented programming (OOP), and a fusion of FP and OOP in a typed setting. Scala’s syntax, grammar, and features have been re-thought, debated in an open process, and updated in 2020 to be more clear and easier to understand than ever before. + +The Overview begins with a whirlwind tour of many of Scala’s features in the “A Taste of Scala” section. After that tour, the sections that follow it provide more details on those language features. + From d2887380914f6c8df1d8d2a3f9a25ec6fb122ec1 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Tue, 6 Oct 2020 18:55:38 -0600 Subject: [PATCH 015/169] Added the initial version of the Scala Features document --- _overviews/overview/scala-features.md | 496 ++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) diff --git a/_overviews/overview/scala-features.md b/_overviews/overview/scala-features.md index e69de29bb2..9d487f85e6 100644 --- a/_overviews/overview/scala-features.md +++ b/_overviews/overview/scala-features.md @@ -0,0 +1,496 @@ +--- +title: Scala 3 Programming Language Features +description: This page discusses the main features of the Scala 3 programming language. +--- + + + +The name _Scala_ comes from the word _scalable_, and true to that name, the Scala language is used to power busy websites and analyze huge data sets. This section introduces the features that make Scala a scalable language. These features are split into three sections: + +- High-level language features +- Lower-level language features +- Scala ecosystem features + + + + + +## 1) High-level Scala language features + +Looking at Scala from the proverbial “30,000 foot view,” you can make the following statements about it: + +- It’s a high-level programming language +- It has an expressive syntax +- It’s statically-typed (but feels dynamic) +- It has an expressive type system +- It’s a pure functional programming (FP) language +- It’s a pure object-oriented programming (OOP) language +- It supports the fusion of FP and OOP +- Contextual abstractions provide a clear way to implement _term inference_ +- It runs on the JVM (and in the browser) +- It interacts seamlessly with Java code +- It’s used for server-side applications (including microservices), big data applications, and can also be used in the browser with Scala.js +- TODO: add something in this section or in the second section about the consistency of the language (I need help here) + +The following sections take a quick look at these features. + + +### It’s a high-level programming language + +Scala is considered a high-level language in at least two ways. First, like Java and many other modern languages, you don’t deal with low-level concepts like pointers and memory management. + +Second, with the use of lambdas and higher-order functions, you write your code at a very high level. As the functional programming saying goes, in Scala you write _what_ you want, not _how_ to achieve it. That is, we don’t write imperative code like this: + +```scala +def double(ints: List[Int]): List[Int] = { + val buffer = new ListBuffer[Int]() + for (i <- ints) { + buffer += i * 2 + } + buffer.toList + foo + bar +} + +val newNumbers = double(oldNumbers) +``` + +That code instructs the compiler what to do on a step-by-step basis. Instead, we write high-level, functional code using higher-order functions and lambdas like this to achieve the same effect: + +```scala +val newNumbers = oldNumbers.map(_ * 2) +``` + +As you can see, that code is much more concise, easier to read, and easier to maintain. + + +### It has an expressive syntax + +Scala has a concise, readable syntax that is often referred to as _expressive_. For instance, variables are created concisely: + +```scala +val nums = List(1,2,3) +val p = Person("Martin", "Odersky") +``` + +Higher-order functions and lambdas make for expressive code: + +```scala +nums.map(i => i * 2) // long form +nums.map(_ * 2) // short form + +nums.filter(i => i > 1) +nums.filter(_ > 1) +``` + +And traits, classes, and methods are defined with a clean, light syntax: + +```scala +trait Animal: + def speak(): Unit + +trait HasTail: + def wagTail(): Unit + +class Dog extends Animal, HasTail: + def speak() = println("Woof") + def wagTail() = println("⎞⎜⎛ ⎞⎜⎛") +``` + +Studies have shown that the time a developer spends _reading_ code to _writing_ code is at least a 10:1 ratio, so writing code that is concise _and_ readable is important. + + +### It’s statically-typed (but feels dynamic) + +Scala is a statically-typed language, but thanks to its type inference capabilities it feels dynamic. All of these expressions look like a dynamically-typed language like Python or Ruby, but they’re all Scala: + +```scala +val s = "Hello" +val p = Person("Al", "Pacino") +val sum = ints.reduceLeft(_ + _) +val y = for i <- nums yield i * 2 +val z = nums.filter(_ > 100) + .filter(_ < 10_000) + .map(_ * 2) +``` + +Because Scala is considered to be a [strong, statically-typed language](https://heather.miller.am/blog/types-in-scala.html), you get all the benefits of static types: + +- Correctness: you catch most errors at compile-time +- Great IDE support + - Code completion + - Catching errors at compile-time means catching mistakes as you type + - Easy and reliable refactoring + - Reliable code completion +- You can refactor your code with confidence +- Method type declarations tell readers what the method does, and help serve as documentation +- Types make your code easier to maintain +- Scalability: types help ensure correctness across arbitrarily large applications and development teams +- Strong types enable Scala features like implicits (TODO: I need help on this wording and description) + + + + +### Expressive type system + + +Scala’s expressive type system enforces, at compile-time, that abstractions are used in a safe and coherent manner. In particular, the type system supports: + +* [Generic classes](/tour/generic-classes.html) +* [Variance annotations](/tour/variances.html) +* [Upper](/tour/upper-type-bounds.html) and [lower](/tour/lower-type-bounds.html) type bounds +* [Inner classes](/tour/inner-classes.html) and [abstract type members](/tour/abstract-type-members.html) as object members +* [Compound types](/tour/compound-types.html) +* [Explicitly typed self references](/tour/self-types.html) +* [Implicit parameters](/tour/implicit-parameters.html) and [conversions](/tour/implicit-conversions.html) +* [Polymorphic methods](/tour/polymorphic-methods.html) +* [Type inference](/tour/type-inference.html) means the user is not required to annotate code with redundant type information. + +In combination, these features provide a powerful basis for the safe reuse of programming abstractions and for the type-safe extension of software. + + + + +### It’s a pure functional programming language + +Scala is a functional programming (FP) language, meaning: + +- Functions are variables, and can be passed around like any other variable +- Higher-order functions are directly supported +- Lambdas are built in +- Everything in Scala is an expression that returns a value +- Syntactically it’s easy to use immutable variables, and their use is encouraged +- It has a wealth of immutable collections classes in the standard library +- Those collections classes come with dozens of functional methods: they don’t mutate the collection, but instead return an updated copy of the data + + +### It’s a pure object-oriented programming language + +Scala is a _pure_ object-oriented programming (OOP) language. Every variable is an object, and every “operator” is a method. + +In Scala, all types inherit from a top-level class `Any`, whose immediate children are `AnyVal` (_value types_, such as `Int` and `Boolean`) and `AnyRef` (_reference types_, as in Java). This means that the Java distinction between primitive types and boxed types (e.g. `int` vs. `Integer`) isn’t present in Scala. Boxing and unboxing is completely transparent to the user. + + + + +### Scala supports the fusion of FP and OOP + + + +The essence of Scala is the fusion of functional programming and object-oriented programming in a typed settings: + +- Functions for the logic +- Objects for the modularity + +As [Martin Odersky has stated](https://jaxenter.com/current-state-scala-odersky-interview-129495.html), “Scala was designed to show that a fusion of functional and object-oriented programming is possible and practical.” + +For more details on this philosophy, see the TODO section in the Reference documentation. + + +### Contextual abstractions provide a clear way to implement term inference + +Following Haskell, Scala was the second popular language to have some form of _implicits_. In Scala 3 these concepts have been completely re-thought and more clearly implemented. + +The core idea is _term inference_: Given a type, the compiler synthesizes a “canonical” term that has that type. In Scala, an implicit parameter directly leads to an inferred argument term that could also be written down explicitly. + +Use cases for this concept include implementing type classes, establishing context, dependency injection, expressing capabilities, computing new types, and proving relationships between them. + +Scala 3 makes this process more clear than ever before. See the TODO section of the Reference documentation for more details. + + +### Scala runs on the JVM (and in the browser) + +Scala code runs on the Java Virtual Machine (JVM), so you get all of its benefits: + +- Security +- Performance +- Memory management +- Portability and platform independence +- The ability to use the wealth of existing Java and JVM libraries + +In addition to running on the JVM, Scala also runs in the browser with Scala.js (and open source third-party tools to integrate popular JavaScript libraries), and native executables can be built with Scala Native and GraalVM. + + +### Interacts seamlessly with Java + +You can use Java classes and libraries in your Scala applications, and you can use Scala code in your Java applications. In regards to the second point, large libraries like [Akka](https://akka.io) and the [Play Framework](https://www.playframework.com) are written in Scala, and can be used in Java applications. + +In regards to the first point, Java classes and libraries are used in Scala applications every day. For instance, in Scala you can read files with a Java `BufferedReader` and `FileReader`: + +```scala +import java.io._ +val br = BufferedReader(FileReader(filename)) +// read the file with `br` ... +``` + +Using Java code in Scala is generally seamless. + +Java collections can also be used in Scala, and if you want to use Scala’s rich collection class methods with them, you can convert them with just a few lines of code: + +```scala +import scala.jdk.CollectionConverters._ +val scalaList: Seq[Integer] = JavaClass.getJavaList().asScala.toSeq +``` + + +### Used for server-side apps, big data, Scala.js + +As you’ll see in the third section of this page, Scala libraries and frameworks like these have been written to power busy websites and work with huge datasets: + +1. The [Play Framework](https://www.playframework.com) is a lightweight, stateless, developer-friendly, web-friendly architecture for creating highly-scalable applications +2. [Lagom](https://www.lagomframework.com) is a microservices framework that helps you decompose your legacy monolith and build, test, and deploy entire systems of reactive microservices +3. [Apache Spark](https://spark.apache.org) is a unified analytics engine for big data processing, with built-in modules for streaming, SQL, machine learning and graph processing + +The [Awesome Scala list](https://github.com/lauris/awesome-scala) shows dozens of additional open source tools that developers have created to build Scala applications. + +In addition to server-side programming, [Scala.js](https://www.scala-js.org) is a strongly-typed replacement for writing JavaScript, with open source third-party libraries that include tools to integrate with Facebook’s React library, jQuery, and more. + + + + +## 2) Lower-level language features + +Where the previous section covered high-level features of Scala 3, it’s interesting to note that at a high level you can make the same statements about both Scala 2 and Scala 3. A decade ago Scala started with a strong foundation of desirable features, and as you’ll see in this section, those benefits have been improved with Scala 3. + +At a “sea level” view of the details — i.e., the language features programmers use everyday — Scala 3 has significant advantages over Scala 2: + +- The ability to create algebraic data types (ADTs) more concisely with enums +- An even more concise and readable syntax: + - The “quiet” control structure syntax is easier to read + - Optional braces + - Fewer symbols in the code creates less visual noise, making it easier to read + - The `new` keyword is generally no longer needed when creating class instances + - The formality of package objects have been dropped in favor of simpler “top level” definitions +- A clearer grammar: + - Multiple different uses of the `implicit` keyword have been removed + - Those uses are replaced by more obvious keywords like `given`, `using`, and `extension`, focusing on intent over mechanism + - Extension methods replace implicit classes with a clearer and simpler mechanism + - The addition of the `open` modifier for classes makes the developer intentionally declare that a class is open for modification, thereby limiting ad-hoc extensions to a code base + - Multiversal equality rules out nonsensical comparisons with `==` and `!=` (i.e., attempting to compare a `Person` to a `Planet`) + - Macros are implemented much more easily + - TODO: Benefits of Union and intersection types (I need help here) + - Trait parameters replace and simplify early initializers + - Opaque type aliases replace most uses of value classes, while guaranteeing the absence of boxing + - Export clauses provide a simple and general way to express aggregation, which can replace the previous facade pattern of package objects inheriting from classes + - The procedure syntax has been dropped, and the varargs syntax has been changed, both to make the language more consistent + - The `@infix` annotation makes it obvious how you want a method to be applied + - The `@alpha` method annotation defines an alternate name for the method, improving Java interoperability, and letting you provide aliases for symbolic operators + +It would take too much space to demonstrate all of those features here, but follow the links in those items above to see those features in action. + + + + + + + + +## 3) Scala ecosystem features + +Scala has a vibrant ecosystem, with libraries and frameworks for every need. The [“Awesome Scala” list](https://github.com/lauris/awesome-scala) provides a list of hundreds of open source projects that are available to Scala developers. Some of the more notable are listed below. + + + + +### Web development + +- The [Play Framework](https://www.playframework.com) followed the Ruby on Rails model to become a lightweight, stateless, developer-friendly, web-friendly architecture for highly-scalable applications +- [Scalatra](https://scalatra.org) is a tiny, high-performance, async web framework, inspired by Sinatra +- [Finatra](https://twitter.github.io/finatra) is Scala services built on TwitterServer and Finagle +- [Scala.js](https://www.scala-js.org) is a strongly-typed replacement for JavaScript that provides a safer way to build robust front-end web applications +- [ScalaJs-React](https://github.com/japgolly/scalajs-react) lifts Facebook’s React library into Scala.js, and endeavours to make it as type-safe and Scala-friendly as possible +- [Lagom](https://www.lagomframework.com) is a microservices framework that helps you decompose your legacy monolith and build, test, and deploy entire systems of Reactive microservices + +HTTP(S) libraries: + +- Akka-http +- Finch +- Http4s +- Sttp + +JSON libraries: + +- Argonaut +- Circe +- Jackson/Scala +- Json4s +- Jsoniter +- Play-JSON + +Serialization: + +- ScalaPB + +Science and data analysis: + +- Algebird +- Spire +- Squants + + +### Big data + +- Spark +- Apache Flink +- SCIO, A Scala API for Apache Beam and Google Cloud Dataflow. + + +### AI, machine learning + +- BigDL, Distributed Deep Learning Framework for Apache Spark +- TensorFlow- Scala + + +### Concurrency + +- Akka + - Actor model + - Concurrency + - High throughput + + +### FP + +- Cats +- Zio + + +### Functional reactive programming + +- fs2 +- monix +- ScalaRx + + +### Build tools + +- sbt +- Gradle +- Mill +- Fury? + + + +## Summary + +As this page shows, Scala has many terrific programming language features at a high level, at an everyday programming level, and through its developer ecosystem. + + + + From 8995017de99b4745ba791d3491643fd45d272d58 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Thu, 8 Oct 2020 15:19:28 -0600 Subject: [PATCH 016/169] =?UTF-8?q?Initial=20version=20of=20the=20?= =?UTF-8?q?=E2=80=98Interacting=20With=20Java=E2=80=99=20document.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/interacting-with-java.md | 368 +++++++++++++++++++ 1 file changed, 368 insertions(+) diff --git a/_overviews/overview/interacting-with-java.md b/_overviews/overview/interacting-with-java.md index e69de29bb2..df8c7e4555 100644 --- a/_overviews/overview/interacting-with-java.md +++ b/_overviews/overview/interacting-with-java.md @@ -0,0 +1,368 @@ +--- +title: Interacting with Java +description: This page demonstrates how Scala code can interact with Java, and how Java code can interact with Scala code. +--- + + +## Introduction + +This section looks at how to use Java code in Scala, and the opposite, how to use Scala code in Java. + +In general, using Java code in Scala is pretty seamless. There are only a few points where you’ll want to use Scala utilities to convert Java concepts to Scala, including: + +- Java collections classes +- The Java `Optional` class + +Similarly, if you’re writing Java code and want to use Scala concepts, you’ll want to convert Scala collections and the Scala `Option` class. + +These following sections demonstrate the most common conversions you’ll need: + +- How to use Java collections in Scala +- How to use Java `Optional` in Scala +- Extending Java interfaces in Scala +- How to use Scala collections in Java +- How to use Scala `Option` in Java +- How to use Scala traits in Java +- How to handle Scala methods that throw exceptions in Java code +- How to use Scala varargs parameters in Java +- Create alternate names to use Scala methods in Java + +Note that the Java examples in this section assume that you’re using Java 11 or newer. + + + +## How to use Java collections in Scala + +When you’re writing Scala code and need to use a Java collection class, you _can_ just use the class as-is. However, if you want to use the class in a Scala `for` loop, or want to take advantage of the higher-order functions on the Scala collections classes, you’ll want to convert the Java collection to a Scala collection. + +Here’s an example of how this works. Given this Java `ArrayList`: + + +```java +// java +public class JavaClass { + public static List getStrings() { + return new ArrayList(List.of("a", "b", "c")); + } +} +``` + +You can convert that Java list to a Scala `Seq`, using the conversion utilities in the Scala _scala.jdk.CollectionConverters_ package: + + +```scala +// scala +import scala.jdk.CollectionConverters._ +import java.util.List + +def testList = + println("Using a Java List in Scala") + val javaList: java.util.List[String] = JavaClass.getStrings() + val scalaSeq: Seq[String] = javaList.asScala.toSeq + scalaSeq.foreach(println) + for s <- scalaSeq do println(s) +``` + +Of course that code can be shortened, but the individual steps are shown here to demonstrate exactly how the conversion process works. + + + +## How to use Java `Optional` in Scala + +When you need to use the Java `Optional` class in your Scala code, import the _scala.jdk.OptionConverters_ object, and then use the `toScala` method to convert the `Optional` value to a Scala `Option`. + +To demonstrate this, here’s a Java class with two `Optional` values, one containing a string and the other one empty: + + +```java +// java +import java.util.Optional; + +public class JavaClass { + static Optional oString = Optional.of("foo"); + static Optional oEmptyString = Optional.empty(); +} +``` + +Now in your Scala code you can access those fields. If you just access them directly, they’ll both be `Optional` values: + + +```scala +// scala +import java.util.Optional + +val optionalString = JavaClass.oString // Optional[foo] +val eOptionalString = JavaClass.oEmptyString // Optional.empty +``` + +But by using the _scala.jdk.OptionConverters_ methods, you can convert them to Scala `Option` values: + + +```scala +import java.util.Optional +import scala.jdk.OptionConverters._ + +val optionalString = JavaClass.oString // Optional[foo] +val optionString = optionalString.toScala // Some(foo) + +val eOptionalString = JavaClass.oEmptyString // Optional.empty +val eOptionString = eOptionalString.toScala // None +``` + + + +## Extending Java interfaces in Scala + +If you need to use Java interfaces in your Scala code, extend them just as though they are Scala traits. For example, given these three Java interfaces: + + +```java +// java +interface Animal { + void speak(); +} + +interface Wagging { + void wag(); +} + +interface Running { + // an implemented method + default void run() { + System.out.println("I’m running"); + } +} +``` + +you can create a `Dog` class in Scala just as though you were using traits. All you have to do is implement the `speak` and `wag` methods: + + +```scala +// scala +class Dog extends Animal, Wagging, Running: + def speak = println("Woof") + def wag = println("Tail is wagging") + +@main def useJavaInterfaceInScala = + val d = new Dog + d.speak + d.wag +``` + + + +## 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.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 +// scala +class ScalaClass: + val strings = List("a", "b") +``` + +You can access that Scala `List` in your Java code like this: + + +```java +// java +import scala.jdk.javaapi.CollectionConverters; + +// create an instance of the Scala class +ScalaClass sc = new ScalaClass(); + +// access the `strings` field as `sc.strings()` +scala.collection.immutable.List xs = sc.strings(); + +// convert the Scala `List` a Java `List` +java.util.List listOfStrings = CollectionConverters.asJava(xs); +listOfStrings.forEach(System.out::println); +``` + +That code can be shortened, but the full steps are shown to demonstrate how the process works. Here are a few things to notice in that code: + +- In your Java code, you create an instance of `ScalaClass` just like an instance of a Java class +- `ScalaClass` has a field named `strings`, but from Java you access that field as a method, i.e., as `sc.strings()` + + + +## 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. + +To demonstrate this, create a Scala class with two `Option[String]` values, one containing a string and the other one empty: + + +```scala +// scala +object ScalaObject: + val someString = Option("foo") + 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: + + +```java +// java +import java.util.Optional; +import static scala.jdk.javaapi.OptionConverters.toJava; + +public class JUseScalaOptionInJava { + public static void main(String[] args) { + Optional stringSome = toJava(ScalaObject.someString()); // Optional[foo] + Optional stringNone = toJava(ScalaObject.noneString()); // Optional.empty + System.out.printf("stringSome = %s\n", stringSome); + System.out.printf("stringNone = %s\n", stringNone); + } +} +``` + +The two Scala `Option` fields are now available as Java `Optional` values. + + + +## How to use Scala traits in Java + +With Java 11 you can use a Scala trait just like a Java interface, even if the trait has implemented methods. For example, given these two Scala traits, one with an implemented method and one with only an interface: + + +```scala +// scala +trait ScalaAddTrait: + def sum(x: Int, y: Int) = x + y // implemented + +trait ScalaMultiplyTrait: + def multiply(x: Int, y: Int): Int // abstract +``` + +A Java class can implement both of those interfaces, and implement the `multiply` method: + + +```java +// java +class JavaMath implements ScalaAddTrait, ScalaMultiplyTrait { + public int multiply(int a, int b) { + return a * b; + } +} + +JavaMath jm = new JavaMath(); +System.out.println(jm.sum(3,4)); // 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 soem 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. + +For example, this Scala `exceptionThrower` method is annotated to declare that it throws an `Exception`: + + +```scala +// scala +object SExceptionThrower: + @throws(classOf[Exception]) + def exceptionThrower = + throw new Exception("Idiomatic Scala methods don’t throw exceptions") +``` + +As a result, you’ll need to handle the exception in your Java code. For instance, this code won’t compile because I don’t handle the exception: + + +```java +// java: won’t compile because the exception isn’t handled +public class ScalaExceptionsInJava { + public static void main(String[] args) { + SExceptionThrower.exceptionThrower(); + } +} +``` + +The compiler gives this error: + +.... +[error] ScalaExceptionsInJava: unreported exception java.lang.Exception; + must be caught or declared to be thrown +[error] SExceptionThrower.exceptionThrower() +.... + +This is good — it’s what you want: the annotation tells the Java compiler that `exceptionThrower` can throw an exception. Now when you’re writing Java code you must handle the exception with a `try` block or declare that your Java method throws an exception. + +Conversely, if you leave the annotation off of the Scala `exceptionThrower` method, the Java code _will compile_. This is probably not what you want, because the Java code may not account for the Scala method throwing the exception. + + + +## How to use Scala varargs parameters in Java + +When a Scala method has a varargs parameter and you want to use that method in Java, mark the Scala method with the `@varargs` annotation. For example, the `printAll` method in this Scala class declares a `String*` varargs field, and the method is marked with the `@varargs` annotation: + + +```scala +// scala +import scala.annotation.varargs + +object VarargsPrinter: + @varargs def printAll(args: String*): Unit = args.foreach(println) +``` + +Because `printAll` is declared with the `@varargs` annotation, it can be called from a Java program with a variable number of parameters, as shown in this example: + + +```scala +// java +public class JVarargs { + public static void main(String[] args) { + VarargsPrinter.printAll("Hello", "world"); + } +} + +``` + +When this code is run, it results in the following output: + +.... +Hello +world +.... + + + +## Create alternate names to use Scala methods in Java + +In Scala you might want to create a method name using a symbolic character: + + +```scala +def +(a: Int, b: Int) = a + b +``` + +That method name won’t work well in Java, but what you can do in Scala 3 is provide an “alternate” name for the method — an alias — that will work in Java: + + +```scala +import scala.annotation.alpha + +class Adder: + @alpha("add") def +(a: Int, b: Int) = a + b +``` + +Now in your Java code you can use the aliased method name `add`: + +```scala +var adder = new Adder(); +int x = adder.add(1,1); +System.out.printf("x = %d\n", x); +``` + + + +## More information + +This section highlights some of the main topics to know when integrating Scala and Java code bases. For more details, including a few other topics not covered here, see the _Interacting with Java_ section in the Reference documentation. + + + From d53ad0e17f5b76ce732831b2b7449d8880b0070a Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Thu, 8 Oct 2020 16:00:09 -0600 Subject: [PATCH 017/169] =?UTF-8?q?Initial=20version=20of=20the=20?= =?UTF-8?q?=E2=80=98First=20Look=20at=20Types=E2=80=99=20document.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/first-look-at-types.md | 237 +++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/_overviews/overview/first-look-at-types.md b/_overviews/overview/first-look-at-types.md index e69de29bb2..4cb7ca6ef9 100644 --- a/_overviews/overview/first-look-at-types.md +++ b/_overviews/overview/first-look-at-types.md @@ -0,0 +1,237 @@ +--- +title: A First Look at Scala Types +description: This page provides a brief introduction to Scala's built-in data types, including Int, Double, String, Long, Any, AnyRef, Nothing, and Null. +--- + + + +## All values have a type + +In Scala, all values have a type, including numerical values and functions. The diagram below illustrates a subset of the type hierarchy. + + +Scala Type Hierarchy + + + +## Scala type hierarchy + +[`Any`](https://www.scala-lang.org/api/2.12.1/scala/Any.html) is the supertype of all types, also called the top type. It defines certain universal methods such as `equals`, `hashCode`, and `toString`. `Any` has two direct subclasses: `AnyVal` and `AnyRef`. + +*`AnyVal`* represents value types. There are nine predefined value types and they are non-nullable: `Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit`, and `Boolean`. `Unit` is a value type which carries no meaningful information. There is exactly one instance of `Unit` which can be declared literally like so: `()`. All functions must return something so sometimes `Unit` is a useful return type. + +*`AnyRef`* represents reference types. All non-value types are defined as reference types. Every user-defined type in Scala is a subtype of `AnyRef`. If Scala is used in the context of a Java runtime environment, `AnyRef` corresponds to `java.lang.Object`. + +Here’s an example that demonstrates that strings, integers, characters, boolean values, and functions are all objects just like every other object: + +```tut +val list: List[Any] = List( + "a string", + 732, // an integer + 'c', // a character + true, // a boolean value + () => "an anonymous function returning a string" +) + +list.foreach(element => println(element)) +``` + +It defines a value `list` of type `List[Any]`. The list is initialized with elements of various types, but each is an instance of `scala.Any`, so you can add them to the list. + +Here’s the output of the program: + +``` +a string +732 +c +true + +``` + + + +## Scala’s “value types” + +As shown above, Scala’s numeric types extend `AnyVal`, and they’re all full-blown objects. These examples show how to declare variables of these numeric types: + +```scala +val b: Byte = 1 +val i: Int = 1 +val l: Long = 1 +val s: Short = 1 +val d: Double = 2.0 +val f: Float = 3.0 +``` + +In the first four examples, if you don’t explicitly specify a type, the number `1` will default to an `Int`, so if you want one of the other data types — `Byte`, `Long`, or `Short` — you need to explicitly declare those types, as shown. Numbers with a decimal (like 2.0) will default to a `Double`, so if you want a `Float` you need to declare a `Float`, as shown in the last example. + +Because `Int` and `Double` are the default numeric types, you typically create them without explicitly declaring the data type: + +```scala +val i = 123 // defaults to Int +val x = 1.0 // defaults to Double +``` + +In your code you can also append the characters `L`, `D`, and `F` (and their lowercase equivalents) to numbers to specify that they are `Long`, `Double`, or `Float` values: + +```scala +val x = 1_000L // val x: Long = 1000 +val y = 2.2D // val y: Double = 2.2 +val z = 3.3F // val z: Float = 3.3 +``` + +Scala also has `String` and `Char` types, which you can generally declare with the implicit form: + +```scala +val s = "Bill" +val c = 'a' +``` + +As shown, enclose strings in double-quotes — or triple-quotes for multiline strings — and enclose a character in single-quotes. + +Those data types and their ranges are: + +| Data Type | Possible Values | +| ------------- | --------------- | +| Boolean | `true` or `false` | +| Byte | 8-bit signed two’s complement integer (-2^7 to 2^7-1, inclusive)
-128 to 127 | +| Short | 16-bit signed two’s complement integer (-2^15 to 2^15-1, inclusive)
-32,768 to 32,767 +| Int | 32-bit two’s complement integer (-2^31 to 2^31-1, inclusive)
-2,147,483,648 to 2,147,483,647 | +| Long | 64-bit two’s complement integer (-2^63 to 2^63-1, inclusive)
(-2^63 to 2^63-1, inclusive) | +| Float | 32-bit IEEE 754 single-precision float
1.40129846432481707e-45 to 3.40282346638528860e+38 | +| Double | 64-bit IEEE 754 double-precision float
4.94065645841246544e-324d to 1.79769313486231570e+308d | +| Char | 16-bit unsigned Unicode character (0 to 2^16-1, inclusive)
0 to 65,535 | +| String | a sequence of `Char` | + + + +## `BigInt` and `BigDecimal` + +When you need really large numbers, use the `BigInt` and `BigDecimal` types: + +```scala +var a = BigInt(1_234_567_890_987_654_321L) +var b = BigDecimal(123_456.789) +``` + +Where `Double` and `Float` are approximate decimal numbers, `BigDecimal` is used for precise arithmetic, such as when working with currency. + +A great thing about `BigInt` and `BigDecimal` is that they support all the operators you’re used to using with numeric types: + +```scala +val b = BigInt(1234567890) // scala.math.BigInt = 1234567890 +val c = b + b // scala.math.BigInt = 2469135780 +val d = b * b // scala.math.BigInt = 1524157875019052100 +``` + + + +## Two notes about strings + +Scala strings are similar to Java strings, but they have two great additional features: + +- They support string interpolation +- It’s easy to create multiline strings + +### String interpolation + +String interpolation provides a very readable way to use variables inside strings. For instance, given these three variables: + +```scala +val firstName = "John" +val mi = 'C' +val lastName = "Doe" +``` + +You can combine those variables in a string like this: + +```scala +println(s"Name: $firstName $mi $lastName") // "Name: John C Doe" +``` + +Just precede the string with the letter `s`, and then put a `$` symbol before your variable names inside the string. + +To enclose expressions inside a string, put them in curly braces: + +~~~ scala +println(s"2 + 2 = ${2 + 2}") // prints "2 + 2 = 4" +val x = -1 +println(s"x.abs = ${x.abs}") // prints "x.abs = 1" +~~~ + +#### Other interpolators + +The `s` that you place before the string is just one possible interpolator. If you use an `f` instead of an `s`, you can use `printf`-style formatting in the string. Furthermore, because `s` and `f` are really just methods, you can write your own interpolators, such as creating a `sql` interpolator for use in a database library. For more details, see the Strings section in this Overview and in the Reference documentation. + + +### Multiline strings + +Multiline strings are created by including the string inside three double-quotes: + +```scala +val quote = """The essence of Scala: + Fusion of functional and object-oriented + programming in a typed setting.""" +``` + +One drawback of this basic approach is that the lines after the first line are indented, and look like this: + +```scala +"The essence of Scala: + Fusion of functional and object-oriented + programming in a typed setting." +``` + +When spacing is important, put a `|` symbol in front of all lines after the first line, and call the `stripMargin` method after the string: + +```scala +val quote = """The essence of Scala: + |Fusion of functional and object-oriented + |programming in a typed setting.""".stripMargin +``` + +Now all of the lines are left-justified inside the string: + +```scala +"The essence of Scala: +Fusion of functional and object-oriented +programming in a typed setting." +``` + + + +## Type casting + + +Value types can be cast in the following way: +Scala Type Hierarchy + +For example: + +```tut +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 (note that some precision is lost in this case) + +val face: Char = '☺' +val number: Int = face // 9786 +``` + +Casting is unidirectional. This will not compile: + +``` +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 +val z: Long = y // Does not conform +``` + +You can also cast a reference type to a subtype. This will be covered later in the tour. + + + +## `Nothing` and `null` + +`Nothing` is a subtype of all types, also called the bottom type. There is no value that has the type `Nothing`. A common use is to signal non-termination, such as a thrown exception, program exit, or an infinite loop — i.e., it is the type of an expression which does not evaluate to a value, or a method that does not return normally. + +`Null` is a subtype of all reference types (i.e. any subtype of `AnyRef`). It has a single value identified by the keyword literal `null`. `Null` is provided mostly for interoperability with other JVM languages and should almost never be used in Scala code. Alternatives to `null` are discussed in the TODO section of the Overview and the Reference documentation. + + From 8dc7054e521f14560e2c190dee8e060edd76eda6 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Thu, 8 Oct 2020 16:03:57 -0600 Subject: [PATCH 018/169] =?UTF-8?q?Moved=20some=20of=20the=20Types=20conte?= =?UTF-8?q?nt=20to=20the=20=E2=80=98First=20Look=20at=20Types=E2=80=99=20d?= =?UTF-8?q?ocument.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/a-taste-of-scala.md | 27 ++----------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index 9d054cf6b1..4e3cec09c3 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -259,7 +259,7 @@ println(s"x.abs = ${x.abs}") // prints "x.abs = 1" #### Other interpolators -The `s` that you place before the string is just one possible interpolator. If you use an `f` instead of an `s`, you can use `printf`-style formatting in the string. Furthermore, because `s` and `f` are really just methods, you can write your own interpolators, such as creating a `sql` interpolator for use in a database library. For more details, see the Strings section in this Overview and in the Reference documentation. +The `s` that you place before the string is just one possible interpolator. If you use an `f` instead of an `s`, you can use `printf`-style formatting syntax in the string. Furthermore, because `s` and `f` are really just methods, you can write your own interpolators, such as creating a `sql` interpolator for use in a database library. For more details, see the Strings section in this Overview and in the Reference documentation (TODO:correct sections and their urls). ### Multiline strings @@ -272,30 +272,7 @@ val quote = """The essence of Scala: programming in a typed setting.""" ``` -One drawback of this basic approach is that the lines after the first line are indented, and look like this: - -```scala -"The essence of Scala: - Fusion of functional and object-oriented - programming in a typed setting." -``` - -When spacing is important, put a `|` symbol in front of all lines after the first line, and call the `stripMargin` method after the string: - -```scala -val quote = """The essence of Scala: - |Fusion of functional and object-oriented - |programming in a typed setting.""".stripMargin -``` - -Now all of the lines are left-justified inside the string: - -```scala -"The essence of Scala: -Fusion of functional and object-oriented -programming in a typed setting." -``` - +The [“First Look at Types” section](TODO:url) provides more details on how to format multiline strings. From ab04eec3e1c82baa4f9b212329c21a71d9733a05 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Sun, 11 Oct 2020 15:09:23 -0600 Subject: [PATCH 019/169] =?UTF-8?q?Initial=20version=20of=20the=20?= =?UTF-8?q?=E2=80=98Collections=20Classes=20and=20Methods=E2=80=99=20docum?= =?UTF-8?q?ent.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../overview/collections-classes-methods.md | 871 ++++++++++++++++++ 1 file changed, 871 insertions(+) diff --git a/_overviews/overview/collections-classes-methods.md b/_overviews/overview/collections-classes-methods.md index e69de29bb2..717006e96f 100644 --- a/_overviews/overview/collections-classes-methods.md +++ b/_overviews/overview/collections-classes-methods.md @@ -0,0 +1,871 @@ +--- +title: Common Collections Classes and Methods +description: This page demonstrates the common collections classes and their methods in Scala 3. +--- + + + + +This page demonstrates the common Scala 3 collections classes and their accompanying methods. Scala comes with a wealth of collections classes, but you can go a long way by starting with just a few classes, and using the others as needed. Similarly, each class has dozens of functional methods to make your life easier, but you can achieve a lot by starting with just a handful of them. + +Therefore, this section introduces and demonstrates the most common classes and methods that you’ll need to get started. When you need to understand more classes or more methods, see the Reference documentation for all the details. + + + +## Three main categories of collections classes + +Looking at the Scala collections classes from a high level, there are three main categories to choose from: + +- Sequences +- Maps +- Sets + +A _sequence_ is a linear collection of elements and may be _indexed_ (like an array) or _linear_ (like a linked list). A _map_ contains a collection of key/value pairs, like a Java `Map`, Python dictionary, or Ruby `Hash`. A _set_ is a sequence that contains no duplicate elements. All of those classes have basic types, as well as subsets of those types for specific purposes, such as concurrency, caching, and streaming. + +In addition to these three main categories, there are other useful collection types, including ranges, stacks, and queues. Ranges are demonstrated later in this section. + +The following sections introduce the basic classes you’ll use on a regular basis. + +There are a few other classes that act like collections, including tuples, enumerations, and the `Option`, `Try`, and `Either` families of classes. See the TODO section(s) for more details on these classes. + + + +## Common collections classes + +The main collections classes you’ll use on a regular basis are: + +| Class | Immutable | Mutable | Description | +| ------------- | --------- | ------- | ----------- | +| `List` | ✓ | | A linear (linked list), immutable sequence | +| `Vector` | ✓ | | An indexed, immutable sequence | +| `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 class 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. | +| `Set` | ✓ | ✓ | An iterable collection with no duplicate elements | + +As shown, `Map` and `Set` come in both immutable and mutable versions. + +The basics of each class are demonstrated in the following sections. + +>In Scala, a _buffer_ — such as `ArrayBuffer` and `ListBuffer` — is a sequence that can grow and shrink. + + + +### A note about immutable collections + +In the sections that follow, whenever the word _immutable_ is used, it’s safe to assume that the class is intended for use in a _functional programming_ (FP) style. With these classes you don’t modify the collection; you apply functional methods to the collection to create a new result. + + + +## Choosing a sequence class + +When choosing a _sequence_ class — a sequential collection of elements — you have two main decisions: + +- Should the sequence be indexed (like an array), allowing rapid access to any element, or should it be implemented as a linear linked list? +- Do you want a mutable or immutable collection? + +The recommended, general-purpose, “go to” sequential collections for the combinations of mutable/immutable and indexed/linear are shown here: + +| Type/Category | Immutable | Mutable | +| --------------------- | --------- | ------------ | +| Indexed | `Vector` |`ArrayBuffer` | +| Linear (Linked lists) | `List` |`ListBuffer` | + +For example, if you need an immutable, indexed collection, in general you should use a `Vector`. 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. `ListBuffer` is used when you’re mixing styles, such as building a list + +The next several sections briefly demonstrate the `List`, `Vector`, and `ArrayBuffer` classes. + + + +## The `List` class + +[The List class](https://www.scala-lang.org/api/current/scala/collection/immutable/List.html) is a linear, immutable sequence. This just means that it’s a linked-list that you can’t modify. Any time you want to add or remove `List` elements, you create a new `List` from an existing `List`. + +### Creating Lists + +This is how you create an initial `List`: + +```scala +val ints = List(1, 2, 3) +val names = List("Joel", "Chris", "Ed") +``` + +You can also declare the `List`’s type, if you prefer, though it generally isn’t necessary: + +```scala +val ints: List[Int] = List(1, 2, 3) +val names: List[String] = List("Joel", "Chris", "Ed") +``` + +One exception is when you have mixed types in a collection; in that case you may want to explicitly specify its type: + +```scala +val things: List[Any] = List(1, "two", 3.0) +``` + +### Adding elements to a List + +Because `List` is immutable, you can’t add new elements to it. Instead you create a new list by prepending or appending elements to an existing `List`. For instance, given this `List`: + +```scala +val a = List(1,2,3) +``` + +You _prepend_ one element with `+:`, and multiple elements with `++:`, as shown here: + +```scala +val b = 0 +: a // List(0, 1, 2, 3) +val c = List(-1, 0) ++: a // List(-1, 0, 1, 2, 3) +``` + +You can also _append_ elements to a `List`, but because `List` is a singly-linked list, you should really only prepend elements to it; appending elements to it is a relatively slow operation, especially when you work with large sequences. + +>Tip: If you want to prepend and append elements to an immutable sequence, use `Vector` instead. + +Because `List` is a linked-list class, you shouldn’t try to access the elements of large lists by their index value. For instance, if you have a `List` with one million elements in it, accessing an element like `myList(999_999)` will take a relatively long time, because that request has to traverse all those elements. If you have a large collection and want to access elements by their index, use a `Vector` or `ArrayBuffer` instead. + +### How to remember the method names + +These days IDEs help us out tremendously, but one way to remember those method names is to think that the `:` character represents the side that the sequence is on, so when you use `+:` you know that the list needs to be on the right, like this: + +```scala +0 +: a +``` + +Similarly, when you use `:+` you know the list needs to be on the left: + +```scala +a :+ 4 +``` + +As explained in the Reference documentation, there are more technical ways to think about this, but this can be a helpful way to remember the method names. + +Also, a good thing about these symbolic method names is that they’re consistent. The same method names are used with other immutable sequence classes, such as `Seq` and `Vector`. You can also use non-symbolic method names to append and prepend elements, if you prefer. + +### How to loop over lists + +Given a `List` of names: + +```scala +val names = List("Joel", "Chris", "Ed") +``` + +you can print each string like this: + +```scala +for name <- names do println(name) +``` + +This is what it looks like in the REPL: + +```scala +scala> for name <- names do println(name) +Joel +Chris +Ed +``` + +A great thing about using `for` loops with collections is that Scala is consistent, and the same approach works with all sequence classes, including `Array`, `ArrayBuffer`, `List`, `Seq`, `Vector`, `Map`, `Set`, etc. + +### A little bit of history + +For those interested in a little bit of history, the `List` class is similar to the `List` from [the Lisp programming language](https://en.wikipedia.org/wiki/Lisp_(programming_language)), which was originally specified in 1958. Indeed, in addition to creating a `List` like this: + +```scala +val ints = List(1, 2, 3) +``` + +you can also create the exact same list this way: + +```scala +val list = 1 :: 2 :: 3 :: Nil +``` + +The REPL shows how this works: + +```scala +scala> val list = 1 :: 2 :: 3 :: Nil +list: List[Int] = List(1, 2, 3) +``` + +This works because a `List` is a singly-linked list that ends with the `Nil` element, and `::` is a `List` class method that works like Lisp’s “cons” operator. For more details on lists, see the Reference documentation. + + +### Aside: The LazyList + +The Scala collections also include a [LazyList](https://www.scala-lang.org/api/current/scala/collection/immutable/LazyList.html), which is a _lazy_ immutable linked list. It’s called “lazy” — or non-strict — because it computes its elements only when they are needed. + +You can see how lazy a `LazyList` is in the REPL: + +```scala +val x = LazyList.range(1, Int.MaxValue) +x.take(1) // LazyList() +x.take(5) // LazyList() +x.map(_ + 1) // LazyList() +``` + +In all of those examples, nothing happens. Indeed, nothing will happen until you force it to happen, such as by calling its `foreach` method: + +```` +scala> x.take(1).foreach(println) +1 +```` + +For more information on the uses, benefits, and drawbacks of strict and non-strict (lazy) collections, see the Reference documentation. + + + + + +## The Vector class + +[The Vector class](https://www.scala-lang.org/api/current/scala/collection/immutable/Vector.html) is an indexed, immutable sequence. The “indexed” part of the description means that it provides random access and updates in effectively constant time, so you can access `Vector` elements rapidly by their index value, such as accessing `listOfPeople(123_456_789)`. + +In general, except for the difference that (a) `Vector` is indexed and `List` is not, and (b) the `List` class has the `::` method, the two classes work the same, so we’ll quickly run through the following examples. + +Here are a few ways you can create a `Vector`: + +```scala +val nums = Vector(1, 2, 3, 4, 5) + +val strings = Vector("one", "two") + +case class Person(val name: String) +val people = Vector( + Person("Bert"), + Person("Ernie"), + Person("Grover") +) +``` + +Because `Vector` is immutable, you can’t add new elements to it. Instead you create a new sequence by appending or prepending elements to an existing `Vector`. These examples show how to _append_ elements to a `Vector`: + +```scala +val a = Vector(1,2,3) // Vector(1, 2, 3) +val b = a :+ 4 // Vector(1, 2, 3, 4) +val c = a ++ Vector(4, 5) // Vector(1, 2, 3, 4, 5) +``` + +This is how you _prepend_ elements: + +```scala +val a = Vector(1,2,3) // Vector(1, 2, 3) +val b = 0 +: a // Vector(0, 1, 2, 3) +val c = Vector(-1, 0) ++: a // Vector(-1, 0, 1, 2, 3) +``` + +In addition to fast random access and updates, `Vector` provides fast append and prepend times, so you can use these features as desired. + +>See the Reference documentation for performance details about `Vector` and other collections classes. + +Finally, you use a `Vector` in a `for` loop just like a `List`, `ArrayBuffer`, or any other sequence: + +```scala +scala> val names = Vector("Joel", "Chris", "Ed") +val names: Vector[String] = Vector(Joel, Chris, Ed) + +scala> for name <- names do println(name) +Joel +Chris +Ed +``` + + + +## The ArrayBuffer class + +Use `ArrayBuffer` when you need a general-purpose, mutable indexed sequence in your Scala applications. It’s mutable so you can change its elements, and also resize it. Because it’s indexed, random access of elements is fast. + +### Creating an ArrayBuffer + +To use an `ArrayBuffer`, first import it: + +```scala +import scala.collection.mutable.ArrayBuffer +``` + +If you need to start with an empty `ArrayBuffer`, just specify its type: + +```scala +var strings = ArrayBuffer[String]() +var ints = ArrayBuffer[Int]() +var people = ArrayBuffer[Person]() +``` + +If you know the approximate size your `ArrayBuffer` eventually needs to be, you can create it with an initial size: + +```scala +// ready to hold 100,000 ints +val buf = new ArrayBuffer[Int](100_000) +``` + +To create a new `ArrayBuffer` with initial elements, just specify its initial elements, just like a `List` or `Vector`: + +```scala +val nums = ArrayBuffer(1, 2, 3) +val people = ArrayBuffer( + Person("Bert"), + Person("Ernie"), + Person("Grover") +) +``` + +### Adding elements to an ArrayBuffer + +Append new elements to an `ArrayBuffer` with the `+=` and `++=` methods. Or if you prefer methods with names you can also use `append`, `appendAll`, `insert`, `insertAll`, `prepend`, and `prependAll`. + +Here are some examples of `+=` and `++=`: + +```scala +var 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) +``` + +### Removing elements from an ArrayBuffer + +`ArrayBuffer` is mutable, so it has methods like `-=`, `--=`, `clear`, `remove`, and more. These examples demonstrate the `-=` and `--=` methods: + +```scala +val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g) +a -= 'a' // ArrayBuffer(b, c, d, e, f, g) +a --= Seq('b', 'c') // ArrayBuffer(d, e, f, g) +a --= Set('d', 'e') // ArrayBuffer(f, g) +``` + +### Updating ArrayBuffer elements + +Update elements in an `ArrayBuffer` by either reassigning the desired element, or use the `update` method: + +```scala +val a = ArrayBuffer.range(1,5) // ArrayBuffer(1, 2, 3, 4) +a(2) = 50 // ArrayBuffer(1, 2, 50, 4) +a.update(0, 10) // ArrayBuffer(10, 2, 50, 4) +``` + +See the Reference documentation for more `ArrayBuffer` information and examples. + + + +## Working with Maps + +A `Map` is an iterable sequence that consists of pairs of keys and values. Scala has both mutable and immutable `Map` classes, and this section demonstrates how to use the _immutable_ `Map` class. + +### Creating an immutable Map + +Create an immutable `Map` like this: + +```scala +val states = Map( + "AK" -> "Alaska", + "AL" -> "Alabama", + "AZ" -> "Arizona" +) +``` + +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") +``` + +The REPL shows how this works: + +```` +scala> for ((k,v) <- states) println(s"key: $k, value: $v") +key: AK, value: Alaska +key: AL, value: Alabama +key: AZ, value: Arizona +```` + +### Accessing Map elements + +Access map elements by specifying the desired key value in parentheses: + +```scala +val ak = states("AK") // ak: String = Alaska +val al = states("AL") // al: String = Alabama +``` + +In practice you’ll also use methods like `keys`, `keySet`, `keysIterator`, `for` loops, and higher-order functions like `map` to work with `Map` keys and values. + +### Adding elements to a Map + +Add elements to an immutable map using `+` and `++`, remembering to assign the result to a new variable: + +```scala +val a = Map(1 -> "one") // a: Map(1 -> one) +val b = a + (2 -> "two") // b: Map(1 -> one, 2 -> two) +val c = b + ( + 3 -> "three", + 4 -> "four" +) +// c: Map(1 -> one, 2 -> two, 3 -> three, 4 -> four) +``` + +## Removing elements from a Map + +Remove elements from an immutable map using `-` or `--` and the key values to remove, remembering to assign the result to a new variable: + +```scala +val a = Map( + 1 -> "one", + 2 -> "two", + 3 -> "three", + 4 -> "four" +) + +a - 4 // Map(1 -> one, 2 -> two, 3 -> three) +a - 4 - 3 // 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: + +```scala +val a = Map( + 1 -> "one", + 2 -> "two", + 3 -> "three" +) + +val b = a.updated(3, "THREE!") // Map(1 -> one, 2 -> two, 3 -> THREE!) +``` + +## Traversing a Map + +As shown earlier, this is a common way to manually traverse elements in a map using a `for` loop: + +```scala +val states = Map( + "AK" -> "Alaska", + "AL" -> "Alabama", + "AZ" -> "Arizona" +) + +for ((k,v) <- states) println(s"key: $k, value: $v") +``` + +That being said, there are _many_ ways to work with the keys and values in a map. Common `Map` methods include `foreach`, `map`, `keys`, and `values`. + +Scala has many more specialized `Map` classes available, including `CollisionProofHashMap`, `HashMap`, `LinkedHashMap`, `ListMap`, `SortedMap`, `TreeMap`, `WeakHashMap`, and more. See the Reference documentation for more details on `Map` methods and these specialized subclasses. + + + +## Working with Sets + + +The [Scala Set class]({{site.baseurl}}/overviews/collections-2.13/sets.html) is an iterable collection with no duplicate elements. + +Scala has both mutable and immutable `Set` classes. This section demonstrates the _mutable_ `Set` class. + +### Adding elements to a Set + +The mutable `Set` class isn’t in scope by default, so first you need to import it: + +```scala +val set = scala.collection.mutable.Set[Int]() +``` + +Once in scope, add elements to a mutable `Set` with the `+=`, `++=`, and `add` methods. Here are a few examples: + +```scala +set += 1 // Set(1) +set += 2 += 3 // Set(1, 2, 3) +set ++= Vector(4, 5) // Set(1, 5, 2, 3, 4) +``` + +Notice that if you add a value that’s already in the set, the attempt is quietly ignored: + +```scala +set += 2 // Set(1, 5, 2, 3, 4) (no warning message is shown) +``` + +`Set` also has an `add` method that returns `true` if an element is successfully added to a set, and `false` if it wasn’t added: + +```scala +set.add(6) // true +set.add(5) // false +``` + +## Deleting elements from a Set + +Remove elements from a set using the `-=` and `--=` methods, as shown in the following examples: + +```scala +val set = scala.collection.mutable.Set(1, 2, 3, 4, 5) +// set: mutable.Set[Int] = HashSet(1, 2, 3, 4, 5) + +// remove one element +set -= 1 // HashSet(2, 3, 4, 5) + +// remove multiple elements defined in another sequence +set --= Array(4,5) // HashSet(2, 3) +``` + +There are more methods for working with sets, including `clear` and `remove`, as shown in these examples: + +```scala +// clear +val set1 = scala.collection.mutable.Set(1, 2, 3, 4, 5) +set1.clear() // HashSet() + +// remove +val set2 = scala.collection.mutable.Set(1, 2, 3, 4, 5) +set2.remove(2) // Boolean = true +set2 // HashSet(1, 3, 4, 5) +set2.remove(40) // false +``` + + +## The Range class + +The `Range` class is often used to populate data structures and to iterate over `for` loops. These REPL examples demonstrate how to create ranges: + + +```scala +1 to 5 // Range(1, 2, 3, 4, 5) +1 until 5 // Range(1, 2, 3, 4) +1 to 10 by 2 // Range(1, 3, 5, 7, 9) +'a' to 'c' // NumericRange(a, b, c) +``` + +You can use ranges to populate collections: + +```scala +val x = (1 to 5).toList // List(1, 2, 3, 4, 5) +val x = (1 to 5).toBuffer // ArrayBuffer(1, 2, 3, 4, 5) +``` + +They’re also used in `for` loops: + +```` +scala> for i <- 1 to 3 do println(i) +1 +2 +3 +```` + +There are also `range` methods on collections classes: + +```scala +Vector.range(1,5) // Vector(1, 2, 3, 4) +Vector.range(1,10,2) // Vector(1, 3, 5, 7, 9) +Set.range(1,10) // HashSet(5, 1, 6, 9, 2, 7, 3, 8, 4) +``` + +When you’re running tests, ranges are also useful for generating test collections: + +```scala +val evens = (0 to 10 by 2).toList // List(0, 2, 4, 6, 8, 10) +val odds = (1 to 10 by 2).toList // List(1, 3, 5, 7, 9) +val doubles = (1 to 5).map(_ * 2.0) // Vector(2.0, 4.0, 6.0, 8.0, 10.0) + +// create a Map +val map = (1 to 3).map(e => (e,s"$e")).toMap + // map: Map[Int, String] = Map(1 -> "1", 2 -> "2", 3 -> "3") +``` + + + + +## Common methods on the collections classes + +A great strength of the Scala collections classes is that they come with dozens of pre-built methods, and those methods are consistently available across the immutable and mutable classes. The benefit of this is that you no longer need to write custom `for` loops every time you need to work on a collection. (If that’s not enough of a benefit, it also means that you no longer have to read custom `for` loops.) + +Because there are so many methods available to you, they aren’t all shown here. Instead, only some of the most commonly-used methods will be shown, including: + +- `map` +- `filter` +- `foreach` +- `head` +- `tail` +- `take`, `takeWhile` +- `drop`, `dropWhile` +- `reduce` + +The following methods work on all of the sequence classes, including `Array`, `ArrayBuffer`, `List`, `Vector`, etc., but these examples use a `List` unless otherwise specified. + +### Note: The methods don’t mutate the collection + +As a very important note, none of these methods mutate the collection that they’re called on. They all work in a functional style, meaning that they return a new collection with the modified results. + +### Examples of some common methods + +These examples show some of the most commonly used methods: + +```scala +val a = List(10, 20, 30, 40, 10) // List(10, 20, 30, 40, 10) + +a.distinct // List(10, 20, 30, 40) +a.drop(2) // List(30, 40, 10) +a.dropRight(2) // List(10, 20, 30) +a.dropWhile(_ < 25) // List(30, 40, 10) +a.filter(_ < 25) // List(10, 20, 10) +a.filter(_ > 100) // List() +a.filterNot(_ < 25) // List(30, 40) +a.find(_ > 20) // Some(30) +a.head // 10 +a.headOption // Some(10) +a.init // List(10, 20, 30, 40) +a.intersect(List(19,20,21)) // List(20) +a.last // 10 +a.lastOption // Some(10) +a.slice(2,4) // List(30, 40) +a.tail // List(20, 30, 40, 10) +a.take(3) // List(10, 20, 30) +a.takeRight(2) // List(40, 10) +a.takeWhile(_ < 30) // List(10, 20) +``` + +### Sample lists + +The following examples use these lists: + +```scala +val oneToTen = (1 to 10).toList +val names = List("adam", "brandy", "chris", "david") +``` + +### `map` + +The `map` method steps through each element in the existing list, applying the algorithm you supply to each element, one at a time; it then returns a new list with all of the modified elements. + +Here’s an example of the `map` method being applied to the `oneToTen` list: + +```scala +scala> val doubles = oneToTen.map(_ * 2) +doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) +``` + +You can also write anonymous functions using a long form, like this: + +```scala +scala> val doubles = oneToTen.map(i => i * 2) +doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) +``` + +However, in this lesson we’ll always use the first, shorter form. + +Here are a few more examples of the `map` method being applied to the `oneToTen` and `names` lists: + +```scala +scala> val capNames = names.map(_.capitalize) +capNames: List[String] = List(Adam, Brandy, Chris, David) + +scala> val nameLengthsMap = names.map(s => (s, s.length)).toMap +nameLengthsMap: Map[String, Int] = Map(adam -> 4, brandy -> 6, chris -> 5, david -> 5) + +scala> val isLessThanFive = oneToTen.map(_ < 5) +isLessThanFive: List[Boolean] = List(true, true, true, true, false, false, false, false, false, false) +``` + +As shown in the last two examples, it’s perfectly legal (and common) to use `map` to return a collection that has a different type than the original type. + +### `filter` + +The `filter` method creates a new, filtered list from the given list. Here are a few examples: + +```scala +scala> val lessThanFive = oneToTen.filter(_ < 5) +lessThanFive: List[Int] = List(1, 2, 3, 4) + +scala> val evens = oneToTen.filter(_ % 2 == 0) +evens: List[Int] = List(2, 4, 6, 8, 10) + +scala> val shortNames = names.filter(_.length <= 4) +shortNames: List[String] = List(adam) +``` + +### `foreach` + +The `foreach` method is used to loop over all elements in a collection. Note that `foreach` is used for side-effects, such as printing information. Here’s an example with the `names` list: + +```scala +scala> names.foreach(println) +adam +brandy +chris +david +``` + +A great thing about the functional methods on the collections classes is that you can chain them together to solve problems. For example, this is one way to print the first three elements from `oneToTen`: + +```scala +oneToTen.filter(_ < 4).foreach(println) +``` + +The REPL shows the result: + +```scala +scala> oneToTen.filter(_ < 4).foreach(println) +1 +2 +3 +``` + +### `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: + +```scala +oneToTen.head // Int = 1 +names.head // adam +``` + +Because a `String` is a sequence of characters, you can also treat it like a list. This is how `head` works on these strings: + +```scala +"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: + +```scala +val emptyList = List[Int]() // emptyList: List[Int] = List() +emptyList.head // java.util.NoSuchElementException: head of empty list +``` + +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 +``` + +As shown, it doesn’t throw an exception, it simply returns the type `Option` that has the value `None`. You can learn more about this programming style in the Reference documentation. + +### `tail` + +The `tail` method also comes from Lisp, and it’s used to print every element in a list after the head element. A few examples demonstrate this: + +```scala +oneToTen.head // Int = 1 +oneToTen.tail // List(2, 3, 4, 5, 6, 7, 8, 9, 10) + +names.head // adam +names.tail // List(brandy, chris, david) +``` + +Just like `head`, `tail` also works on strings: + +```scala +"foo".tail // "oo" +"bar".tail // "ar" +``` + +Like `head` and `headOption`, there’s also a `tailOption` method, which is preferred in functional programming. + +### `take`, `takeRight`, `takeWhile` + +The `take`, `takeRight`, and `takeWhile` methods give you a nice way of “taking” the elements from a list that you want to use to create a new list. This is `take` and `takeRight`: + +```scala +oneToTen.take(1) // List(1) +oneToTen.take(2) // List(1, 2) + +oneToTen.takeRight(1) // List(10) +oneToTen.takeRight(2) // List(9, 10) +``` + +And this is `takeWhile`, which works with a predicate function: + +```scala +oneToTen.takeWhile(_ < 5) // List(1, 2, 3, 4) +names.takeWhile(_.length < 5) // List(adam) +``` + +### `drop`, `dropRight`, `dropWhile` + +`drop`, `dropRight`, and `dropWhile` are essentially the opposite of their “take” counterparts, dropping elements from a list. Here are some examples: + +```scala +oneToTen.drop(1) // List(2, 3, 4, 5, 6, 7, 8, 9, 10) +oneToTen.drop(5) // List(6, 7, 8, 9, 10) + +oneToTen.dropRight(8) // List(1, 2) +oneToTen.dropRight(7) // List(1, 2, 3) +``` + +And this is `dropWhile`, which works with a predicate function: + +```scala +oneToTen.dropWhile(_ < 5) // List(5, 6, 7, 8, 9, 10) +names.dropWhile(_ != "chris") // List(chris, david) +``` + +### `reduce` + +When you hear the term, “map reduce,” the “reduce” part refers to methods like `reduce`. It takes a function (or anonymous function) and applies that function to successive elements in the list. + +The best way to explain `reduce` is to create a little helper method you can pass into it. For example, this is an `add` method that adds two integers together, and also provides us some nice debug output: + +```scala +def add(x: Int, y: Int): Int = + val theSum = x + y + println(s"received $x and $y, their sum is $theSum") + theSum +``` + +Given that method and this list: + +```scala +val a = List(1,2,3,4) +``` + +this is what happens when you pass the `add` method into `reduce`: + +```scala +scala> a.reduce(add) +received 1 and 2, their sum is 3 +received 3 and 3, their sum is 6 +received 6 and 4, their sum is 10 +res0: Int = 10 +``` + +As that result shows, `reduce` uses `add` to reduce the list `a` into a single value, in this case, the sum of the integers in the list. + +Once you get used to `reduce`, you’ll write a “sum” algorithm like this: + +```scala +scala> a.reduce(_ + _) +res0: Int = 10 +``` + +Similarly, a “product” algorithm looks like this: + +```scala +scala> a.reduce(_ * _) +res1: Int = 24 +``` + +>An important concept to know about `reduce` is that — as its name implies — it’s used to _reduce_ a collection down to a single value. + + + +## Even more + +There are literally dozens of additional methods on the Scala collections classes that will keep you from ever needing to write another `for` loop. See the Reference documentation for more details and examples. + +>As a final note, if you’re using Java code in a Scala project, you can convert Java collections to Scala collections. By doing this you can use those collections in `for` expressions, and can also take advantage of Scala’s functional collections methods. See the [Integrating with Java](TODO) section for more details. + + + + + + + + + From 4e049594214cdee6045476cc9dffe8330992e8f4 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Tue, 13 Oct 2020 20:18:22 -0600 Subject: [PATCH 020/169] =?UTF-8?q?Initial=20version=20of=20the=20?= =?UTF-8?q?=E2=80=98Scala=20for=20Java=20Developers=E2=80=99=20document.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/scala-for-java-devs.md | 1028 ++++++++++++++++++++ _overviews/overview/scala-forjava-devs.md | 0 2 files changed, 1028 insertions(+) create mode 100644 _overviews/overview/scala-for-java-devs.md delete mode 100644 _overviews/overview/scala-forjava-devs.md diff --git a/_overviews/overview/scala-for-java-devs.md b/_overviews/overview/scala-for-java-devs.md new file mode 100644 index 0000000000..6cc72c93cb --- /dev/null +++ b/_overviews/overview/scala-for-java-devs.md @@ -0,0 +1,1028 @@ + + + +Scala for Java Developers + + +

Notes to reviewers:
+

+
    +
  1. I’m initially submitting this page as HTML because it’s much +easier to work on these side-by-side source code examples with a +Wysiwyg editor (I’m using SeaMonkey). I will convert it to +Markdown when we’re confident with the content in those tables. +If at all possible, I think the side-by-side code examples are +the best approach.
    +
  2. +
  3. I haven’t formatted the multiline source code examples inside +table cells because I think it will be easier to convert them to +Markdown if I don’t do that now. (My initial conversion tests +with Pandoc showed that the conversion will be smoother by not +using <code> tags on those multiline +examples.)
    +
  4. +
  5. This page will eventually link to many other pages. So if you +wonder why I don’t explain certain things in more detail, it’s +because (a) I’m trying to keep this page as short as possible +and (b) I assume this page will eventually link to dozens of +other pages for more details.
  6. +
+
+


+

+

This page provides a comparison between the Java and Scala +programming languages. It’s intended for programmers who know Java +and want to learn about Scala, specifically by seeing how Java +language features compare to Scala.

+

Executive overview (TL;DR)
+

+

This section provides a relatively brief introduction and summary +of the sections that follow. It presents the similarities and +differences between Java and Scala at a high level, and then +introduces the differences you’ll experience every day as you +write code.

+

High level similarities
+

+

At a high level, Scala shares these similarities with Java:
+

+
    +
  • Scala code is compiled to .class files, packaged in +JAR files, and runs on the JVM
  • +
  • It’s an object-oriented programming (OOP) language
  • +
  • It’s statically typed
  • +
  • Both languages have support for immutable collections, +lambdas, and higher-order functions
    +
  • +
  • It has great IDE support
  • +
  • It has great build tools
  • +
  • It has terrific libraries and frameworks for building +server-side, network-intensive applications, including web +server applications, microservices, machine learning, and more
  • +
  • Both Java and Scala can use the Akka actors library to build +actor-based concurrent systems, and Apache Spark to build +data-intensive applications
    +
  • +
  • You can use GraalVM to compile your projects into native +executables
  • +
  • Scala can seamlessly use the wealth of libraries that have +been developed for Java
  • +
+

High level differences
+

+

Also at a high level, the differences between Java and Scala are:
+

+
    +
  • Scala has a concise but readable syntax; we call it expressive
    +
  • +
  • Though it’s statically typed, Scala feels like a dynamic +language
  • +
  • Scala is a pure OOP language, so every object is an instance +of a class, and symbols like + and += +that look like operators are really methods; this means that you +can create your own operators
    +
  • +
  • In addition to being a pure OOP language, Scala is also a pure +FP language; in fact, it encourages a fusion of OOP and FP, with +functions for the logic and objects for modularity
    +
  • +
  • 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
    +
  • +
  • In addition to running on the JVM, the Scala.js project lets you +use Scala as a JavaScript replacement
  • +
  • The Scala + +Native project adds low-level constructs to let you write +“systems” level code, and also compiles to native executables
  • +
  • Sound type system (TODO: need a good, simple way to state +this)
    +
  • +
+

Programming level differences
+

+

At a lower level, these are some of the differences you’ll see +every day when writing code:
+

+
    +
  • Scala’s syntax is extremely consistent (TODO: need a good way +to state this)
  • +
  • Variables and parameters are defined as val +(immutable, like final in Java) or var +(mutable)
    +
  • +
  • Type inference makes your code feel dynamic and helps to keep +it as brief as you want it
    +
  • +
  • In addition to simple for loops, Scala has +powerful for comprehensions that yield results +based on your algorithms
    +
  • +
  • Pattern matching and match expressions will +change the way you write code
    +
  • +
  • Scala’s type system lets you express details (TODO: need a +good way to state this)
  • +
  • Writing immutable code by default leads to writing expressions +rather than statements; in time you’ll see that writing +expressions simplifies your code (and your tests)
    +
  • +
  • Toplevel definitions let you put method, field, and +other definitions anywhere, also leading to concise, expressive +code
    +
  • +
  • You can create mixins by “mixing in” multiple traits +(traits are similar to interfaces in Java 8 and newer)
    +
  • +
  • Classes are closed by default, supporting Joshua Bloch’s Effective + +Java idiom, “Design and document for inheritance or else +forbid it”
    +
  • +
  • Scala’s contextual abstractions and term inference +provide a collection of features:
    +
  • +
      +
    • Extension methods let you add new functionality to +closed classes
    • +
    • Given instances let you define terms that the +compiler can synthesize at using points, making your +code less verbose and essentially letting the compiler write +code for you
      +
    • +
    • Multiversal equality lets you limit equality comparisons — +at compile time — to only those comparisons that make sense
      +
    • +
    +
  • First class support for building modules
    +
  • +
  • Scala has state of the art, third-party, open source +functional programming libraries
  • +
  • Case classes are like records in Java 14; they help you model +data when writing FP code, with built-in support for concepts +like pattern matching and cloning
    +
  • +
  • Thanks to features like by-name parameters, infix notation, +optional parentheses, extension methods, and higher-order +functions, you can create your own “control structures” and DSLs
    +
  • +
  • Scala files do not have to be named according to the classes +or traits they contain
  • +
  • Many other goodies: companion classes and objects, macros, +union and intersection types, toplevel definitions, numeric +literals, multiple parameter lists, default values for +parameters, named arguments, and more
    +
  • +
+

Features compared with examples

+

Given that introduction, the following sections provide +side-by-side comparisons of Java and Scala programming language +features.

+

OOP style classes and methods

+

This section provides comparisons of features related to +OOP-style classes and methods.
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Java
+
Scala
+
Comments
+(the same in Java and Scala)
+
+
//
+/* ... */
+/** ... */
//
+/* ... */
+/** ... */
OOP style class,
+primary constructor
+
class Person {
+  private String firstName;
+  private String lastName;
+  private int age;
+  public Person(String firstName, String lastName, int +age) {
+    this.firstName = firstName;
+    this.lastName = lastName;
+    this.age = age;
+  }
+  override String toString() {
+    return String.format("%s %s is %d years +old.", firstName, lastName, age);
+  }
+}
class Person (
+  var firstName: String,
+  var lastName: String,
+  var age: Int
+):  
+    override def toString = s"$firstName +$lastName is $age years old."
+
+
Auxiliary constructors
+
public class Person {
+  private String firstName;
+  private String lastName;
+  private int age;
+
+  // primary constructor
+  public Person(String firstName, String lastName, int +age) {
+    this.firstName = firstName;
+    this.lastName = lastName;
+    this.age = age;
+  }
+
+  // zero-arg constructor
+  public Person(String firstName, String lastName, int +age) {
+    this("", "", 0);
+  }
+
+  // one-arg constructor
+  public Person(String firstName) {
+    this(firstName, "", 0);
+  }
+
+  // two-arg constructor
+  public Person(String firstName, String lastName) {
+    this(firstName, lastName, 0);
+  }
+
+}
+
class Person (
+  var firstName: String,
+  var lastName: String,
+  var age: Int
+):
+    // zero-arg auxiliary constructor
+    def this() = this("", "", 0)
+
+    // one-arg auxiliary constructor
+    def this(firstName: String) = +this(firstName, "", 0)
+
+    // two-arg auxiliary constructor
+    def this(firstName: String, lastName: +String) =
+      this(firstName, lastName, 0)
+
+end Person
+
Classes closed by default (“Plan for +inheritance or else forbid it”)
+
final class Person
+
class Person
+
Create a class that’s open for extension
+
class Person
+
open class Person
+
Method, one line
+
public int add(int a, int b) {
+  return a + b;
+}
+
def add(a: Int, b: Int): Int = a + b
+
Method, multiple lines
+
public void walkThenRun() {
+  System.out.println("walk");
+  System.out.println("run");
+}
+
def walkThenRun() =
+  println("walk")
+  println("run")
+
Immutable field
+
final int i = 1;
+
val i = 1
+
Mutable field
+
int i = 1;
+var i = 1;
+
var i = 1
+
+


+

+

Interfaces, traits, and inheritance

+

This section compares Java interfaces to Scala traits, including +how classes extend interfaces and traits.
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Java
+
Scala
+
Interfaces/Traits
+
public interface Marker;
+
trait Marker
+
Simple interface
+
public interface Adder {
+  public int add(int a, int b);
+}
+
trait Adder:
+  def add(a: Int, b: Int): Int
+
Interface with a concrete method
+
public interface Adder {
+  int add(int a, int b);
+  default int multiply(int a, int b) {
+    return a * b;
+  }
+}
trait Adder:
+  def add(a: Int, b: Int): Int
+  def multiply(a: Int, b: Int): Int = a * b
+
Inheritance
+
class Dog extends +Animal,HasLegs,HasTail class Dog extends +Animal,HasLegs,HasTail
+
Extending multiple interfaces/traits
+that have implemented methods
+(default methods)
+
interface Adder {
+  default int add(int a, int b) {
+    return a + b;
+  }
+}
+interface Multiplier {
+  default int multiply(int a, int b) {
+    return a * b;
+  }
+}
+public class JavaMath implements Adder, Multiplier {}
+
+JavaMath jm = new JavaMath();
+jm.add(1,1);
+jm.multiply(2,2);
+
trait Adder:
+  def add(a: Int, b: Int) = a + b
+
+trait Multiplier:
+  def multiply(a: Int, b: Int) = a * b
+
+class ScalaMath extends Adder, Multiplier
+
+val sm = new ScalaMath
+sm.add(1,1)
+sm.multiply(2,2)
+
Mixin
+
N/A
+
class DavidBanner
+trait Angry:
+  def beAngry() = println("You won’t like me ...")
+trait Big:
+  println("I’m big")
+trait Green:
+  println("I’m green")
+
+// mix in the traits as DavidBanner is created
+val hulk = new DavidBanner with Big with Angry with Green
+
+

+


+

+

Control structures

+

+

This section compares control structures in Java and Scala.
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Java
+
Scala
+
if statement,
+one line
+
if (x == 1) { System.out.println(1); }
+
if x == 1 then println(x)
+
if statement,
+multiline
+
if (x == 1) {
+  System.out.println("x is 1, as you can see:")
+  System.out.println(x)
+}
if x == 1 then
+  println("x is 1, as you can see:")
+  println(x)
+
if/else if/else
+
if (x < 0) {
+  System.out.println("negative")
+} else if (x == 0) {
+  System.out.println("zero")
+} else {
+  System.out.println("positive")
+}
+
if x < 0 then
+  println("negative")
+else if x == 0
+  println("zero")
+else
+  println("positive")
+
if as method body
+
public int min(int a, int b) {
+  return (a < b) ? a : b;
+}
+
def min(a: Int, b: Int): Int =
+  if a < b then a else b
+
Returning a value from if
+
int minVal = (a < b) ? a : b;val minValue = if a < b then a else +b
+
while
+
while (i < 3) {
+  System.out.println(i);
+  i++;
+}
+
while i < 3 do
+  println(i)
+  i += 1
+
for loop,
+single line
+
for (int i: ints) {
+  System.out.println(i);
+}
+
for i <- ints do println(i)   // +preferred
+for (i <- ints) println(i)   // also available
+
for loop,
+multiple lines
for (int i: ints) {
+  int x = i * 2;
+  System.out.println(x);
+}
for
+  i <- ints
+do
+  val x = i * 2
+  println(s"i = $i, x = $x")
+
Multiple generators
+
for (int i: ints1) {
+  for (int j: chars) {
+    for (int k: ints2) {
+      System.out.printf("i = %d, j += %d, k = %d\n", i,j,k);
+    }
+  }
+}
+
for
+  i <- 1 to 2
+  j <- 'a' to 'b'
+  k <- 1 to 10 by 5
+do
+  println(s"i = $i, j = $j, k = $k")
+
Generator with guards (if +expressions)
+
List ints = ArrayList(1,2,3,4,5,6,7,8,9,10);
+for (int i: ints) {
+  if (i % 2 == 0 && i < 5) {
+    System.out.println(x);
+  }
+}
for
+  i <- 1 to 10
+  if i % 2 == 0
+  if i < 5
+do
+  println(i)
+
for comprehension
+
N/A
+
val list =
+  for
+    i <- 1 to 3
+  yield
+    i * 10
+// result: Vector(10, 20, 30)
+
switch/match
+
String monthAsString = "";
+switch(day) {
+  case 1: monthAsString = "January";
+          +break;
+  case 2: monthAsString = "February";
+          +break;
+  default: monthAsString = "Other";
+          +break;
+}
+
val monthAsString = day match {
+  case 1 => "January"
+  case 2 => "February"
+  _ => "Other"
+}
+
switch/match: +handling
+multiple conditions
+per case
+
String numAsString = "";
+switch (i) {
+  case 1: case 3:
+  case 5: case 7: case 9:
+    numAsString = "odd";
+    break;
+  case 2: case 4:
+  case 6: case 8: case 10:
+    numAsString = "even";
+    break;
+  default:
+    numAsString = "too big";
+    break;
+}
+
val numAsString = i match {
+  case 1 | 3 | 5 | 7 | 9 => println("odd")
+  case 2 | 4 | 6 | 8 | 10 => println("even")
+  case _ => println("too big")
+}
+
try/catch/finally
+
try {
+  writeTextToFile(text);
+} catch (IOException ioe) {
+  println("Got an IOException.")
+} catch (NumberFormatException nfe) {
+  println("Got an NumberFormatException.")
+} finally {
+  println("Clean up your resources here.")
+}
+
try
+  writeTextToFile(text)
+catch
+  case ioe: IOException => println("Got an +IOException.")
+  case nfe: NumberFormatException => println("Got a +NumberFormatException.")
+finally
+  println("Clean up your resources here.")
+
+


+

+

Collections classes

+

This section compares the collections classes that are available +in Java and Scala.

+

Immutable collections classes

+

Examples of how to create instances of immutable collections:
+

+ + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Java
+
Scala
+
Sequences
+
List strings = List.of("a", "b", "c");
+
val strings = List("a", "b", "c")
+val strings = Vector("a", "b", "c")
+
Set
+
Set set = Set.of("a", "b", "c"); +val set = Set("a", "b", "c")
+
Map
+
Map map = Map.of("a", 1, "b", 2, "c", +3); val map = Map("a" -> 1, "b" -> 2, +"c" -> 3)
+
+


+

+

Mutable collections classes

+

The table below shows which Java collections classes can be +converted to Scala collections classes with the Scala CollectionConverters +objects. There are two objects in different packages, one for +converting from Java to Scala, and another for converting from +Scala to Java. Here are the conversions:
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Java
+
Scala
+
java.util.Collection
+
scala.collection.Iterable
java.util.List
+
scala.collection.mutable.Buffer
java.util.Set
+
scala.collection.mutable.Set
+
java.util.Map
+
scala.collection.mutable.Map
+
java.util.concurrent.ConcurrentMap + +scala.collection.mutable.ConcurrentMap
+
java.util.Dictionary
+
scala.collection.mutable.Map
+


+

+

Methods on collections classes

+

With the ability to treat Java collections as streams, Java and +Scala now have many of the same common functional methods +available to them:

+
    +
  • map
  • +
  • filter
  • +
  • forEach/foreach
  • +
  • findFirst/find
  • +
  • reduce
    +
  • +
+

If you’re used to using these methods with lambda expressions in +Java, you’ll find it easy to use the same methods on Scala’s +collection classes.
+

+

Tuples
+

+

Java tuples are created like this:
+

+
Pair<String, Integer> pair = new Pair<String, Integer>("Eleven", 11);
Triplet<String, Integer, Double> triplet = Triplet.with("Eleven", 11, 11.0);
Quartet<String, Integer, Double,Person> triplet = Triplet.with("Eleven", 11, 11.0, new Person("Eleven"));
+

Other Java tuple names are Quintet, Sextet, Septet, Octet, +Ennead, Decade.

+

Tuples in Scala are created by putting the values inside +parentheses, like this:
+

+
val a = ("eleven")
val b = ("eleven", 11)
val c = ("eleven", 11, 11.0)
val d = ("eleven", 11, 11.0, Person("Eleven"))
+


+

+

Enums

+

This section compares enums (enumerations) in Java and Scala.
+

+ + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Java
+
Scala
+
Basic enum
+
enum Color {
+  RED, GREEN, BLUE
+}
+
enum Color:
+  case Red, Green, Blue
+
Parameterized enum
+
enum Color {
+  Red(0xFF0000),
+  Green(0x00FF00),
+  Blue(0x0000FF);
+
+  private int rgb;
+
+  Color(int rgb) {
+    this.rgb = rgb;
+  }
+}
+
enum Color(val rgb: Int):
+  case Red   extends Color(0xFF0000)
+  case Green extends Color(0x00FF00)
+  case Blue  extends Color(0x0000FF)
+
User-defined enum members
+
enum Planet {
+  MERCURY (3.303e+23, 2.4397e6),
+  VENUS   (4.869e+24, 6.0518e6),
+  EARTH   (5.976e+24, 6.37814e6);
+  // more planets ...
+
+  private final double mass;
+  private final double radius;
+
+  Planet(double mass, double radius) {
+    this.mass = mass;
+    this.radius = radius;
+  }
+
+  public static final double G = 6.67300E-11;
+
+  private double mass() { return mass; }
+  private double radius() { return radius; }
+
+  double surfaceGravity() {
+    return G * mass / (radius * radius);
+  }
+  double surfaceWeight(double otherMass) {
+    return otherMass * surfaceGravity();
+  }
+
+}
+
+
enum Planet(mass: Double, radius: Double):
+  case Mercury extends Planet(3.303e+23, 2.4397e6)
+  case Venus   extends Planet(4.869e+24, +6.0518e6)
+  case Earth   extends Planet(5.976e+24, +6.37814e6)
+  // more planets ...
+
+  private final val G = 6.67300E-11
+  def surfaceGravity = G * mass / (radius * radius)
+  def surfaceWeight(otherMass: Double) =  +otherMass * surfaceGravity
+
+
+


+

+

Exceptions and error handling

+

Java uses checked exceptions

+

Java uses checked exceptions, so in Java code you’ll see try/catch/finally +blocks, and throws clauses on methods:

+
public int makeInt(String s) throws NumberFormatException {
  // code here to convert a String to an int
}
+

Scala does not use checked exceptions
+

+

The Scala idiom is to not use checked exceptions like +this. When working with code that can throw exceptions, you can +use try/catch/finally +blocks to catch exceptions from code that throws them, but how you +proceed from there is different.

+

The best way to explain this is that Scala code consists of expressions, +which return values. As a result, you end up writing your code as +a series of algebraic expressions:

+
val a = f(x)
val b = g(a,z)
val c = h(b,y)
+

This is nice, it’s just algebra, and a combination of equations. +As you may remember from high school algebra, algebraic +expressions don’t short circuit — they don’t throw exceptions that +blow up the series of equations.

+

Therefore, in Scala our methods don’t throw exceptions. Instead, +they return types like Option. For example, this makeInt +method catches a possible exception and returns an Option +value:
+

+
def makeInt(s: String): Option[Int] =
+try
+Some(s.toInt)
+catch
+case e: NumberFormatException => None
+
+

The Scala Option is similar to the Java Optional +class. As shown, if the string-to-int conversion succeeds, the Int +is returned inside a Some value, and if it fails, a +None value is returned. Some and None +are subtypes of Option, so the method is declared to +return the Option[Int] type.

+

When you have an Option value, such as the one +returned by makeInt, there are many ways to work +with it, depending on your needs. This code shows one possible +approach:
+

+
makeInt(aString) match
  case Some(i) => println(s"i = $i")
  case None => println(s"Could not convert $aString to an Int.")
+

Option is commonly used in Scala, and it’s built +into many classes in the standard library.
+

+

For more information on dealing with errors and exceptions in +Scala, see the Functional Error Handling section in the Reference +documentation.

+

Concepts that are unique to Scala
+

+

There are other concepts in Scala which currently have no equal +in Java 11:

+
    +
  • Everything related to contextual abstractions
  • +
  • Method features:
  • +
      +
    • Multiple parameter lists
    • +
    • Default parameter values
    • +
    • Using named arguments when calling methods
      +
    • +
    +
  • Case classes (like “records” in Java 14) and case objects
  • +
  • Companion classes and objects
  • +
  • The ability to create your own control structures and DSLs
  • +
  • Toplevel definitions
  • +
  • Pattern matching
  • +
  • Advanced features of match expressions
  • +
  • Type lambdas
  • +
  • Trait parameters
  • +
  • Opaque type aliases
  • +
  • Multiversal equality
  • +
  • Type classes
  • +
  • Infix methods
  • +
  • Macros and metaprogramming
  • +
  • More ...
    +
  • +
+
+


+

+

+

+

+


+

+ + + + diff --git a/_overviews/overview/scala-forjava-devs.md b/_overviews/overview/scala-forjava-devs.md deleted file mode 100644 index e69de29bb2..0000000000 From f0940acb93b2e33a91ab7001cae3045b9321d0fd Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Wed, 21 Oct 2020 09:57:11 -0600 Subject: [PATCH 021/169] =?UTF-8?q?Initial=20versions=20of=20the=20?= =?UTF-8?q?=E2=80=98Methods=E2=80=99=20and=20=E2=80=98Scala=20for=20Python?= =?UTF-8?q?=20Developers=E2=80=99=20documents.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/methods-and-functions.md | 380 ++++++ _overviews/overview/scala-for-python-devs.md | 1094 ++++++++++++++++++ 2 files changed, 1474 insertions(+) diff --git a/_overviews/overview/methods-and-functions.md b/_overviews/overview/methods-and-functions.md index e69de29bb2..86688f6461 100644 --- a/_overviews/overview/methods-and-functions.md +++ b/_overviews/overview/methods-and-functions.md @@ -0,0 +1,380 @@ +--- +title: Scala 3 Methods +description: This section introduces methods and functions in Scala 3. +--- + + + +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. In short, methods can now be defined anywhere. + +This section demonstrates many of the basic features of defining and calling methods. + + + +## Defining methods + +Scala’s general method syntax looks like this: + +```scala +def methodName(param1: Type1 = defaultValue1, param2: Type2 = defaultValue2): ReturnType = + // the method body + // goes here +end methodName // this is optional +``` + +In that syntax: + +- The keyword `def` is used to define a method +- The Scala standard is to name methods using the camel case convention +- Method parameters are always defined with an accompanying type +- Parameters can also have default values +- Declaring the method return type is optional +- Methods can consist of many lines, or just one line +- Providing the `end methodName` portion after the method body is also optional, and is only recommended for long methods + +Here are two examples of a one-line method named `add` that takes two `Int` input parameters. The first version explicitly shows the method’s `Int` return type, and the second does not: + +```scala +def add(a: Int, b: Int): Int = a + b +def add(a: Int, b: Int) = a + b +``` + +Because Scala is an expressive language, a surprising number of methods end up being only one line. + + + +## Calling methods + +Invoking a method is straightforward: + +```scala +val x = add(1, 2) // 3 +``` + +The Scala collections classes have dozens of built-in methods. Here are a few examples of how to call their methods: + +```scala +val x = List(1,2,3) + +x.size // 3 +x.contains(1) // true +x.map(_ * 10) // List(10, 20, 30) +``` + +Notice: + +- `size` takes no arguments, and returns the number of elements in the list +- The `contains` method takes one argument, the value to search for +- `map` takes one argument, a function; in this case an anonymous function is passed into it + + + +## Multiline methods + +When a method is longer than one line, start the method body on the second line, indented to the right: + +```scala +def addThenDouble(a: Int, b: Int): Int = + // imagine that this takes multiple lines + val sum = a + b + sum * 2 +``` + +In that method: + +- `sum` is an immutable local variable; it can’t be accessed outside of the method +- The last line doubles the value of `sum`; this result is the value that’s yielded by the method + +When you paste that code into the REPL, you’ll see that it works as desired: + +```scala +scala> addThenDouble(1, 1) +res0: Int = 4 +``` + +Notice in this example that there’s no need for a `return` statement at the end of the method. Because everything in Scala is an _expression_ — meaning that each line of code returns (or _evaluates to) a value — there’s no need to add `return` to an expression. + +This becomes more clear when you condense that method and write it on one line: + +```scala +def addThenDouble(a: Int, b: Int): Int = (a + b) * 2 +``` + +Methods can contain any lines of code you desire: + +- `if`/`else` expressions +- `match` expressions +- `while` loops +- `for` loops and `for` expressions +- Variable assignments +- Calls to other methods + +As an example of a real-world multiline method, this `getStackTraceAsString` method converts its `Throwable` input parameter into a well-formatted `String`: + +```scala +def getStackTraceAsString(t: Throwable): String = + val sw = new StringWriter + t.printStackTrace(new PrintWriter(sw)) + sw.toString +``` + +In that method: + +- The first line is a variable-assignment expression +- The second line is a statement; it puts the stack trace content into the `StringWriter` +- The third line yields the `String` representation of the stack trace + +>Lines of code that don’t return values are called _statements_, and they’re used for their side-effects. Conversely, _expressions_ always return a result and generally do not have side effects. As you learn more about Scala you’ll find yourself writing more expressions and fewer statements. + + +## Default parameter values + +Method parameters can have default values. In this example, default values are given for both the `timeout` and `protocol` parameters: + +```scala +def makeConnection(timeout: Int = 5_000, protocol: String = "http") = + println(f"timeout = ${timeout}%d, protocol = ${protocol}%s") + // more code here ... +``` + +Because the parameters have default values, the method can be called in these ways: + +```scala +makeConnection() // timeout = 5000, protocol = http +makeConnection(2_000) // timeout = 2000, protocol = http +makeConnection(3_000, "https") // timeout = 3000, protocol = https +``` + +Here are a few key points about those examples: + +- In the first example no arguments are provided, so the method uses the default parameter values of `5_000` and `http` +- In the second example, `2_000` is supplied for the `timeout` value, so it’s used, along with the default value for the `protocol` +- In the third example, values are provided for both parameters, so they’re both used + +Notice that by using default parameter values, it appears to the consumer that they can use three different overridden methods. + + + +## Using named parameters + +If you prefer, you can also use the names of the method parameters when calling a method. For instance, `makeConnection` can also be called in these ways: + +```scala +makeConnection(timeout=10_000) +makeConnection(protocol="https") +makeConnection(timeout=10_000, protocol="https") +makeConnection(protocol="https", timeout=10_000) +``` + +In some frameworks named parameters heavily used. They’re also very useful when multiple method parameters have the same type: + +```scala +engage(true, true, true, false) +``` + +Without help from an IDE that code can be hard to read, but this code is much more clear and obvious: + +```scala +engage( + speedIsSet = true, + directionIsSet = true, + picardSaidMakeItSo = true, + turnedOffParkingBrake = false +) +``` + + + +## A suggestion about methods that take no parameters + +When a method takes no parameters, it’s said to have an _arity_ level of _arity-0_. 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 + +For example, this method has a side effect, so it’s declared with empty parentheses: + +```scala +def speak() = println("hi") +``` + +Doing this requires callers of the method to use open parentheses when calling the method: + +```scala +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. + + + +## Using an `if` expression as a method body + +Because `if`/`else` expressions return a value, they can be used as the body of a method. Here’s a method named `isTrue` that implements the Perl definitions of `true` and `false`: + +```scala +def isTrue(a: Any) = + if a == 0 || a == "" + false + else + true +``` + +These examples show how that method works: + +```scala +isTrue(0) // false +isTrue("") // false +isTrue("hi") // true +isTrue(1.0) // true +``` + + +## Using a `match` expression as a method body + +A `match` expression can also be used as the entire method body, and often is. Here’s another version of `isTrue`, written with a `match` expression : + +```scala +def isTrue(a: Any) = a match + case 0 | "" => false + case _ => true +``` + +This method works just like the previous method that used an `if`/`else` expression. + + + +## Controlling method scope in classes + +In classes, objects, and traits, Scala methods are public by default, so the `Dog` instance created here can access the `speak` method: + +```scala +class Dog: + def speak() = println("Woof") + +val d = new Dog +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: + +```scala +class Animal: + private def breathe() = println("I’m breathing") + +class Cat extends Animal: + // this method won’t compile + 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: + +```scala +class Animal: + private def breathe() = println("I’m breathing") + def walk() = + breathe() + println("I’m walking") + protected def speak() = println("Hello?") + +class Cat extends Animal: + override def speak() = println("Meow") + +val cat = new Cat +cat.walk() +cat.speak() +cat.breathe() // won’t compile because it’s private +``` + +The `protected` setting means: + +- The method (or field) can be accessed by other instances of the same class +- It is not visible by other code in the current package +- It is available to subclasses + +For more details on controlling the scope of methods in classes, traits, and objects, see the Reference documentation. + + + +## Objects can contain methods + +Earlier you saw that traits and classes can have methods. The Scala `object` keyword is used to create a singleton class, and an object can also contain methods. This is a nice way to create a set of “utility” methods. For instance, this object contains a collection of methods that work on strings: + +```scala +object StringUtils: + + /** + * Returns a string that is the same as the input string, but + * truncated to the specified length. + */ + def truncate(s: String, length: Int): String = s.take(length) + + /** + * Returns true if the string contains only letters and numbers. + */ + def lettersAndNumbersOnly_?(s: String): Boolean = s.matches("[a-zA-Z0-9]+") + + /** + * Returns true if the given string contains any whitespace at all. + * Assumes that `s` is not null. + */ + def containsWhitespace(s: String): Boolean = s.matches(".*\\s.*") + +end StringUtils +``` + + + +## Extension methods + +Extension methods let you add methods to a type after the type is defined. This example shows how to create an extension method named `circumference` on a previously-defined `Circle` class: + +```scala +case class Circle(x: Double, y: Double, radius: Double) + +extension (c: Circle) + def circumference: Double = c.radius * math.Pi * 2 +``` + +In this example, you may have written the `Circle` class, or it might be a part of some third-party library. The important part is that this approach adds the `circumference` method to the `Circle` class, so it can be used just as though it was defined inside `Circle`: + +```scala +val circle = Circle(0, 0, 1) +circle.circumference +``` + +The `extension` keyword declares that you’re about to define one or more extension methods on the type that’s put in parentheses. As shown with this example, the parameter `c` can then be used in the body of your extension methods. + +Here’s another example that shows how to define methods with symbolic names — i.e., what appears to be an operator on a `String`: + +```scala +extension (x: String) + def < (y: String): Boolean = x.length < y.length + +"ab" < "c" // false +``` + +In this example the `<` definition overrides the default `<` on the `String` class. + + + +## Even more + +There’s even more to know about methods, including how to: + +- Call methods on superclasses +- Define and use by-name parameters +- Write a method that takes a function parameter +- Create inline methods +- Handle exceptions +- Use vararg input parameters +- Write methods that have multiple parameter groups (partially-applied functions) +- Create methods that have generic type parameters + +See the Reference documentation for more details on these features. + + + + + diff --git a/_overviews/overview/scala-for-python-devs.md b/_overviews/overview/scala-for-python-devs.md index e69de29bb2..2f68168bc8 100644 --- a/_overviews/overview/scala-for-python-devs.md +++ b/_overviews/overview/scala-for-python-devs.md @@ -0,0 +1,1094 @@ +--- +title: Scala for Python Developers +description: This page is for Python developers who are interested in learning about Scala 3. +--- + +

Notes to reviewers:

+
    +
  1. This page will eventually link to many other pages. So if you +wonder why I don’t explain certain things in more detail, it’s +because (a) I’m trying to keep this page short, and (b) I assume +this page will eventually link to dozens of other pages for more +details.
  2. +
  3. I’m initially submitting this page as HTML because it’s much +easier to work on these side-by-side source code examples with a +Wysiwyg editor (I’m using SeaMonkey). I’ll convert it to +Markdown when we’re confident with the content in those tables. +If at all possible, I think the side-by-side code examples are +the best approach.
  4. +
  5. I haven’t formatted the multiline source code examples inside +table cells because I think it will be easier to convert them to +Markdown if I don’t do that now. (My initial conversion tests +with Pandoc showed that the conversion will be smoother by not +using <code> tags on those multiline +examples.)
  6. +
+
+


+

+

This section provides a comparison between the Python and Scala +programming languages. It’s intended for programmers who know +Python and want to learn about Scala, specifically by seeing how +Python language features compare to Scala.

+

Introduction
+

+

This section provides a summary of the similarities and +differences between Python and Scala. The two languages are first +compared at a high level, and then at an everyday programming +level.

+

High level similarities
+

+

At a high level, Scala shares these similarities with +Python:
+

+
    +
  • Both are high-level programming languages, where you don’t +have to concern yourself with low-level concepts like pointers +and manual memory management
  • +
  • Both have a relatively simple, concise syntax
    +
  • +
  • Both are object-oriented programming (OOP) languages
  • +
  • Both have comprehensions: Python has list comprehensions and +Scala has for comprehensions
  • +
  • Both languages have support for lambdas and higher-order +functions
  • +
  • Both can be used with Apache Spark for big data processing
  • +
  • Both have a wealth of terrific libraries
  • +
+

High level differences
+

+

Also at a high level, the differences between Python and +Scala are:
+

+
    +
  • Python is dynamically typed, and Scala is statically typed
    +
  • +
      +
    • Though it’s statically typed, type inference makes Scala +feel like a dynamic language
    • +
    +
  • In addition to being an OOP language, Scala is also a pure FP +language
  • +
  • Python is interpreted, and Scala code is compiled to .class +files, and runs on the Java Virtual Machine (JVM)
  • +
  • In addition to running on the JVM, the Scala.js project lets you +use Scala as a JavaScript replacement
  • +
  • The Scala +Native project adds low-level constructs to let you write +“systems” level code, and compiles to native executables
  • +
  • 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 variables and immutable collections
  • +
  • Scala has excellent support for concurrent and parallel +programming
    +
  • +
+

Programming level similarities

+

This section looks at the similarities you’ll see between Python +and Scala when you write code on an every day basis:
+

+
    +
  • Scala’s type inference often makes it feel like a dynamically +typed language
    +
  • +
  • Neither language uses semi-colons to end expressions
    +
  • +
  • Both languages support the use of significant indentation +rather than braces and parentheses
  • +
  • The syntax for defining methods is similar
  • +
  • Both have lists, dictionaries (maps), sets, and tuples
  • +
  • Both have comprehensions for mapping and filtering
  • +
  • Both have higher-order functions and strong support for +lambdas
  • +
+

Programming level differences

+

Also at a programming level, these are some of the differences +you’ll see every day when writing code:
+

+
    +
  • Scala’s syntax is extremely consistent
  • +
      +
    • Lists, maps, sets, and tuples are all created and accessed +similarly; they have most of the same higher-order functions; +val and var fields are used +consistently +when defining fields and parameters; pattern matching is used +consistently; more
      +
    • +
    +
  • Scala variables and parameters are defined with the val +(immutable) or var (mutable) keywords
    +
  • +
  • Scala idioms prefer immutable data structures
    +
  • +
  • Scala has terrific IDE support
  • +
  • Comments: Python uses #, Scala uses the C, C++, +and +Java style: //, /*...*/, and /**...*/
    +
  • +
  • Naming conventions: Python is my_list, Scala is +myList
    +
  • +
  • Scala is statically typed, so you declare types for method +parameters, method return values, and in other places
    +
  • +
  • Pattern matching and match expressions are used +extensively in Scala, and will change the way you write code
  • +
  • Toplevel definitions let you put method, field, and +other definitions anywhere, also leading to concise, expressive +code
    +
  • +
  • Traits are used heavily in Scala; interfaces and abstract +classes are used less often in Python
    +
  • +
  • Scala’s contextual abstractions and term inference +provide a collection of different features:
    +
  • +
      +
    • Extension methods let you add new functionality to +closed classes
    • +
    • Given instances let you define terms that the +compiler can synthesize at using points, making your +code less verbose and essentially letting the compiler write +code for you
      +
    • +
    • Multiversal equality lets you limit equality +comparisons — at compile time — to only those comparisons that +make sense
      +
    • +
    +
  • Scala has state-of-the-art open source functional programming +libraries
  • +
  • You can create your own “control structures” and DSLs, thanks +to features like objects, by-name parameters, infix notation, +optional parentheses, extension methods, higher-order functions, +and more
    +
  • +
  • Many other goodies: case classes, companion classes and +objects, macros, union and intersection types, toplevel +definitions, numeric literals, multiple parameter lists, +and more
  • +
+

TODO: mention compiling code; mention performance?

+


+

+
    +
+

Features compared with examples

+

Given that introduction, the following sections provide +side-by-side comparisons of Python and Scala programming language +features.

+


+

+

Comments

+

The Scala comments syntax is the same as languages like C, C++, +and Java:

+ + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
Comments
+
#
+
//
+/* ... */
+/** ... */
+


+

+

Variable assignment
+

+

These examples demonstrate how to create variables in Python and +Scala:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
Integer,
+String
+
x = 1
+x = "Hi"
+
val x = 1
+val x = "Hi"
+
List
+
x = [1,2,3]
+
val x = List(1,2,3)
+
Dictionary/Map
+
x = {
+  "Toy Story": 8.3,
+  "Forrest Gump": 8.8,
+  "Cloud Atlas": 7.4
+}
+
val movies = Map(
+  "Toy Story" -> 8.3,
+  "Forrest Gump" -> 8.8,
+  "Cloud Atlas" -> 7.4
+)
+
Set
+
x = {1,2,3}
+
val x = Set(1,2,3)
+
Tuple
+
x = (11, "Eleven")val x = (11, "Eleven")
+

If a Scala field is going to be mutable, you should use var +instead of val for variable assignment:

+
var x = 1
x += 1
+

However, the rule of them is to always use val +unless +the variable specifically needs to be mutated.
+

+


+

+

OOP style classes and methods

+

This section provides comparisons of features related to +OOP-style classes and methods.
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
OOP style class,
+primary constructor
+
class Person(object):
+  def __init__(self, name):
+    self.name = name
+
+  def speak(self):
+    print('Hello, my name is %s' % self.name)
+
class Person (var name: String):
+  def speak() = println(s"Hello, my name is $name")
+
Create and use an instance
+
p = Person("John")
+p.name   # John
+p.name = 'Fred'
+p.name   # Fred
+p.speak()
+
val p = Person("John")
+p.name   // John
+p.name = "Fred"
+p.name   // Fred
+p.speak()
+
Method, one line
+
def add(a,b) = a + b
+
def add(a: Int, b: Int): Int = a + b
+
Method, multiple lines
+
def walkThenRun():
+    print('walk')
+    print('run')
+
def walkThenRun() =
+  println("walk")
+  println("run")
+
+


+

+

Interfaces, traits, and inheritance

+

If you’re familiar with Java 8 and newer, Scala traits are +similar to those Java interfaces. Traits are used all the time in +Scala, while Python interfaces and abstract classes are used much +less often. Therefore, rather than attempt to compare the two side +by side, this example shows how to use Scala traits to build a +small solution to a simulated math problem.

+
trait Adder:
  def add(a: Int, b: Int) = a + b
+trait Multiplier: +  def multiply(a: Int, b: Int) = a * b

// create a class from the traits +class SimpleMath extends Adder, Multiplier
val sm = new SimpleMath
sm.add(1,1) // 2
sm.multiply(2,2) // 4
+

There are many other ways to use traits with classes and objects, +but this gives you a little idea of how they can be used to +organize concepts into logical groups of behavior, and then merge +them as needed to create a complete solution.
+

+
+

Control structures

+

+

This section compares control structures in Python and Scala. +Both languages have constructs like if/else, +while, +for loops, and try. Scala also has match +expressions.
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
if statement,
+one line
+
if x == 1: print(x)
+
if x == 1 then println(x)
+
if statement,
+multiline
+
if x == 1:
+    print("x is 1, as you can see:")
+    print(x)
+
if x == 1 then
+  println("x is 1, as you can see:")
+  println(x)
+
if/else-if/else
+
if x < 0:
+    print("negative")
+elif x == 0:
+    print("zero")
+else:
+    print("positive")
+
if x < 0 then
+  println("negative")
+else if x == 0
+  println("zero")
+else
+  println("positive")
+
Returning a value from ifmin_val = a if a < b else bval minValue = if a < b then a else +b
if as the body of a method
+
def min(a, b):
+    return a if a < b else b
+
def min(a: Int, b: Int): Int =
+  if a < b then a else b
+
while
+
i = 1
+while i < 3:
+    print(i)
+    i += 1
+
var i = 1
+while i < 3 do
+  println(i)
+  i += 1
+
for loop with range
+
for i in range(0,3):
+    print(i)
+
// preferred
+for i <- 0 until 3 do println(i)
+
+// also available
+for (i <- 0 until 3) println(i)
+
+// multiline syntax
+for
+  i <- 0 until 3
+do
+  println(i)
for loop with a list
+
for i in ints: print(i)
+
+for i in ints:
+    print(i)
+
for i <- ints do println(i)   // +preferred
+
+for (i <- ints) println(i)   // also available
for loop,
+multiple lines
for i in ints:
+    x = i * 2
+    s = "i = {}, x = {}"
+    print(s.format(i,x))
for
+  i <- ints
+do
+  val x = i * 2
+  println(s"i = $i, x = $x")
+
Multiple range generators
+
for i in range(1,3):
+    for j in range(4,6):
+        for k in +range(1,10,3):
+            + + +s += "i = {}, j = {}, k = {}"
+            +print(s.format(i,j,k))
+
+
for
+  i <- 1 to 2
+  j <- 4 to 5
+  k <- 1 until 10 by 3
+do
+  println(s"i = $i, j = $j, k = $k")
+
Generator with guards (if +expressions)
+
for i in range(1,11):
+    if i % 2 == 0:
+        if i < 5:
+            +print(i)
+
+
for
+  i <- 1 to 10
+  if i % 2 == 0
+  if i < 5
+do
+  println(i)
+
Same as previous, with the if +statements condensed to one line
+
for i in range(1,11):
+    if i % 2 == 0 and i < 5:
+            +print(i)
+
for
+  i <- 1 to 10
+  if i % 2 == 0 && i < 5
+do
+  println(i)
Comprehensions: list comprehension in +Python, for comprehension in Scala
+
x = [i*10 for i in range(1,4)]
+
+# result: [10,20,30]
+
val x =
+  for
+    i <- 1 to 3
+  yield
+    i * 10
+// result: Vector(10, 20, 30)
+
match expressionsN/A (can use dictionaries for basic switch +functionality)
+
val monthAsString = day match {
+  case 1 => "January"
+  case 2 => "February"
+  _ => "Other"
+}
+
switch/match: +handling
+multiple conditions
+per case
+
N/A
+
val numAsString = i match {
+  case 1 | 3 | 5 | 7 | 9 => println("odd")
+  case 2 | 4 | 6 | 8 | 10 => println("even")
+  case _ => println("too big")
+}
+
try/catch/finally
+
try:
+    print(a)
+except NameError:
+    print("NameError")
+except:
+    print("Other")
+finally:
+    print("Finally")
+
try
+  writeTextToFile(text)
+catch
+  case ioe: IOException => println("Got an +IOException.")
+  case nfe: FileNotFoundException => println("Got a +FileNotFoundException.")
+finally
+  println("Finally")
+
+

Scala has many more match expression features; only +a +few are shown here.

+


+

+

Collections classes

+

This section compares the collections classes that are available +in Python and Scala, including lists, dictionaries/maps, sets, and +tuples.
+

+

Lists
+

+

Where Python has its list, Scala has several different +specialized mutable and immutable sequence classes, depending on +your needs. Because the Python list is mutable, it most directly +compares to Scala’s ArrayBuffer.
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
Python list,
+Scala sequences
+
a = [1,2,3]
+
// many different sequence classes
+val a = List(1,2,3)
+val a = Vector(1,2,3)
+val a = ArrayBuffer(1,2,3)
+
Access elements
+
a[0]
+a[1]
+
a(0)
+a(1)
+
Update list elements
+
a[0] = 10
+a[1] = 20
+
// ArrayBuffer is mutable
+a(0) = 10
+a(1) = 20
+
Combine two lists
+
c = a + bval c = a ++ b
Iterate over a list with a for +loop
+
for i in ints: print(i)
+
+for i in ints:
+    print(i)
for i <- ints do println(i)   // +preferred
+
+for (i <- ints) println(i)   // also available
+

Scala’s main three sequence classes are List, Vector, +and ArrayBuffer. The List and Vector +classes are the main classes to use when you want an immutable +sequence. The ArrayBuffer class is the main class to +use when you want a mutable sequence. (A “buffer” in Scala is a +sequence that can grow and shrink.)
+

+


+

+

Dictionary/Map

+

The Python dictionary is like the mutable Scala Map +class.

+ + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
Creation
+
my_dict = {
+    'a': 1,
+    'b': 2,
+    'c': 3
+}
+
val myMap = Map(
+  "a" -> 1,
+  "b" -> 2,
+  "c" -> 3
+)
+
Access elements
+
my_dict['a']   # 1
+
myMap("a")   // 1
+
for loop
+
for key, value in my_dict.items():
+    print(key)
+    print(value)
+
for
+  (key,value) <- myMap
+do
+  println(key)
+  println(value)
+
+

Scala has other specialized Map classes for +different needs.
+

+

Sets

+

The Python set is like the mutable Scala Set class. +

+ + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
Creation
+
+ +set = {"a", "b", "c"} val set = Set(1,2,3)
+

+
set = {1,2,1}
+# result: {1,2}
+
val set = Set(1,2,1)
+// result: Set(1,2)
+
+

Scala has other specialized Set classes for +different needs.

+

Tuples
+

+

The Python and Scala tuples are also similar:

+ + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
Creation
+
t = (11, 11.0, "Eleven")
+
val t = (11, 11.0, "Eleven")
+
Access elements
+
t[0]   # 11
+t[1]   # 11.0
+
t(0)   // 11
+t(1)   // 11.0
+
+


+

+

Methods on collections classes

+

Python and Scala have several of the same common functional +methods available to them:

+
    +
  • map
  • +
  • filter
  • +
  • reduce
    +
  • +
+

If you’re used to using these methods with lambda expressions in +Python, you’ll see that Scala has a similar approach with methods +on its collections classes. To demonstrate this functionality, +here are two sample lists:
+

+
list = (1,2,3)    // python
val list = List(1,2,3)   // scala
+

Given those lists, this table shows how to apply mapping and +filtering algorithms to it:
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Method
+
Python
+
Scala
+
Mapping with a comprehension
+
x = [i*10 for i in list]val x = for i <- list yield i * 10
+
Filtering with a comprehensionevens = [i for i in list if i % 2 == 0]val evens = list.filter(_ % 2 == 0)
Mapping and filtering with a comprehensionx = [i * 10 for i in list if i % 2 == +0]val x = xs.filter(_ % 2 == 0)
+           +.map(_ * 10)
Mapping with map
+
def times_10(n): return n * 10
+x = map(times_10, list)
+
val x = list.map(_ * 10)
+
Filtering with filter
+
f = lambda x: x if x > 1 else 1
+x = filter(f, list)
+
val x = list.filter(_ > 1)
+

Scala collections classes have over 100 functional methods to +simplify your code. In addition to map, filter, +and reduce, other commonly-used methods are listed +below.

+

Filtering methods:

+
    +
  • diff
  • +
  • distinct
    +
  • +
  • drop
    +
  • +
  • filter
  • +
  • head
  • +
  • slice
    +
  • +
  • tail
    +
  • +
+

Transformer methods:

+
    +
  • collect
  • +
  • flatten
    +
  • +
  • flatMap
  • +
  • fold
    +
  • +
  • map
  • +
  • reduce
    +
  • +
  • sortWith
    +
  • +
+

Grouping methods:

+
    +
  • groupBy
  • +
  • partition
  • +
  • sliding
  • +
  • span
  • +
  • splitAt
    +
  • +
+

Informational and mathematical methods:
+

+
    +
  • containsSlice
    +
  • +
  • count
  • +
  • distinct
  • +
  • exists
  • +
  • find
    +
  • +
  • min
  • +
  • max
  • +
  • slice
  • +
  • sum
    +
  • +
+

Here are a few examples that demonstrate how these methods work +on a list:

+
val a = List(10, 20, 30, 40, 10)      // List(10, 20, 30, 40, 10)
a.distinct                            // List(10, 20, 30, 40)
a.drop(2)                             // List(30, 40, 10)
a.dropRight(2)                        // List(10, 20, 30)
a.dropWhile(_ < 25)                   // List(30, 40, 10)
a.filter(_ < 25)                      // List(10, 20, 10)
a.filter(_ > 100)                     // List()
a.find(_ > 20)                        // Some(30)
a.head                                // 10
a.headOption                          // Some(10)
a.init                                // List(10, 20, 30, 40)
a.intersect(List(19,20,21))           // List(20)
a.last                                // 10
a.lastOption                          // Some(10)
a.slice(2,4)                          // List(30, 40)
a.tail                                // List(20, 30, 40, 10)
a.take(3)                             // List(10, 20, 30)
a.takeRight(2)                        // List(40, 10)
a.takeWhile(_ < 30)                   // List(10, 20)
+

These methods show a common pattern in Scala: Functional methods +that are available on objects. None of these methods mutate the +initial list a; instead, they all return the data +shown +after the comments.
+

+

Enums

+

This section compares enums (enumerations) in Python and Scala 3.
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature
+
Python
+
Scala
+
Simple creation
+
from enum import Enum, auto
+class Color(Enum):
+    RED = auto()
+    GREEN = auto()
+    BLUE = auto()
+
enum Color:
+  case Red, Green, Blue
+
Values and comparison
+
Color.RED == Color.BLUE
+# False
+
Color.Red == Color.Blue  // false
+
Parameterized enum
+
N/A
+
enum Color(val rgb: Int):
+  case Red   extends Color(0xFF0000)
+  case Green extends Color(0x00FF00)
+  case Blue  extends Color(0x0000FF)
+
User-defined enum members
+
N/A
+
enum Planet(mass: Double, radius: Double):
+  case Mercury extends Planet(3.303e+23, 2.4397e6)
+  case Venus   extends Planet(4.869e+24, +6.0518e6)
+  case Earth   extends Planet(5.976e+24, +6.37814e6)
+  // more planets ...
+
+  // fields and methods
+  private final val G = 6.67300E-11
+  def surfaceGravity = G * mass / (radius * radius)
+  def surfaceWeight(otherMass: Double) =  +otherMass * surfaceGravity
+
+
+


+

+

Concepts that are unique to Scala

+

TODO: Someone who knows more about Python than I do should +edit/update this section.
+

+

There are other concepts in Scala which currently don’t have +equivalent functionality in Python. Follow these links for more +details:
+

+
    +
  • Most concepts related to contextual abstractions
  • +
      +
    • Extension methods, type classes, implicit values
      +
    • +
    +
      +
    +
  • Scala allows multiple parameter lists
  • +
      +
    • This enables features like partially-applied functions, and +the ability to create your own DSL
      +
    • +
    +
  • Case classes
  • +
      +
    • Useful for functional programming and pattern matching
      +
    • +
    +
  • The ability to create your own control structures and DSLs
  • +
  • Pattern matching and match expressions
    +
  • +
  • Multiversal equality: the ability to control at compile time +what equality comparisons make sense
    +
  • +
  • Infix methods
  • +
  • Macros and metaprogramming
  • +
  • More ...
    +
  • +
+ From eedcf9554323ba841c7d53ddacb683f161edf345 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Wed, 21 Oct 2020 18:01:00 -0600 Subject: [PATCH 022/169] Corrected miscellaneous spelling and syntax errors. --- _overviews/overview/a-taste-of-scala.md | 4 +-- .../overview/collections-classes-methods.md | 14 +++++------ _overviews/overview/control-structures.md | 4 +-- _overviews/overview/interacting-with-java.md | 11 ++++---- _overviews/overview/scala-for-java-devs.md | 25 ++++++------------- _overviews/overview/scala-for-python-devs.md | 4 +-- 6 files changed, 25 insertions(+), 37 deletions(-) diff --git a/_overviews/overview/a-taste-of-scala.md b/_overviews/overview/a-taste-of-scala.md index 4e3cec09c3..f8e04c2cd6 100644 --- a/_overviews/overview/a-taste-of-scala.md +++ b/_overviews/overview/a-taste-of-scala.md @@ -1022,7 +1022,7 @@ t._2 // "eleven" t._3 // Person("Eleven") ``` -Tuples are nice for those times when you want to put a collection of heterogenous types in a little collection-like structure. See the Reference documentation for more tuple details. +Tuples are nice for those times when you want to put a collection of heterogeneous types in a little collection-like structure. See the Reference documentation for more tuple details. @@ -1041,7 +1041,7 @@ For more details, see the Contextual Abstractions section in this Overview, and -## Even more! +## Even more Scala has even more features that weren’t covered in this whirlwind tour. See the remainder of this Overview and the Reference documentation for many more details. diff --git a/_overviews/overview/collections-classes-methods.md b/_overviews/overview/collections-classes-methods.md index 717006e96f..d56c643072 100644 --- a/_overviews/overview/collections-classes-methods.md +++ b/_overviews/overview/collections-classes-methods.md @@ -38,13 +38,13 @@ The main collections classes you’ll use on a regular basis are: | Class | Immutable | Mutable | Description | | ------------- | --------- | ------- | ----------- | -| `List` | ✓ | | A linear (linked list), immutable sequence | -| `Vector` | ✓ | | An indexed, immutable sequence | -| `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 class 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. | -| `Set` | ✓ | ✓ | An iterable collection with no duplicate elements | +| `List` | ✓ | | A linear (linked list), immutable sequence | +| `Vector` | ✓ | | An indexed, immutable sequence | +| `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 class 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. | +| `Set` | ✓ | ✓ | An iterable collection with no duplicate elements | As shown, `Map` and `Set` come in both immutable and mutable versions. diff --git a/_overviews/overview/control-structures.md b/_overviews/overview/control-structures.md index fc4bff784d..6a384dfd30 100644 --- a/_overviews/overview/control-structures.md +++ b/_overviews/overview/control-structures.md @@ -285,7 +285,7 @@ val list = After that `for` expression runs, the variable `list` is a `Vector` that contains the values shown. This is how the expression works: -1. The `for` expression starts to iterate over the values in the range `(10, 11, 12)`. It first works on the value `10`, multiples it by `2`, then _yields_ that result, the value `20`. +1. The `for` expression starts to iterate over the values in the range `(10, 11, 12)`. It first works on the value `10`, multiplies it by `2`, then _yields_ that result, the value `20`. 2. Next, it works on the `11` — the second value in the range. It multiples it by `2`, then yields the value `22`. You can think of these yielded values as accumulating in a temporary holding place. 3. Finally the loop gets the number `12` from the range, multiplies it by `2`, yielding the number `24`. The loop completes at this point and yields the final result, the `Vector(20,22,24)`. @@ -481,7 +481,7 @@ Using a `match` expression as the body of a method is a very common use. - Typed - Default/wildcard -All of these patterns are shown in the following `pattern` method, which takes an input parameter of tye `Any` and returns a `String`: +All of these patterns are shown in the following `pattern` method, which takes an input parameter of type `Any` and returns a `String`: ```scala def pattern(x: Any): String = x match diff --git a/_overviews/overview/interacting-with-java.md b/_overviews/overview/interacting-with-java.md index df8c7e4555..8276116194 100644 --- a/_overviews/overview/interacting-with-java.md +++ b/_overviews/overview/interacting-with-java.md @@ -257,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 soem 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. +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. For example, this Scala `exceptionThrower` method is annotated to declare that it throws an `Exception`: @@ -284,11 +284,11 @@ public class ScalaExceptionsInJava { The compiler gives this error: -.... +```` [error] ScalaExceptionsInJava: unreported exception java.lang.Exception; must be caught or declared to be thrown [error] SExceptionThrower.exceptionThrower() -.... +```` This is good — it’s what you want: the annotation tells the Java compiler that `exceptionThrower` can throw an exception. Now when you’re writing Java code you must handle the exception with a `try` block or declare that your Java method throws an exception. @@ -319,15 +319,14 @@ public class JVarargs { VarargsPrinter.printAll("Hello", "world"); } } - ``` When this code is run, it results in the following output: -.... +```` Hello world -.... +```` diff --git a/_overviews/overview/scala-for-java-devs.md b/_overviews/overview/scala-for-java-devs.md index 6cc72c93cb..0c67d0d863 100644 --- a/_overviews/overview/scala-for-java-devs.md +++ b/_overviews/overview/scala-for-java-devs.md @@ -1,9 +1,8 @@ - - - -Scala for Java Developers - - +--- +title: Scala for Java Developers +description: This page is for Java developers who are interested in learning about Scala 3. +--- +

Notes to reviewers:

    @@ -963,9 +962,9 @@ value:

    def makeInt(s: String): Option[Int] =
     try
    -Some(s.toInt)
    +  Some(s.toInt)
     catch
    -case e: NumberFormatException => None
    +  case e: NumberFormatException => None
     

    The Scala Option is similar to the Java Optional class. As shown, if the string-to-int conversion succeeds, the Int @@ -1014,15 +1013,5 @@ in Java 11:

  1. More ...
  2. -
    -


    -

    -

    -

    -

    -


    -

    - - diff --git a/_overviews/overview/scala-for-python-devs.md b/_overviews/overview/scala-for-python-devs.md index 2f68168bc8..feaeb9c161 100644 --- a/_overviews/overview/scala-for-python-devs.md +++ b/_overviews/overview/scala-for-python-devs.md @@ -91,13 +91,13 @@ programming

    Programming level similarities

    This section looks at the similarities you’ll see between Python -and Scala when you write code on an every day basis:
    +and Scala when you write code on an everyday basis:

    • Scala’s type inference often makes it feel like a dynamically typed language
    • -
    • Neither language uses semi-colons to end expressions
      +
    • Neither language uses semicolons to end expressions
    • Both languages support the use of significant indentation rather than braces and parentheses
    • From ae6cbbdc0c6dfd3a1bde18f6753df092bc03825b Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Wed, 28 Oct 2020 22:02:10 -0600 Subject: [PATCH 023/169] Adding four new Overview chapters. --- _overviews/overview/anonymous-functions.md | 326 +++++++++++++++++++++ _overviews/overview/concurrency.md | 267 +++++++++++++++++ _overviews/overview/eta-expansion.md | 72 +++++ _overviews/overview/scala-tools.md | 320 ++++++++++++++++++++ 4 files changed, 985 insertions(+) create mode 100644 _overviews/overview/anonymous-functions.md create mode 100644 _overviews/overview/eta-expansion.md diff --git a/_overviews/overview/anonymous-functions.md b/_overviews/overview/anonymous-functions.md new file mode 100644 index 0000000000..07f58dabf0 --- /dev/null +++ b/_overviews/overview/anonymous-functions.md @@ -0,0 +1,326 @@ +--- +title: Anonymous Functions (and Function Variables) +description: This page shows how to use anonymous functions in Scala, including examples with the List class 'map' and 'filter' functions. +--- + + +This chapter focuses on how to create *anonymous functions* — also known as *function literals*, and called *lambdas* in some languages — in Scala. Creating anonymous functions also helps us demonstrate something else: How to create function variables in Scala. + + + +## Examples + +An anonymous function is like a little mini-function. For example, given a list like this: + +```scala +val ints = List(1,2,3) +``` + +You can create a new list by doubling each element in `ints`, using the `List` class `map` method and your custom anonymous function: + +```scala +val doubledInts = ints.map(_ * 2) // List(2, 4, 6) +``` + +As the comment shows, `doubledInts` contains the list, `List(2, 4, 6)`. In that example, this portion of the code is an anonymous function: + +```scala +_ * 2 +``` + +This is a shorthand way of saying, “Multiply a given element by 2.” + + +### Longer forms + +Once you’re comfortable with Scala, that’s a common way to write anonymous functions, but if you prefer, you can also write them using longer forms. So, in addition to writing that code like this: + +```scala +val doubledInts = ints.map(_ * 2) +``` + +you can also write it using these forms: + +```scala +val doubledInts = ints.map((i: Int) => i * 2) +val doubledInts = ints.map((i) => i * 2) +val doubledInts = ints.map(i => i * 2) +``` + +All of these lines have the exact same meaning: Double each element in `ints` to create a new list, `doubledInts`. The syntax of each form is explained in the next section. + +If you’re familiar with Java, it may help to know that those `map` examples are the equivalent of this Java code: + +```java +List ints = List.of(1,2,3); +List doubledInts = ints.stream() + .map(i -> i * 2) + .collect(Collectors.toList()); +``` + + + +## The rules about shortening anonymous functions + +When you want to be explicit, you can write an anonymous function using this long form: + +```scala +val doubledInts = ints.map((i: Int) => i * 2) +``` + +The anonymous function in that expression is this: + +```scala +(i: Int) => i * 2 +``` + +If you’re not familiar with this syntax, it helps to think of the `=>` symbol as a transformer, because the expression *transforms* the parameter list on the left side of the symbol (an `Int` variable named `i`) into a new result using the algorithm on the right side of the `=>` symbol (in this case, an expression that doubles the `Int`). + + +### Shortening that expression + +Starting with that longest and most explicit form: + +```scala +val doubledInts = ints.map((i: Int) => i * 2) +``` + +Because the Scala compiler can infer from the data in `ints` that `i` is an `Int`, the `Int` declaration can be removed: + +```scala +val doubledInts = ints.map((i) => i * 2) +``` + +Because there’s only one argument, the parentheses around the parameter `i` aren’t needed: + +```scala +val doubledInts = ints.map(i => i * 2) +``` + +Next, because Scala lets you use the `_` symbol instead of a variable name when the parameter appears only once in your function, the code can be simplified even more: + +```scala +val doubledInts = ints.map(_ * 2) +``` + +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)) +``` + +As before, the `Int` declaration isn’t required, and because there’s only one argument, the parentheses around `i` aren’t needed: + +```scala +ints.foreach(i => println(i)) +``` + +Because `i` is used only once in the body of the function, the expression can be further simplified with the `_` symbol: + +```scala +ints.foreach(println(_)) +``` + +Finally, if a function literal 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: + +```scala +ints.foreach(println) +``` + + + +## From anonymous functions to function variables + +Going back to this example: + +```scala +val doubledInts = ints.map((i: Int) => i * 2) +``` + +We noted earlier that this part of the expression is an anonymous function: + +```scala +(i: Int) => i * 2 +``` + +The reason it’s called *anonymous* is because it’s not assigned to a variable, and therefore doesn’t have a name. + +What’s interesting now is that you can assign this anonymous function (function literal) to a variable to create a function variable: + +```scala +val double = (i: Int) => i * 2 +``` + +Now we have a function variable named `double`. In this expression, our original function literal is on the right side of the `=` symbol: + +```scala +val double = (i: Int) => i * 2 + ----------------- +``` + +and the new variable name is on the left side: + +```scala +val double = (i: Int) => i * 2 + ------ +``` + +In this expression the function’s parameter list is underlined here: + +```scala +val double = (i: Int) => i * 2 + -------- +``` + +Like the parameter list for a method, this means that the `double` function takes one parameter, an `Int` named `i`. In fact, you can see in the REPL that `double` has the type `Int => Int`, meaning that it takes a single `Int` parameter and returns an `Int`: + +```scala +scala> val double = (i: Int) => i * 2 +val double: Int => Int = ... +``` + +Now you can call the `double` function like this: + +```scala +val x = double(2) // 4 +``` + +You can also pass `double` into a `map` call: + +```scala +List(1,2,3).map(double) // List(2, 4, 6) +``` + +Furthermore, if you happen to have another function that has the `Int => Int` type: + +```scala +val triple = (i: Int) => i * 3 +``` + +You can also store them in a `List` or `Map`: + +```scala +val functionList = List(double, triple) + +val functionMap = Map( + "2x" -> double, + "3x" -> triple +) +``` + +If you paste those expressions into the REPL, you’ll see that they have these types: + +```` +// a List that contains functions of the type `Int => Int` +functionList: List[Int => Int] + +// a Map whose keys have the type `String`, and whose +// values have the type `Int => Int` +functionMap: Map[String, Int => Int] +```` + +The important parts here are: + +- To create a function variable, just assign a variable name to a function literal +- Once you have a function, you can treat it like any other variable, i.e., like a `String` or `Int` variable + +>And thanks to the improved Eta Expansion functionality in Scala 3, you can treat *methods* in the same way. + + + + +## Key points + +The key points of this lesson are: + +- You can write anonymous functions as little snippets of code +- You can pass them into the dozens of higher-order functions (methods) on the collections classes, i.e., methods like `filter`, `map`, etc. +- With these little snippets of code and powerful higher-order functions, you create a lot of functionality with just a little code +- Function variables are simply anonymous functions that have been bound to a variable + + + +## Bonus: Digging a little deeper + +You may be wondering how methods like `map` and `filter` work. Technically, the `map` method on a sequence class like `List` takes a *function* parameter. That function is defined to take a generic parameter `A` as input, and it returns a generic value `B` as its result. Any function that matches this type signature can be used here. When `map` finishes traversing over all of its elements, it returns a `List[B]` as its result. When you see this `map` method signature in the `List` Scaladoc, that’s what it means: + +```scala +def map[B](f: (A) => B): List[B] +``` + +In the case of our `ints` list, the compiler knows that `ints` is a list of integers, i.e., a `List[Int]`. Therefore, the generic parameter `A` must be the type `Int`. + +The parameter `B` is whatever your custom function returns. For instance, while the anonymous function shown previously transformed one `Int` into another `Int` value, this function transforms an `Int` to a `Double`: + +```tut +val intToDouble = (i: Int) => i.toDouble +``` + +So now when you use that function with `ints` and `map`, you get a `List[Double]`: + +```scala +ints.map(intToDouble) // List[Double] = List(1.0, 2.0, 3.0) +``` + +In summary, while generically the `map` method expects this type: + +```scala +f: (A) => B +``` + +in this specific example the `intToDouble` function has this type: + +```scala +f: (Int) => Double +``` + + +### More `map` and `filter` examples + +Here’s another `map` example that transforms a `Seq[String]` into a `Seq[Int]`: + +```tut +Seq("hi", "there").map(_.length) // Seq[Int] = List(2, 5) +``` + +In this case the anonymous function `_.length` transforms each `String` it’s given into an `Int` by calling the `length` function on the `String`. + +Similarly, `filter` also takes a function parameter, but the function it’s given must return a `Boolean` value, so it’s Scaladoc signature looks like this: + +```scala +def filter(p: (A) => Boolean): List[A] +``` + +So any anonymous function used with `filter` must take the type `A` that’s contained in the list and return a `Boolean`, like these examples: + +```scala +val list = Seq(1,2,3,4,5) +list.filter(_ < 3) // List(1, 2) +list.filter(_ % 2 == 0) // List(2, 4) +``` + +If you’re not familiar with it, that last example performs a modulus comparison that returns `true` for even numbers. + +>The letter `p` is used in the `filter` Scaladoc because any function that returns a `Boolean` value is known as a *predicate*. + + +### One more thing + +As a bit of extra credit, you may be interested to know that this expression: + +```scala +val doubledInts = ints.map(_ * 2) +``` + +is equivalent to this expression: + +```scala +val doubledInts = for i <- ints yield i * 2 +``` + +Depending on their background, when some developers come to Scala they may initially feel more comfortable writing this `for` expression, until one day they realize it’s the exact same thing as writing the more concise `map` expression. + + + + + diff --git a/_overviews/overview/concurrency.md b/_overviews/overview/concurrency.md index e69de29bb2..290ac9c304 100644 --- a/_overviews/overview/concurrency.md +++ b/_overviews/overview/concurrency.md @@ -0,0 +1,267 @@ +--- +title: Concurrency with Scala Futures +description: This page discusses how Scala concurrency works, with an emphasis on Scala Futures. +--- + + +When you want to write parallel and concurrent applications in Scala, you *can* use the native Java `Thread` — but the Scala [Future](https://www.scala-lang.org/api/current/scala/concurrent/Future$.html) makes parallel/concurrent programming much simpler, so it’s preferred, and covered in this chapter. + + + +## Introduction + +Here’s a description of the Scala `Future` from its Scaladoc: + +>“A `Future` represents a value which may or may not *currently* be available, but will be available at some point, or an exception if that value could not be made available.” + +To help demonstrate what that means, let’s first look at single-threaded programming. In the single-threaded world you bind the result of a method call to a variable like this: + +```tut +def aShortRunningTask(): Int = 42 +val x = aShortRunningTask() +``` + +In this code, the value `42` is immediately bound to the variable `x`. + +When you’re working with a `Future`, the assignment process looks similar: + +```scala +def aLongRunningTask(): Future[Int] = ??? +val x = aLongRunningTask() +``` + +The main difference in this case is that because `aLongRunningTask` takes an indeterminate amount of time to return, the value in `x` may or may not be *currently* available, but it will be available at some point — in the future. + +In this chapter you’ll see how to use futures, including how to run multiple futures in parallel and combine their results in a `for` expression. You’ll also see other methods that are used to handle the value in a future once it returns. + +>An important point to know about futures is 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. + + + +## An example in the REPL + +A future is used to create a temporary pocket of concurrency. For instance, you use a future when you need to call an algorithm that runs an indeterminate amount of time — such as calling a remote microservice — so you want to run it off of the main thread. + +To demonstrate how this works, let’s start with a `Future` example in the REPL. First, paste in these required `import` statements: + +```scala +import scala.concurrent.Future +import scala.concurrent.ExecutionContext.Implicits.global +import scala.util.{Failure, Success} +``` + +Now you’re ready to create a future. For this example, first define a long-running, single-threaded algorithm: + +```scala +def longRunningAlgorithm = + Thread.sleep(10_000) + 42 +``` + +That fancy algorithm returns the integer value `42` after a ten second delay. Now call that algorithm by passing it into the `Future` constructor, and assigning the result to a variable: + +```scala +scala> val f = Future(longRunningAlgorithm) +f: 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: + +```scala +scala> f +val res1: scala.concurrent.Future[Int] = Future() +``` + +But if you check again after ten seconds, you’ll see that it completes successfully: + +```scala +scala> f +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`. Therefore, when you work with the result of a future, you use the usual `Try`-handling techniques. + + + +### Using `map` with futures + +`Future` has a `map` method, which you use just like the `map` method on the collections classes. This is what the result looks like when you call `map` right after creating the variable `f`: + +```scala +scala> val a = f.map(_ * 2) +a: scala.concurrent.Future[Int] = Future() +``` + +As shown, for the future that was created with the `longRunningAlgorithm`, the initial output shows `Future()`. But when you check `a`’s value after ten seconds you’ll see that it contains the expected result of `84`: + +```scala +scala> a +res1: scala.concurrent.Future[Int] = Future(Success(84)) +``` + +Once again, the successful result is wrapped inside a `Success` and a `Future`. + + +### Using callback methods with futures + +In addition to higher-order functions like `map`, you can also use callback methods with futures. One commonly used callback method is `onComplete`, which takes a *partial function* in which you handle the `Success` and `Failure` cases: + +```scala +f.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: + +```scala +Got the callback, value = 42 +``` + + + +## Other Future methods + +The `Future` class has other methods you can use. It has some of the methods that you find on Scala collections classes, including: + +- `filter` +- `flatMap` +- `map` + +Its callback methods are: + +- `onComplete` +- `andThen` +- `foreach` + +Other transformation methods include: + +- `fallbackTo` +- `recover` +- `recoverWith` + + +See the `Future` class Scaladoc for a list of additional methods. Also, see the Concurrency section in the Reference documentation for more details on how futures work. + + + + +## 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. The correct approach is: + +1. Create the futures +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: + +```tut +import scala.concurrent.Future +import scala.concurrent.ExecutionContext.Implicits.global +import scala.util.{Failure, Success} + +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 + 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 + val result = + for + r1 <- f1 + r2 <- f2 + r3 <- f3 + yield + println(s"in the 'yield': ${delta()}") + (r1 + r2 + r3) + + // (3) process the result + result.onComplete { + case Success(x) => + println(s"in the Success case: ${delta()}") + println(s"result = $x") + case Failure(e) => + e.printStackTrace + } + + println(s"before the 'sleep(3000)': ${delta()}") + + // important for a little parallel demo: keep the jvm alive + sleep(3000) +``` + +When you run that application, you see output that looks like this: + +```` +creating the futures: 1 +before the 'sleep(3000)': 2 +in the 'yield': 806 +in the Success case: 806 +result = 6 +```` + +As that output shows, the futures are created very rapidly, and in just two milliseconds the print statement right before the `sleep(3000)` statement at the end of the method is reached. 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. + + +### A method that returns a future + +So far you’ve seen how to pass a single-threaded algorithm into a `Future` constructor. You can use the same technique to create a method that returns a `Future`: + +```scala +// simulate a slow-running method +def slowlyDouble(x: Int, delay: Long): Future[Int] = Future { + sleep(delay) + x * 2 +} +``` + +As with the previous examples, just assign the result of the method call to a new variable. Then when you check the result right away you’ll see that it’s not completed, but after the delay time the future will have a result: + +```` +scala> val f = slowlyDouble(2, 5_000L) val f: concurrent.Future[Int] = Future() + +scala> f +val res0: concurrent.Future[Int] = Future() + +scala> f +val res1: concurrent.Future[Int] = Future(Success(4)) +```` + + + +## Key points about futures + +Hopefully those examples give you an idea of how Scala futures work. To summarize, a few key points about futures are: + +- You construct futures to run tasks off of the main thread +- Futures are intended for one-shot, potentially long-running concurrent tasks that *eventually* return a value; they create a temporary pocket of concurrency +- A future starts running as soon as you construct it +- A benefit of futures over threads is that they work with `for` expressions, and come with a variety of callback methods that simplify the process of working with concurrent threads +- When you work with futures you don’t have to concern yourself with the low-level details of thread management +- You handle the result of a future with callback methods like `onComplete` and `andThen`, or transformation methods like `filter`, `map`, etc. +- The value inside a `Future` is always an instance of one of the `Try` types: `Success` or `Failure` +- If you’re using multiple futures to yield a single result, combine them in a `for` expression + +Also, as you saw with the `import` statements in these examples, the Scala `Future` depends on an `ExecutionContext`. This relationship, and many more details about futures, are explained in the Reference documentation. + + + + + diff --git a/_overviews/overview/eta-expansion.md b/_overviews/overview/eta-expansion.md new file mode 100644 index 0000000000..0594370fad --- /dev/null +++ b/_overviews/overview/eta-expansion.md @@ -0,0 +1,72 @@ +--- +title: Eta Expansion in Scala 3 +description: This page discusses Eta Expansion, the Scala technology that automatically and transparently converts methods into functions. +--- + + + +## Background + +When you look at the Scaladoc for the `map` method on Scala collections classes, you see that it’s defined to accept a *function*: + +```scala +def map[B](f: (A) => B): List[B] + ----------- +``` + +Indeed, the Scaladoc clearly states, “`f` is the function to apply to each element.” But despite that, somehow you can pass a *method* into `map`, and it still works: + +```scala +def times10(i: Int) = i * 10 // a method +List(1,2,3).map(times10) // List(10,20,30) +``` + +Have you ever wondered how this works — how you can pass a *method* into `map`, which expects a *function*? + + +### Eta Expansion + +The technology behind this is known as *Eta Expansion*. It converts an expression of method type to an equivalent expression of function type, and it does so seamlessly and quietly. + + +### The differences between methods and functions + + +Technically, *methods* are part of the definition of a class, while *functions* are complete objects themselves, making them first-class entities. For example, functions can be assigned to variables. + +Their syntax is also different. This example shows how to define a method and a function that perform the same task, determining if the given integer is even: + +```scala +def isEvenMethod(i: Int) = i % 2 == 0 // a method +val isEvenFunction = (i: Int) => i % 2 == 0 // a function +``` + +The function truly is an object, so you can use it just like any other variable, such as putting it in a list: + +```scala +val functions = List(isEvenFunction) +``` + +Conversely, a method technically isn’t an object, so in Scala 2 you couldn’t put a method in a `List`, at least not directly, as shown in this error message: + +```scala +// this example shows the Scala 2 error message +val methods = List(isEvenMethod) + ^ +error: missing argument list for method isEvenMethod +Unapplied methods are only converted to functions when a function type is expected. +You can make this conversion explicit by writing `isEvenMethod _` or `isEvenMethod(_)` instead of `isEvenMethod`. +``` + +As shown in that text, there is a manual way to convert a method into a function in Scala 2, but the important part for Scala 3 is that the Eta Expansion technology is improved, so now when you attempt to use a method as a variable, it just works — you don’t have to handle the manual conversion yourself: + +```scala +val functions = List(isEvenFunction) // works +val methods = List(isEvenMethod) // works +``` + + + + diff --git a/_overviews/overview/scala-tools.md b/_overviews/overview/scala-tools.md index e69de29bb2..8c0cdba053 100644 --- a/_overviews/overview/scala-tools.md +++ b/_overviews/overview/scala-tools.md @@ -0,0 +1,320 @@ +--- +title: Scala Tools +description: This chapter looks at two commonly-used Scala tools, sbt and ScalaTest. +--- + + + +In this chapter you’ll see two tools that are commonly used in Scala projects: + +- [The *sbt* build tool](https://www.scala-sbt.org) +- [ScalaTest](https://www.scalatest.org), a source code testing framework + +We’ll start by showing how to use sbt to build your Scala projects, and then we’ll show how to use sbt and ScalaTest together to test your Scala projects. + + + +## 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. + +>To install sbt, see [its download page](https://www.scala-sbt.org/download.html). + + +### The sbt directory structure + +Like Maven, sbt uses a standard project directory structure. A nice benefit of that is that once you’re comfortable with its structure, it makes it easy to work on other Scala/sbt projects. + +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/ + |-- resources/ + |-- scala/ +|-- test/ + |-- resources/ + |-- scala/ +target/ +``` + +The *resources* directories are optional, and you can also add *java* directories at the same level as the *scala* directories if you want to use Java source code in your project. You can also add a *lib* directory under the root directory if you want to add unmanaged dependencies — JAR files — to your project. + + +### Creating a “Hello, world” sbt project directory structure + + + +Creating this directory structure is simple. There are tools to do this for you, but assuming that you’re using a Unix/Linux system, you can use these commands to create your first sbt project directory structure: + +```bash +mkdir HelloWorld +cd HelloWorld +mkdir -p src/{main,test}/{resources,scala} +mkdir project target +``` + +When you run a `find .` command after running those commands, you should see this result: + +```bash +$ find . +. +./project +./src +./src/main +./src/main/resources +./src/main/scala +./src/test +./src/test/resources +./src/test/scala +./target +``` + +If you see that, you’re in great shape for the next step. + +>There are other ways to create the files and directories for an sbt project. One way is to use the `sbt new` command, [which is documented here on scala-sbt.org](https://www.scala-sbt.org/1.x/docs/Hello.html). That approach isn’t shown here because some of the files it creates are more complicated than necessary for an introduction like this. + + +### Creating a first build.sbt file + +At this point you only need two more things to run a “Hello, world” project: + +- A *build.sbt* file +- A *Hello.scala* file + +For a little project like this, the *build.sbt* file only needs to contain a few lines, like this: + +```scala +name := "HelloWorld" +version := "1.0" +scalaVersion := "{{ site.scala-version }}" +``` + +Because sbt projects use a standard directory structure, sbt will be able to find everything else it needs. + +Now you just need to add a little “Hello, world” program. + + +### A “Hello, world” program + +In large projects, all of your Scala source code files will go under the *src/main/scala* and *src/test/scala* directories, but for a little sample project like this, you can put your source code file in the root directory of your project. Therefore, create a file named *HelloWorld.scala* in the root directory with these contents: + +```scala +@main def helloWorld = println("Hello, world") +``` + +Now, use the `sbt run` command to compile and run your project. When you do so, you’ll see output that looks like this: + +```bash +$ sbt run + +[info] welcome to sbt +[info] loading settings for project ... +[info] loading project definition +[info] loading settings for project root from build.sbt ... +[info] Compiling 1 Scala source ... +[info] running helloWorld +Hello, world +[success] Total time: 4 s +``` + +The first time you run `sbt` it downloads everything it needs, and that can take a little while to run, but after that it gets much faster. + +Also, once you get this first step working, you’ll find that it’s much faster to run sbt interactively. To do that, first run the `sbt` command by itself: + +```bash +$ sbt + +[info] welcome to sbt +[info] loading settings for project ... +[info] loading project definition ... +[info] loading settings for project root from build.sbt ... +[info] sbt server started at local:///Users/al/.sbt/1.0/server/7d26bae825c38a31074c/sock +sbt:hello-world> _ +``` + +Then inside this sbt shell, execute its `run` command: + +```` +sbt:hello-world> run + +[info] running helloWorld +Hello, world +[success] Total time: 0 s +```` + +There, that’s much faster. + +If you type `help` at the sbt command prompt you’ll see a list of other commands you can run. But for now, just type `exit` to leave the sbt shell. (You can also press `CTRL-D` instead of typing `exit`.) + + +### Other build tools for Scala + + +While sbt is widely used, there are other tools you can use to build Scala projects: + +- [Ant](https://ant.apache.org/) +- [Gradle](https://gradle.org/) +- [Maven](https://maven.apache.org/) +- [Mill](https://www.lihaoyi.com/mill/) + + + +## Using sbt with ScalaTest + +ScalaTest is one of the main testing libraries for Scala projects, and in this section you’ll see how to create a Scala/sbt project that uses ScalaTest. + + +### Creating the project directory structure + +As with the previous lesson, create an sbt project directory structure for a project named *HelloScalaTest* with the following commands: + +```sh +mkdir HelloScalaTest +cd HelloScalaTest +mkdir -p src/{main,test}/{resources,scala} +mkdir project target +``` + + +### Creating the build.sbt file + +Next, create a *build.sbt* file in the root directory of your project with these contents: + +```scala +name := "HelloScalaTest" +version := "1.0" +scalaVersion := "{{site.scala-version}}" + +libraryDependencies ++= Seq( + "org.scalatest" % "scalatest-core_0.27" % "3.2.2", + "org.scalatest" % "scalatest_0.27" % "3.2.2" % Test +) +``` + +The first three lines of this file are essentially the same as the first example. The `libraryDependencies` lines tell sbt to include the dependencies (JAR files) that are needed to include ScalaTest. + +>The ScalaTest documentation has always been good, and you can always find the up to date information on what those lines should look like on the [Installing ScalaTest](https://www.scalatest.org/install) page. + + +### Create a Scala source code file + +Next, create a Scala program that you can use to demonstrate ScalaTest. First, create a directory under *src/main/scala* named *math*: + +```sh +$ mkdir src/main/scala/math + ---- +``` + +Then, inside that directory, create a file named *MathUtils.scala* with these contents: + +```scala +package math + +object MathUtils: + def double(i: Int) = i * 2 +``` + +There isn’t much that can go wrong with that source code, but it provides a simple way to demonstrate ScalaTest. This object doesn’t have a `main` method, so instead of trying to run the project with `sbt run`, we’ll just compile it with `sbt compile`: + +```` +$ sbt compile + +[info] welcome to sbt +[info] loading settings for project ... +[info] loading project definition ... +[info] loading settings for project ... +[info] Executing in batch mode. For better performance use sbt's shell +[success] Total time: 1 s +```` + +With that compiling, let’s create a ScalaTest file to test the `double` method. + + +### Your first ScalaTest tests + +ScalaTest is very flexible, and there are several different ways to write tests. A simple way to get started is to write tests using the ScalaTest `AnyFunSuite`. To get started, create a directory named *math* under the *src/test/scala* directory, like this: + +```sh +$ mkdir src/test/scala/math + ---- +``` + +Next, create a file named *MathUtilsTests.scala* in that directory with the following contents: + +```scala +package math + +import org.scalatest.funsuite.AnyFunSuite + +class MathUtilsTests extends AnyFunSuite { + + // test 1 + test("'double' should handle 0") { + val result = MathUtils.double(0) + assert(result == 0) + } + + // test 2 + test("'double' should handle 1") { + val result = MathUtils.double(1) + assert(result == 2) + } + + test ("test with Int.MaxValue") (pending) + +} +``` + +This code demonstrates the ScalaTest `AnyFunSuite` approach. A few important points: + +- Your test class should extend `AnyFunSuite` +- You create tests as shown, by giving each `test` a unique name +- At the end of each test you should call `assert` to test that a condition has been satisfied +- When you know you want to write a test, but you don’t want to write it right now, create the test as “pending,” with the syntax shown + +Using ScalaTest like this is similar to JUnit, so if you’re coming to Scala from Java, hopefully this looks similar. + +Now you can run these tests with the `sbt test` command. Skipping the first few lines of output, the result looks like this: + +```` +sbt:HelloScalaTest> test + +[info] Compiling 1 Scala source ... +[info] MathUtilsTests: +[info] - 'double' should handle 0 +[info] - 'double' should handle 1 +[info] - test with Int.MaxValue (pending) +[info] Total number of tests run: 2 +[info] Suites: completed 1, aborted 0 +[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 1 +[info] All tests passed. +[success] Total time: 1 s +```` + +If everything works well, you’ll see output that looks like that. Welcome to the world of testing Scala applications with sbt and ScalaTest. + + +### Multiple types of tests + +This example demonstrated a style of testing that is similar to xUnit testing, with a few benefits of the *Behavior-Driven Development* (BDD) style. But as mentioned, ScalaTest is flexible and you can also write tests using other styles. See the User Guide on the [ScalaTest website](https://www.scalatest.org) for more details on the different testing styles that are available. + + + +## Where to go from here + +For more information about sbt and ScalaTest, see the following resources: + +- [The sbt documentation](https://www.scala-sbt.org/1.x/docs/) +- [The ScalaTest website](https://www.scalatest.org/) + + + From 26eb09657c083ba73f2353a3761ec9a94ecd8df9 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Sat, 31 Oct 2020 22:20:04 -0600 Subject: [PATCH 024/169] =?UTF-8?q?Initial=20versions=20of=20the=20?= =?UTF-8?q?=E2=80=98Main=20Methods=E2=80=99=20and=20=E2=80=98Higher-Order?= =?UTF-8?q?=20Functions=E2=80=99=20documents.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/higher-order-functions.md | 547 ++++++++++++++++++ _overviews/overview/main-methods.md | 136 +++++ 2 files changed, 683 insertions(+) create mode 100644 _overviews/overview/higher-order-functions.md create mode 100644 _overviews/overview/main-methods.md diff --git a/_overviews/overview/higher-order-functions.md b/_overviews/overview/higher-order-functions.md new file mode 100644 index 0000000000..cb427b3b69 --- /dev/null +++ b/_overviews/overview/higher-order-functions.md @@ -0,0 +1,547 @@ +--- +title: Higher-order Functions +description: This page demonstrates how to create and use higher-order functions in Scala. +--- + + + +A higher-order function is often defined as a function that (a) takes other functions as input parameters or (b) returns a function as a result. In Scala this is possible because functions are first-class values. While we use the term “higher-order function,” in Scala this phrase is used for both *methods* and *functions* because they can generally be used in the same places. + +>The ability to pass functions around as values enables a form of “power” programming that lets you write code that’s concise and still readable. + +In the examples so far you’ve seen how to be the *consumer* of functions that take other functions as input parameters, such as higher-order functions like `map` and `filter`. In this chapter you’ll see how to be the *creator* of higher-order functions, including: + +- How to write methods that take functions as input parameters +- How to return a function from a method + +In the process you’ll see: + +- The syntax you use to define function input parameters +- Multiple examples of that syntax +- How to call a function once you have a reference to it + +As a beneficial side effect of this chapter, once you’re comfortable with the syntax, you’ll use it to define function parameters, anonymous functions, and function variables, and it also becomes easier to read the Scaladoc for higher-order functions. + + + +## Recap: Being a consumer of higher-order functions + +As a quick recap, in this Overview you’ve already seen examples of how to use existing higher-order functions in the Scala collections classes, including `map` and `filter`: + +```scala +List(1,2,3).map(_ * 10) // List(10,20,30) + +def isEven(i: Int) = i % 2 == 0 +Vector(1,2,3,4).filter(isEven) // Vector(2,4) +``` + +A few key points of the `filter` example are: + +- `filter` accepts a function, anonymous function, or method as an input parameter. +- The functions you pass into `filter` must match the type signature that `filter` expects — in this example, a function that takes an `Int` input parameter and returns a `Boolean`. + +>Although we use the term “function,” `filter` can accept a *method* as a parameter thanks to Scala’s [Eta Expansion](TODO:LINK) technology. + + +### Understanding `filter`’s Scaladoc + +You can understand the type of functions `filter` accepts by looking at its Scaladoc. For example here’s the `filter` definition on the `List` class: + +```scala +def filter(p: (A) => Boolean): List[A] +``` + +This states that `filter` is a method that takes a predicate parameter named `p` and returns a `List[A]`. (A *predicate* is just a function that returns a `Boolean` value.) Without looking at the internal details of `filter`, all we know is that it somehow uses that predicate to return a new list of the type `List[A]`, where `A` is the type in the list. + +Digging a little deeper, this part of `filter`’s description: + +```scala +p: (A) => Boolean +``` + +means that `filter` takes a function input parameter named `p`, and `p` must transform a generic input `A` into a `Boolean` value. Therefore, if a list holds the type `Int`, you can replace the generic type `A` with `Int`, and read that signature like this: + +```scala +p: (Int) => Boolean +``` + +Because `isEven` has this type — it transforms an input `Int` into a resulting `Boolean` — it can be used with `filter`. + + +### A lot of functionality with a little code + +The great thing about `filter` — and higher-order functions in general — is that they save you from writing a lot of custom code, typically custom `for` loops. For example, if `List` didn’t have a `filter` method, you’d have to write a custom method like this to do the same work: + +```scala +// what you’d have to do if `filter` didn’t exist +def getEvens(list: List[Int]): List[Int] = + val tmpArray = ArrayBuffer[Int]() + for + i <- list + if i % 2 == 0 + do + tmpArray += i + + // return this + tmpArray.toList + +val result = getEvens(aList) +``` + +Compare all of that imperative code to this equivalent functional code: + +```scala +val result = list.filter(_ % 2 == 0) +``` + +As you can see, this is a great advantage of functional programming. The code is much more concise, and it’s also easier to comprehend. + +In short, as they pertain to Scala’s collections classes, higher-order functions: + +- Are generally a replacement for custom traversal algorithms (such as `for` expressions) +- Are easier to read than custom traversal algorithms +- Mostly eliminate the need to write (or read) custom traversal algorithms + +As Martin Odersky wrote in his book, [Programming in Scala](https://www.amazon.com/Programming-Scala-Martin-Odersky/dp/098153161X/): + +>“You can use functions within your code to factor out common control patterns, and you can take advantage of higher-order functions in the Scala library to reuse control patterns that are common across all programmers’ code.” + + + +## Writing methods that take functions as parameters + +>Note: To make the following sections more clear, we’ll refer to the code you’re writing as a *method*, and the code you’re accepting as an input parameter as a *function*. + + +Given that background, let’s start writing methods that take functions as input parameters. + +To define a method that takes a function parameter, all you have to do is: + +1. In your method’s parameter list, define the signature of the function you want to accept +2. Use that function inside your method + +To demonstrate this, here’s a method that that takes an input parameter named `f`, where `f` is a function: + +```scala +def sayHello(f: () => Unit): Unit = f() +``` + +This portion of the code — the *type signature* — defines the types of functions the `sayHello` method will accept: + +```scala +f: () => Unit +``` + +Here’s how this works: + +- `f` is the name of the function input parameter. 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. +- Looking back at the body of the `sayHello` method, 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. For instance, the following function takes no input parameters and returns nothing, so it matches `f`’s type signature: + +```scala +def helloJoe(): Unit = println("Hello, Joe") +``` + +Because the type signatures match, you can pass `helloJoe` into `sayHello`: + +```scala +sayHello(helloJoe) // prints "Hello, Joe" +``` + +If you’ve never done this before, congratulations. You just defined a method named `sayHello` that takes another function as an input parameter, and then invokes that function when it’s called. + + +### sayHello can take many functions + +It’s important to know that the beauty of this approach is not that `sayHello` can take *one* function as an input parameter; the beauty is that it can take *any* function that matches `f`’s signature. For instance, because this next function takes no input parameters and returns nothing, it also works with `sayHello`: + +```scala +def bonjourJulien(): Unit = println("Bonjour, Julien") +``` + +Here it is in the REPL: + +```scala +scala> sayHello(bonjourJulien) +Bonjour, Julien +``` + +This is a good start. The only thing to do now is see a few more examples of how to define different type signatures for function parameters. + + +### The general syntax for defining function input parameters + +In this method: + +```scala +def sayHello(f: () => Unit) +``` + +The type signature for `f` is: + +```scala +f: () => Unit +``` + +We know that’s a function that takes no input parameters and returns nothing (given by `Unit`). + +To demonstrate more type signature examples, here’s a function that takes a `String` parameter and returns an `Int`: + +```scala +f: (String) => Int +``` + +That could be something like a string length or checksum function. + +Similarly, this function takes two `Int` parameters and return an `Int`: + +```scala +f: (Int, Int) => Int +``` + +As you can infer from these examples, the general syntax for defining function parameter type signatures is: + +```scala +variableName: (parameterTypes ...) => returnType +``` + +Going back to this type signature: + +```scala +f: (Int, Int) => Int +``` + +Can you imagine what sort of functions match that signature? + +The answer is that any function that takes two `Int` input parameters and returns an `Int` matches that signature, so all of these functions match that signature: + +```scala +def add(a: Int, b: Int): Int = a + b +def subtract(a: Int, b: Int): Int = a - b +def multiply(a: Int, b: Int): Int = a * b +``` + +>Because functional programming is like combining a series of algebraic equations, it’s common to think about types a *lot*. You might say that you “think in types.” + + +### Taking a function parameter along with other parameters + +So far the methods we’ve shown have only taken a function input parameter. But for a method like this to be really interesting, it must also have some data to work on. For a class like `List`, its `map` method already has data to work on: the data in the `List`. But for a standalone method that doesn’t have its own data, it should accept data as another input parameter. + +For instance, here’s a method named `executeNTimes` that has two input parameters: a function, and an `Int`: + +```scala +def executeNTimes(f: () => Unit, n: Int): Unit = + for i <- 1 to n do f() +``` + +As the code shows, `executeNTimes` executes the `f` function `n` times. Because `f` returns `Unit`, `executeNTimes` also returns `Unit`. To test `executeNTimes`, define a method that matches `f`’s signature: + +```scala +// a method of type `() => Unit` +def helloWorld(): Unit = println("Hello, world") +``` + +Then pass the method into `executeNTimes` along with an `Int`: + +```` +scala> executeNTimes(helloWorld, 3) +Hello, world +Hello, world +Hello, world +```` + +Excellent. The `executeNTimes` method executes the `helloWorld` function three times. + +Your methods can continue to get as complicated as necessary. For example, this method takes a function of type `(Int, Int) => Int`, along with two input parameters: + +```scala +def executeAndPrint(f: (Int, Int) => Int, i: Int, j: Int): Unit = + println(f(i, j)) +``` + +Again, because methods like these match that type signature, they can be passed into `executeAndPrint`: + +```scala +def sum(x: Int, y: Int) = x + y +def multiply(x: Int, y: Int) = x * y + +executeAndPrint(sum, 3, 11) // prints 14 +executeAndPrint(multiply, 3, 9) // prints 27 +``` + + +### Consistency in the use of function type signatures + +One of the great things about Scala is the consistency of the language. In this case, this means that the syntax you use to define function input parameters is the same syntax you use to write anonymous functions and function variables. + +For instance, if you were to write an anonymous function that calculates the sum of two integers, you’d write it like this: + +```scala +(Int, Int) => Int = (a, b) => a + b +``` + +That code consists of the type signature: + +```` +(Int, Int) => Int = (a, b) => a + b +----------------- +```` + +The input parameters: + +```` +(Int, Int) => Int = (a, b) => a + b + ------ +```` + +and the body of the function: + +```` +(Int, Int) => Int = (a, b) => a + b + ----- +```` + +The consistency in Scala is shown here, where this anonymous function type signature: + +```` +(Int, Int) => Int = (a, b) => a + b +----------------- +```` + +is the same as the type signature you use to define a function input parameter: + +```` +def executeAndPrint(f: (Int, Int) => Int, ... + ----------------- +```` + +Once you’re comfortable with this syntax, you’ll use it to define function parameters, anonymous functions, and function variables, and it becomes easier to read the Scaladoc for higher-order functions. + + +### How to write your own `map` method + +At this point you can now write your own methods that take function parameters. For instance, if the `List` class didn’t have its own `map` method, you could write your own. + +A good first step for this is to accurately state the problem. Focusing only on a `List[Int]`, you state: + +>I want to write a `map` method that can be used to apply other functions to each element in a `List[Int]` that it’s given, returning the transformed elements as a new list. + +Given that statement, you start to write the method signature. First, you know that you want to accept a function as a parameter, and that function should transform an `Int` into some generic type `A`, so you start to write: + +```scala +def map(f: (Int) => A +``` + +The syntax for using a generic type requires declaring that type symbol before the parameter list: + +```scala +def map[A](f: (Int) => A +``` + +Next, you know that you also want to accept a `List[Int]`: + +```scala +def map[A](f: (Int) => A, xs: List[Int] +``` + +You also know that `map` returns a transformed list of the generic type `A`: + +```scala +def map[A](f: (Int) => A, xs: List[Int]): List[A] = ??? +``` + +That takes care of the method signature. Now all you have to do is write the method body. A `map` method applies the function it’s given to every element in the list it’s given to produce a new, transformed list. One way to do this is with a `for` expression: + +```scala +for x <- xs yield f(x) +``` + +Putting this together with the method signature, you now have a standalone `map` method that works with a `List[Int]`: + +```scala +def map[A](f: (Int) => A, xs: List[Int]): List[A] = + for x <- xs yield f(x) +``` + +As a bonus, because the method body doesn’t care about the type inside the `List`, you can replace `Int` in the type signature with a generic type parameter: + +```scala +def map[A,B](f: (B) => A, xs: List[B]): List[A] = + for x <- xs yield f(x) +``` + +Now you have a `map` method that works with any `List`. + +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) + +def strlen(s: String) = s.length +map(strlen, List("a", "bb", "ccc")) // List(1, 2, 3) +``` + +Now that you’ve seen how to write methods that accept functions as parameters, let’s look at methods that return functions. + + +## Writing a method that returns a function + +Thanks to Scala’s consistency, writing a method that returns a function is similar to everything you just saw. For instance, to define a “greeting” method that returns a function, start by defining an anonymous function: + +```scala +(name: String) => println(s"Hello, $name") +``` + +This anonymous function takes a `String` input parameter and returns nothing, so it has this type: + +```scala +String => Unit +``` + +Therefore, if you want to write a method that returns this function, first define the method signature: + +```scala +def greet(): String => Unit = ??? +``` + +Now you just need to add in the method body. In this case the method returns that anonymous function, so the complete method looks like this: + +```scala +// a method that returns a function +def greet(): String => Unit = + (name: String) => println(s"Hello, $name") +``` + +Because that method returns a function, you first create your new function: + +```scala +val greetFunction = greet() +``` + +When you put that line of code in the REPL, you see that it has the type `String => Unit`, as expected: + +```` +scala> val greetFunction = greet() +val greetFunction: String => Unit = Lambda.... + ----------------------------- +```` + +Now you can call your new `greetFunction`: + +```scala +greetFunction("Joe") // prints "Hello, Joe" +``` + +Congratulations, you just created a method that returns a function, and then executed that function. + + +### Improving the method + +That function would be more useful if you could pass in a greeting, so let’s do that. All you have to do is pass the greeting in as a parameter to the `greet` method, and use it in the string inside `println`: + +```scala +def greet(theGreeting: String): String => Unit = + (name: String) => println(s"$theGreeting, $name") +``` + +Now when you call your method, the process is more flexible because you can change the greeting. Again, the first step of creating a function from the method is good to show in the REPL because it shows the resulting type: + +```` +scala> val sayHello = greet("Hello") +val sayHello: String => Unit = Lambda..... + ------------------------ +```` + +This shows that `sayHello` is a function that takes a `String` input parameter and returns `Unit` (nothing). So now when you give `sayHello` a `String`, it prints the greeting: + +```scala +sayHello("Joe") // prints "Hello, Joe" +``` + +You can also change the greeting and create a new function, as desired: + +```scala +val sayHey = greet("Hey") +sayHey("Joe") // prints "Hey, Joe" +``` + + + +### A more real-world example + +In a more real-world example, a method like this is more useful when it can return one of many possible functions, like a factory that returns custom-built functions. + +For instance, imagine that you want to write a method that returns functions that greet people in different languages. We’ll limit this to functions that greet in English or French, depending on a parameter that’s passed into the method. + +A first thing you know is that you want to create a method that (a) takes a “desired language” as an input, and (b) returns a function as its result. Furthermore, because that function will print a string that it’s given, you know it has the type `String => Unit`. With that information you can start writing the method signature like this: + +```scala +def createGreetingFunction(desiredLanguage: String): String => Unit = ??? +``` + +That’s a good start. Because you know that the possible functions you’ll return take a string and print it, you can write two anonymous functions like this for the English and French languages: + +```scala +(name: String) => println(s"Hello, $name") +(name: String) => println(s"Bonjour, $name") +``` + +Inside a method it might be a little more readable if you give those anonymous functions some names, so let’s write them like this instead: + +```scala +val englishGreeting = (name: String) => println(s"Hello, $name") +val frenchGreeting = (name: String) => println(s"Bonjour, $name") +``` + +Now all you need to do is (a) return `englishGreeting` if the `desiredLanguage` is English, and (b) return `frenchGreeting` if the `desiredLanguage` is French. One way to do that is with a `match` expression: + +```scala +def createGreetingFunction(desiredLanguage: String): String => Unit = + val englishGreeting = (name: String) => println(s"Hello, $name") + val frenchGreeting = (name: String) => println(s"Bonjour, $name") + desiredLanguage match + case "english" => englishGreeting + case "french" => frenchGreeting +``` + +And that’s the final method. Notice that returning a function value from a method is no different than returning a string or integer value. + +This is how `createGreetingFunction` builds a French-greeting function: + +```scala +val greetInFrench = createGreetingFunction("french") +greetInFrench("Raphael") // prints "Bonjour, Raphael" +``` + +And this is how it builds an English-greeting function: + +```scala +val greetInEnglish = createGreetingFunction("english") +greetInEnglish("Joe") // prints "Hello, Joe" +``` + +If this all makes sense — congratulations — you’ve just seen how to write methods that return functions. + + + +## Summary + +A higher-order function is often defined as a function that takes other functions as input parameters or returns a function as a result. In Scala this is possible because functions are first-class values. + +In previous chapters you saw how to be a *consumer* of higher-order functions, and this chapter showed you how to be a *creator* of higher-order functions. Specifically, you saw: + +- How to write methods that take functions as input parameters +- How to return a function from a method + +A beneficial side effect of this chapter is that you saw many examples of how to declare type signatures for functions. The benefits of that are that you’ll use that same syntax to define function parameters, anonymous functions, and function variables, and it also becomes easier to read the Scaladoc for higher-order functions like `map`, `filter`, and others. + + + diff --git a/_overviews/overview/main-methods.md b/_overviews/overview/main-methods.md new file mode 100644 index 0000000000..a32f6d7920 --- /dev/null +++ b/_overviews/overview/main-methods.md @@ -0,0 +1,136 @@ +--- +title: Main Methods +description: This page describes how 'main' methods and the '@main' annotation work in Scala 3. +--- + + + +Scala 3 offers a new way to define programs that can be invoked from the command line: Adding an `@main` annotation to a method turns that method into an executable program: + +```tut +@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`: + +```sh +$ scalac Hello.scala +``` + +Then run it with `scala`: + +```sh +$ scala hello +Hello, world +``` + +An `@main` annotated method can be written either at the top-level (as shown), or inside a statically accessible object. In either case, the name of the program is in each case the name of the method, without any object prefixes. + + + +### Command line arguments + +With this approach your `@main` method can handle command line arguments, and those arguments can have different types. For example, given this `@main` method that takes an `Int`, a `String`, and a varargs `String*` parameter: + +```scala +@main def happyBirthday(age: Int, name: String, others: String*) = { + val suffix = + (age % 100) match { + case 11 | 12 | 13 => "th" + case _ => + (age % 10) match { + case 1 => "st" + case 2 => "nd" + case 3 => "rd" + case _ => "th" + } + } + val bldr = new StringBuilder(s"Happy $age$suffix birthday, $name") + for other <- others do bldr.append(" and ").append(other) + bldr.toString +} +``` + +When you compile that code, it creates a main program named `happyBirthday` that’s called like this: + +``` +$ scala happyBirthday 23 Lisa Peter +Happy 23rd Birthday, Lisa and Peter! +``` + +As shown, the `@main` method can have an arbitrary number of parameters. For each parameter type there must be an instance of the *scala.util.FromString* type class that converts an argument `String` to the required parameter type. Also as shown, a main method’s parameter list can end in a repeated parameter like `String*` that takes all remaining arguments given on the command line. + + +The program implemented from an `@main` method checks that there are enough arguments on the command line to fill in all parameters, and that the argument strings can be converted to the required types. If a check fails, the program is terminated with an error message: + +``` +$ scala happyBirthday 22 +Illegal command line after first argument: more arguments expected + +$ scala happyBirthday sixty Fred +Illegal command line: java.lang.NumberFormatException: For input string: "sixty" +``` + + + + +## The details + +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 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: + +```scala +final class happyBirthday { + import scala.util.{CommandLineParser => CLP} + def main(args: Array[String]): Unit = + try + happyBirthday( + CLP.parseArgument[Int](args, 0), + CLP.parseArgument[String](args, 1), + CLP.parseRemainingArguments[String](args, 2)) + catch { + case error: CLP.ParseError => CLP.showError(error) + } +} +``` + +>**Note**: In this generated code, the `` modifier expresses that the `main` method is generated as a static method of class `happyBirthday`. This feature is not available for user programs in Scala. Regular “static” members are generated in Scala using objects instead. + + + +## Scala 3 compared to Scala 2 + +`@main` methods are the recommended way to generate programs that can be invoked from the command line in Scala 3. They replace the previous approach in Scala 2, which was to create an `object` that extends the `App` class: + +```scala +// scala 2 +object happyBirthday extends App: + // needs by-hand parsing of the command line arguments ... +``` + +The previous functionality of `App`, which relied on the “magic” `DelayedInit` trait, is no longer available. `App` still exists in limited form for now, but it doesn’t support command line arguments and will be deprecated in the future. + +If programs need to cross-build between Scala 2 and Scala 3, it’s recommended to use an explicit `main` method with an `Array[String]` argument instead: + +```scala +object happyBirthday: + def main(args: Array[String]) = println("Hello, world") +``` + +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: + +```sh +$ scalac happyBirthday.scala + +$ scala happyBirthday +Hello, world +``` + + + + From 9ac0fb79f15685cc8eb20f4ae1b2611382364a1d Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Sat, 31 Oct 2020 22:38:30 -0600 Subject: [PATCH 025/169] =?UTF-8?q?Initial=20version=20of=20the=20?= =?UTF-8?q?=E2=80=98Packaging=20and=20Imports=E2=80=99=20document.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/packaging-imports.md | 378 +++++++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 _overviews/overview/packaging-imports.md diff --git a/_overviews/overview/packaging-imports.md b/_overviews/overview/packaging-imports.md new file mode 100644 index 0000000000..4785a4aafc --- /dev/null +++ b/_overviews/overview/packaging-imports.md @@ -0,0 +1,378 @@ +--- +title: Packaging and Imports +description: A discussion of using packages and imports to organize your code, build related modules of code, control scope, and help prevent namespace collisions. +--- + + + +Scala uses *packages* to create namespaces that let you modularize programs and help prevent namespace collisions. Scala supports the package-naming style used by Java, and also the “curly brace” namespace notation used by languages like C++ and C#. + +The Scala approach to importing members is also similar to Java, and more flexible. With Scala you can: + +- Import packages, classes, objects, traits, and methods +- Place import statements anywhere +- Hide and rename members when you import them + +These features are demonstrated in the following examples. + + + +## Creating a package + +Packages are created by declaring one or more package names at the top of a Scala file. For example, when your domain name is _acme.com_ and you’re working in the _model_ package of an application named _myapp_, your package declaration looks like this: + +```scala +package com.acme.myapp.model + +class Person ... +``` + +By convention, package names should be all lower case, and the formal naming convention is *...*. + +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 + +The other way to declare packages in Scala is by using the curly brace namespace notation used in languages like C, C++, and C#: + +```tut +package users { + package administrators { + class AdminUser + } + package normalusers { + class NormalUser + } +} +``` + +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. + + + + +## Import statements, Part 1 + +Import statements are used to access entities in other packages. Import statements fall into two main categories: + +- Importing classes, traits, objects, functions, and methods +- Importing `given` clauses + +If you’re used to a language like Java, the first class of import statements is similar to what Java uses, with a slightly different syntax that allows for more flexibility. These examples demonstrate some of that flexibility: + +```` +import users._ // import everything from the `users` package +import users.User // import only the `User` class +import users.{User, UserPreferences} // only import the selected members +import users.{UserPreferences => UPrefs} // rename a member as you import it +```` + +Those examples are meant to give you a taste of how the first class of `import` statements work. They’re explained more in the subsections that follow. + +Import statements are also used to import `given` instances into scope. Those are discussed at the end of this chapter. + +Two notes before moving on: + +- Import clauses are not required for accessing members of the same package. +- When the `_` character is used in Scala import statements, it’s similar to the `*` character in Java. One reason for this difference is that in Scala, the `*` character can be used for method names. + + +### Importing one or more members + +In Scala you can import one member from a package like this: + +```scala +import java.io.File +``` + +and multiple members like this: + +```scala +import java.io.File +import java.io.IOException +import java.io.FileNotFoundException +``` + +When importing multiple members, you can import them more concisely like this: + +```scala +import java.io.{File, IOException, FileNotFoundException} +``` + +When you want to import everything from the *java.io* package, use this syntax: + +```scala +import java.io._ +``` + + +### Renaming members on import + +Sometimes it can help to rename entities when you import them to avoid name collisions. For instance, if you want to use the Scala `List` class and also the *java.util.List* class at the same time, you can rename the *java.util.List* class when you import it: + +```scala +import java.util.{List => JavaList} +``` + +Now you use the name `JavaList` to refer to that class, and use `List` to refer to the Scala list class. + +You can also rename multiple members at one time using this syntax: + +```scala +import java.util.{Date => JDate, HashMap => JHashMap, _} +``` + +That line of code says, “Rename the `Date` and `HashMap` classes as shown, and import everything else in the _java.util_ package without renaming any other members.” + + +### Hiding members on import + +You can also *hide* members during the import process. This `import` statement hides the *java.util.Random* class, while importing everything else in the *java.util* package: + +```scala +import java.util.{Random => _, _} +``` + +If you try to access the `Random` class it won’t work, but you can access all other members from that package: + +```scala +val r = new Random // won’t compile +new ArrayList // works +``` + +#### Hiding multiple members + +To hide multiple members during the import process, list them before using the final wildcard import: + +```scala +scala> import java.util.{List => _, Map => _, Set => _, _} +import java.util.{List=>_, Map=>_, Set=>_, _} +``` + +Once again those classes are hidden, but you can use all other classes in *java.util*: + +```scala +scala> new ArrayList[String] +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) +val a: List[Int] = List(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) +val c: Map[Int, Int] = Map(1 -> 1, 2 -> 2) +``` + + +### Use imports anywhere + +In Scala, `import` statements can be anywhere. They can be used at the top of a source code file: + +```scala +package foo + +import scala.util.Random + +class ClassA: + def printRandom: + val r = new Random // use the imported class + // more code here... +``` + +You can also use `import` statements closer to the point where they are needed, if you prefer: + +```scala +package foo + +class ClassA: + import scala.util.Random // inside ClassA + def printRandom { + val r = new Random + // more code here... + +class ClassB: + // the Random class is not visible here + val r = new Random // this code will not compile +``` + + +### “Static” imports + +When you want to import members in a way similar to the Java “static import” approach — so you can refer to the member names directly, without having to prefix them with their class name — use the following approach. + +Use this syntax to import all static members of the Java `Math` class: + +```scala +import java.lang.Math._ +``` + +Now you can access static `Math` class methods like `sin` and `cos` without having to precede them with the class name: + +```scala +import java.lang.Math._ + +val a = sin(0) // 0.0 +val b = cos(PI) // -1.0 +``` + + +### Packages imported by default + +Two packages are implicitly imported into the scope of all of your source code files: + +- java.lang._ +- scala._ + +The Scala `Predef` object is 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. + + + +### Handling naming conflicts + +In the rare event there’s a naming conflict and you need to import something from the root of the project, prefix the package name with `_root_`: + +``` +package accounts + +import _root_.users._ +``` + + + +## Importing `given` instances + + +As you’ll see in the Contextual Abstractions chapter, a special form of the `import` statement is used to import `given` instances. The basic form is shown in this example: + + +```tut +object A: + class TC + given tc as TC + def f(using TC) = ??? + +object B: + import A._ // import all non-given members + import A.given // import the given instance +``` + +In this code, the `import A._` clause of object `B` imports all members of `A` *except* the `given` instance `tc`. Conversely, the second import, `import A.given`, imports *only* that `given` instance. The two `import` clauses can also be merged into one: + +```scala +object B: + import A.{given, _} +``` + +### Discussion + +The wildcard selector `_` brings all definitions other than givens or extensions into scope, whereas a `given` selector brings all *givens* — including those resulting from extensions — into scope. + +These rules have two main benefits: + +- It’s more clear where givens in scope are coming from. In particular, it’s not possible to hide imported givens in a long list of other wildcard imports. +- It enables importing all givens without importing anything else. This is particularly important since givens can be anonymous, so the usual use of named imports is not practical. + + +### By-type imports + +Since givens can be anonymous, it’s not always practical to import them by their name, and wildcard imports are typically used instead. *By-type imports* provide a more specific alternative to wildcard imports, which makes it more clear what is imported: + +```scala +import A.{given TC} +``` + +This imports any `given` in `A` that has a type which conforms to `TC`. Importing givens of several types `T1,...,Tn` is expressed by multiple `given` selectors: + +```scala +import A.{given T1, ..., given Tn} +``` + +Importing all `given` instances of a parameterized type is expressed by wildcard arguments. For example, when you have this `object`: + +```scala +object Instances: + given intOrd as Ordering[Int] + given listOrd[T: Ordering] as Ordering[List[T]] + given ec as ExecutionContext = ... + given im as Monoid[Int] +``` + +This import statement imports the `intOrd`, `listOrd`, and `ec` instances, but leaves out the `im` instance because it doesn’t fit any of the specified bounds: + +```scala +import Instances.{given Ordering[?], given ExecutionContext} +``` + +By-type imports can be mixed with by-name imports. If both are present in an import clause, by-type imports come last. For instance, this import clause imports `im`, `intOrd`, and `listOrd`, but leaves out `ec`: + +```scala +import Instances.{im, given Ordering[?]} +``` + + +### An example + +As a concrete example, imagine that you have this `MonthConversions` object that contains two `given` definitions: + +```scala +object MonthConversions: + trait MonthConverter[A]: + def convert(a: A): String + + given intMonthConverter as MonthConverter[Int]: + def convert(i: Int): String = + i match + case 1 => "January" + case 2 => "February" + // more cases here ... + + given stringMonthConverter as MonthConverter[String]: + def convert(s: String): String = + s match + case "jan" => "January" + case "feb" => "February" + // more cases here ... +} +``` + +To import those givens into the current scope, use these two `import` statements: + +```scala +import MonthConversions._ +import MonthConversions.{given MonthConverter[?]} +``` + +Now you can create a method that uses those `given` instances: + +```scala +def genericMonthConverter[A](a: A)(using monthConverter: MonthConverter[A]): String = + monthConverter.convert(a) +``` + +Then you can use that method in your application: + +```scala +@main def main = + println(genericMonthConverter(1)) // January + println(genericMonthConverter("jan")) // January +``` + +As mentioned, one of the key design benefits of the “import given” syntax is to make it clear where givens in scope come from, and it’s clear in these `import` statements that the givens come from the `MonthConversions` object. + + + +## Summary + +While that covers almost all of the features of packing and imports, see the Reference documentation for a few more details and features. + + + From 8f3c91e837013c66bd530ca1fa14541792b358bd Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Sat, 31 Oct 2020 22:39:06 -0600 Subject: [PATCH 026/169] =?UTF-8?q?Renamed=20the=20=E2=80=98Packaging=20an?= =?UTF-8?q?d=20Imports=E2=80=99=20document.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/packaging-imports-exports.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 _overviews/overview/packaging-imports-exports.md diff --git a/_overviews/overview/packaging-imports-exports.md b/_overviews/overview/packaging-imports-exports.md deleted file mode 100644 index e69de29bb2..0000000000 From a65106b6842313159095d988432ccec42196500a Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Sun, 1 Nov 2020 21:14:37 -0700 Subject: [PATCH 027/169] =?UTF-8?q?Didn=E2=80=99t=20make=20this=20shorter,?= =?UTF-8?q?=20but=20improved=20some=20areas.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/higher-order-functions.md | 186 +++++++++--------- 1 file changed, 95 insertions(+), 91 deletions(-) diff --git a/_overviews/overview/higher-order-functions.md b/_overviews/overview/higher-order-functions.md index cb427b3b69..eec8bbab99 100644 --- a/_overviews/overview/higher-order-functions.md +++ b/_overviews/overview/higher-order-functions.md @@ -3,15 +3,15 @@ title: Higher-order Functions description: This page demonstrates how to create and use higher-order functions in Scala. --- - -A higher-order function is often defined as a function that (a) takes other functions as input parameters or (b) returns a function as a result. In Scala this is possible because functions are first-class values. While we use the term “higher-order function,” in Scala this phrase is used for both *methods* and *functions* because they can generally be used in the same places. +A higher-order function is often defined as a function that (a) takes other functions as input parameters or (b) returns a function as a result. In Scala, higher-order functions are possible because functions are first-class values. + +As an important point, while we use the common industry term “higher-order function,” in Scala this phrase applies to both *methods* and *functions* because — thanks to [Eta Expansion](TODO:link) — they can generally be used in the same places. + ->The ability to pass functions around as values enables a form of “power” programming that lets you write code that’s concise and still readable. +### From consumer to creator -In the examples so far you’ve seen how to be the *consumer* of functions that take other functions as input parameters, such as higher-order functions like `map` and `filter`. In this chapter you’ll see how to be the *creator* of higher-order functions, including: +In the examples so far in this Overview, you’ve seen how to be a *consumer* of methods that take other functions as input parameters, such as using higher-order functions like `map` and `filter`. In this chapter you’ll see how to be a *creator* of higher-order functions, including: - How to write methods that take functions as input parameters - How to return a function from a method @@ -19,10 +19,9 @@ In the examples so far you’ve seen how to be the *consumer* of functions that In the process you’ll see: - The syntax you use to define function input parameters -- Multiple examples of that syntax - How to call a function once you have a reference to it -As a beneficial side effect of this chapter, once you’re comfortable with the syntax, you’ll use it to define function parameters, anonymous functions, and function variables, and it also becomes easier to read the Scaladoc for higher-order functions. +As a beneficial side effect of this chapter, once you’re comfortable with this syntax, you’ll use it to define function parameters, anonymous functions, and function variables, and it also becomes easier to read the Scaladoc for higher-order functions. @@ -30,38 +29,39 @@ As a beneficial side effect of this chapter, once you’re comfortable with the As a quick recap, in this Overview you’ve already seen examples of how to use existing higher-order functions in the Scala collections classes, including `map` and `filter`: -```scala +```tut List(1,2,3).map(_ * 10) // List(10,20,30) def isEven(i: Int) = i % 2 == 0 Vector(1,2,3,4).filter(isEven) // Vector(2,4) ``` -A few key points of the `filter` example are: - -- `filter` accepts a function, anonymous function, or method as an input parameter. -- The functions you pass into `filter` must match the type signature that `filter` expects — in this example, a function that takes an `Int` input parameter and returns a `Boolean`. +A few key points about these examples: ->Although we use the term “function,” `filter` can accept a *method* as a parameter thanks to Scala’s [Eta Expansion](TODO:LINK) technology. +- The functions you pass into them must match their expected type signature +- They take a function, anonymous function, or method as an input parameter +- [Eta Expansion](TODO:link) is the technology that enables you to pass a method into a place where a function is expected ### Understanding `filter`’s Scaladoc -You can understand the type of functions `filter` accepts by looking at its Scaladoc. For example here’s the `filter` definition on the `List` class: +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: ```scala def filter(p: (A) => Boolean): List[A] ``` -This states that `filter` is a method that takes a predicate parameter named `p` and returns a `List[A]`. (A *predicate* is just a function that returns a `Boolean` value.) Without looking at the internal details of `filter`, all we know is that it somehow uses that predicate to return a new list of the type `List[A]`, where `A` is the type in the list. +This states that `filter` is a method that takes a function parameter named `p`. By convention, `p` stands for a *predicate*, which is just a function that returns a `Boolean` value. So `filter` takes `p` as an input parameter, and returns a `List[A]`, where `A` the type held in the list. If you call `filter` on a `List[Int]`, `A` is the type `Int`. -Digging a little deeper, this part of `filter`’s description: +If you didn’t know how `filter` works, all you’d know is that it somehow uses the predicate to create and return the `List[A]`. + +Digging into the function parameter, this part of `filter`’s description: ```scala p: (A) => Boolean ``` -means that `filter` takes a function input parameter named `p`, and `p` must transform a generic input `A` into a `Boolean` value. Therefore, if a list holds the type `Int`, you can replace the generic type `A` with `Int`, and read that signature like this: +means that whatever function you pass in must take the type `A` as an input parameter and return a `Boolean`. So if your list is a `List[Int]`, you can replace the generic type `A` with `Int`, and read that signature like this: ```scala p: (Int) => Boolean @@ -70,6 +70,7 @@ p: (Int) => Boolean Because `isEven` has this type — it transforms an input `Int` into a resulting `Boolean` — it can be used with `filter`. + ## Writing methods that take functions as parameters ->Note: To make the following sections more clear, we’ll refer to the code you’re writing as a *method*, and the code you’re accepting as an input parameter as a *function*. - +**Note:** To make the following sections more clear, we’ll refer to the code you’re writing as a *method*, and the code you’re accepting as an input parameter as a *function*. Given that background, let’s start writing methods that take functions as input parameters. -To define a method that takes a function parameter, all you have to do is: +To create a method that uses a function parameter, all you have to do is: 1. In your method’s parameter list, define the signature of the function you want to accept 2. Use that function inside your method To demonstrate this, here’s a method that that takes an input parameter named `f`, where `f` is a function: -```scala +```tut def sayHello(f: () => Unit): Unit = f() ``` -This portion of the code — the *type signature* — defines the types of functions the `sayHello` method will accept: +This portion of the code — the *type signature* — states that `f` is a function, and defines the types of functions the `sayHello` method will accept: ```scala f: () => Unit @@ -140,11 +140,11 @@ Here’s how this works: - 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. -- Looking back at the body of the `sayHello` method, the `f()` statement there invokes the function that’s passed in. +- 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. For instance, the following function takes no input parameters and returns nothing, so it matches `f`’s type signature: +Now that we’ve defined `sayHello`, let’s create a function to match `f`’s signature so we can test it. The following function takes no input parameters and returns nothing, so it matches `f`’s type signature: -```scala +```tut def helloJoe(): Unit = println("Hello, Joe") ``` @@ -154,23 +154,23 @@ Because the type signatures match, you can pass `helloJoe` into `sayHello`: sayHello(helloJoe) // prints "Hello, Joe" ``` -If you’ve never done this before, congratulations. You just defined a method named `sayHello` that takes another function as an input parameter, and then invokes that function when it’s called. +If you’ve never done this before, congratulations. You just defined a method named `sayHello` that takes a function as an input parameter, and then invokes that function in its method body. ### sayHello can take many functions It’s important to know that the beauty of this approach is not that `sayHello` can take *one* function as an input parameter; the beauty is that it can take *any* function that matches `f`’s signature. For instance, because this next function takes no input parameters and returns nothing, it also works with `sayHello`: -```scala +```tut def bonjourJulien(): Unit = println("Bonjour, Julien") ``` Here it is in the REPL: -```scala +```` scala> sayHello(bonjourJulien) Bonjour, Julien -``` +```` This is a good start. The only thing to do now is see a few more examples of how to define different type signatures for function parameters. @@ -186,10 +186,10 @@ def sayHello(f: () => Unit) The type signature for `f` is: ```scala -f: () => Unit +() => Unit ``` -We know that’s 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 (given by `Unit`).” To demonstrate more type signature examples, here’s a function that takes a `String` parameter and returns an `Int`: @@ -197,7 +197,7 @@ To demonstrate more type signature examples, here’s a function that takes a `S f: (String) => Int ``` -That could be something like a string length or checksum function. +What kinds of functions take a string and return an integer? Functions like “string length” and checksum are two examples. Similarly, this function takes two `Int` parameters and return an `Int`: @@ -214,36 +214,36 @@ variableName: (parameterTypes ...) => returnType Going back to this type signature: ```scala -f: (Int, Int) => Int +(Int, Int) => Int ``` Can you imagine what sort of functions match that signature? -The answer is that any function that takes two `Int` input parameters and returns an `Int` matches that signature, so all of these functions match that signature: +The answer is that any function that takes two `Int` input parameters and returns an `Int` matches that signature, so all of these functions are a match: -```scala +```tut def add(a: Int, b: Int): Int = a + b def subtract(a: Int, b: Int): Int = a - b def multiply(a: Int, b: Int): Int = a * b ``` ->Because functional programming is like combining a series of algebraic equations, it’s common to think about types a *lot*. You might say that you “think in types.” +>Because functional programming is like creating and combining a series of algebraic equations, it’s common to think about types a *lot* when designing functions and applications. You might say that you “think in types.” ### Taking a function parameter along with other parameters -So far the methods we’ve shown have only taken a function input parameter. But for a method like this to be really interesting, it must also have some data to work on. For a class like `List`, its `map` method already has data to work on: the data in the `List`. But for a standalone method that doesn’t have its own data, it should accept data as another input parameter. +For higher-order functions to be really useful, they also need some data to work on. For a class like `List`, its `map` method already has data to work on: the data in the `List`. But for a standalone higher-order function that doesn’t have its own data, it should also accept data as other input parameters. For instance, here’s a method named `executeNTimes` that has two input parameters: a function, and an `Int`: -```scala +```tut def executeNTimes(f: () => Unit, n: Int): Unit = for i <- 1 to n do f() ``` -As the code shows, `executeNTimes` executes the `f` function `n` times. Because `f` returns `Unit`, `executeNTimes` also returns `Unit`. To test `executeNTimes`, define a method that matches `f`’s signature: +As the code shows, `executeNTimes` executes the `f` function `n` times. Because a simple `for` loop like this has no return value, `executeNTimes` returns `Unit`. To test `executeNTimes`, define a method that matches `f`’s signature: -```scala +```tut // a method of type `() => Unit` def helloWorld(): Unit = println("Hello, world") ``` @@ -261,12 +261,12 @@ Excellent. The `executeNTimes` method executes the `helloWorld` function three t Your methods can continue to get as complicated as necessary. For example, this method takes a function of type `(Int, Int) => Int`, along with two input parameters: -```scala +```tut def executeAndPrint(f: (Int, Int) => Int, i: Int, j: Int): Unit = println(f(i, j)) ``` -Again, because methods like these match that type signature, they can be passed into `executeAndPrint`: +Because these `sum` and `multiply` methods match that type signature, they can be passed into `executeAndPrint`: ```scala def sum(x: Int, y: Int) = x + y @@ -279,7 +279,7 @@ executeAndPrint(multiply, 3, 9) // prints 27 ### Consistency in the use of function type signatures -One of the great things about Scala is the consistency of the language. In this case, this means that the syntax you use to define function input parameters is the same syntax you use to write anonymous functions and function variables. +Scala’s syntax is very consistent. In this case, this means that the syntax you use to define function input parameters is the same syntax you use to write anonymous functions and function variables. For instance, if you were to write an anonymous function that calculates the sum of two integers, you’d write it like this: @@ -308,7 +308,7 @@ and the body of the function: ----- ```` -The consistency in Scala is shown here, where this anonymous function type signature: +Scala’s consistency is shown here, where this anonymous function type signature: ```` (Int, Int) => Int = (a, b) => a + b @@ -327,31 +327,33 @@ Once you’re comfortable with this syntax, you’ll use it to define function p ### How to write your own `map` method -At this point you can now write your own methods that take function parameters. For instance, if the `List` class didn’t have its own `map` method, you could write your own. +Now that you’ve seen how to write your own higher-order functions, let’s take a quick look at a more real-world example. + +Imagine for a moment that the `List` class doesn’t have its own `map` method, and you want to write your own. -A good first step for this is to accurately state the problem. Focusing only on a `List[Int]`, you state: +A good first step when creating functions is to accurately state the problem. Focusing only on a `List[Int]`, you state: ->I want to write a `map` method that can be used to apply other functions to each element in a `List[Int]` that it’s given, returning the transformed elements as a new list. +>I want to write a `map` method that can be used to a function to each element in a `List[Int]` that it’s given, returning the transformed elements as a new list. -Given that statement, you start to write the method signature. First, you know that you want to accept a function as a parameter, and that function should transform an `Int` into some generic type `A`, so you start to write: +Given that statement, you start to write the method signature. First, you know that you want to accept a function as a parameter, and that function should transform an `Int` into some generic type `A`, so you write: ```scala def map(f: (Int) => A ``` -The syntax for using a generic type requires declaring that type symbol before the parameter list: +The syntax for using a generic type requires declaring that type symbol before the parameter list, so you add that: ```scala def map[A](f: (Int) => A ``` -Next, you know that you also want to accept a `List[Int]`: +Next, you know that `map` should also accept a `List[Int]`: ```scala -def map[A](f: (Int) => A, xs: List[Int] +def map[A](f: (Int) => A, xs: List[Int]) ``` -You also know that `map` returns a transformed list of the generic type `A`: +Finally, you also know that `map` returns a transformed `List` that contains elements of the generic type `A`: ```scala def map[A](f: (Int) => A, xs: List[Int]): List[A] = ??? @@ -363,16 +365,18 @@ That takes care of the method signature. Now all you have to do is write the met for x <- xs yield f(x) ``` -Putting this together with the method signature, you now have a standalone `map` method that works with a `List[Int]`: +As with this example, `for` expressions often make code surprisingly simple, and that ends up being the entire method body. -```scala +Putting it together with the method signature, you now have a standalone `map` method that works with a `List[Int]`: + +```tut def map[A](f: (Int) => A, xs: List[Int]): List[A] = for x <- xs yield f(x) ``` -As a bonus, because the method body doesn’t care about the type inside the `List`, you can replace `Int` in the type signature with a generic type parameter: +As a bonus, notice that the `for` expression doesn’t do anything that depends on the type inside the `List` being `Int`. Therefore, you can replace `Int` in the type signature with a generic type parameter: -```scala +```tut def map[A,B](f: (B) => A, xs: List[B]): List[A] = for x <- xs yield f(x) ``` @@ -383,50 +387,49 @@ 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) ``` -Now that you’ve seen how to write methods that accept functions as parameters, let’s look at methods that return functions. +Now that you’ve seen how to write methods that accept functions as input parameters, let’s look at methods that return functions. ## Writing a method that returns a function -Thanks to Scala’s consistency, writing a method that returns a function is similar to everything you just saw. For instance, to define a “greeting” method that returns a function, start by defining an anonymous function: +Thanks to Scala’s consistency, writing a method that returns a function is similar to everything you just saw. For example, imagine that you want to write a `greet` method that returns a function. The statement of what we want to build goes like this: -```scala -(name: String) => println(s"Hello, $name") -``` +>I want to create a `greet` method that returns a function. That function will take a string parameter and print it using `println`. To simplify this first example, `greet` won’t take any input parameters; it will just build a function and return it. -This anonymous function takes a `String` input parameter and returns nothing, so it has this type: +Given that statement, you can start building `greet`. You know it’s going to be a method: ```scala -String => Unit +def greet() ``` -Therefore, if you want to write a method that returns this function, first define the method signature: +You also know this method will return a function that (a) takes a `String` parameter and (b) prints that string using `println`. Therefore that function has the type, `String => Unit`: ```scala def greet(): String => Unit = ??? + ---------------- ``` -Now you just need to add in the method body. In this case the method returns that anonymous function, so the complete method looks like this: +Now you just need a method body. You know that the method needs to return a function, and that function takes a `String` and prints it. Therefore, this anonymous function should do: ```scala -// a method that returns a function -def greet(): String => Unit = - (name: String) => println(s"Hello, $name") +(name: String) => println(s"Hello, $name") ``` -Because that method returns a function, you first create your new function: +Now you just return that function from the method: -```scala -val greetFunction = greet() +```tut +// a method that returns a function +def greet(): String => Unit = + (name: String) => println(s"Hello, $name") ``` -When you put that line of code in the REPL, you see that it has the type `String => Unit`, as expected: +Because this method returns a function, you get the function by calling `greet()`. This is a good step to do in the REPL because it verifies the type of the new function: ```` scala> val greetFunction = greet() @@ -434,7 +437,7 @@ val greetFunction: String => Unit = Lambda.... ----------------------------- ```` -Now you can call your new `greetFunction`: +Now you can call `greetFunction`: ```scala greetFunction("Joe") // prints "Hello, Joe" @@ -445,14 +448,14 @@ Congratulations, you just created a method that returns a function, and then exe ### Improving the method -That function would be more useful if you could pass in a greeting, so let’s do that. All you have to do is pass the greeting in as a parameter to the `greet` method, and use it in the string inside `println`: +Our method would be more useful if you could pass in a greeting, so let’s do that. All you have to do is pass the greeting in as a parameter to the `greet` method, and use it in the string inside `println`: -```scala +```tut def greet(theGreeting: String): String => Unit = (name: String) => println(s"$theGreeting, $name") ``` -Now when you call your method, the process is more flexible because you can change the greeting. Again, the first step of creating a function from the method is good to show in the REPL because it shows the resulting type: +Now when you call your method, the process is more flexible because you can change the greeting. This is what it looks like when you create a function from this method: ```` scala> val sayHello = greet("Hello") @@ -460,7 +463,7 @@ val sayHello: String => Unit = Lambda..... ------------------------ ```` -This shows that `sayHello` is a function that takes a `String` input parameter and returns `Unit` (nothing). So now when you give `sayHello` a `String`, it prints the greeting: +The REPL type signature output shows that `sayHello` is a function that takes a `String` input parameter and returns `Unit` (nothing). So now when you give `sayHello` a `String`, it prints the greeting: ```scala sayHello("Joe") // prints "Hello, Joe" @@ -477,24 +480,24 @@ sayHey("Joe") // prints "Hey, Joe" ### A more real-world example -In a more real-world example, a method like this is more useful when it can return one of many possible functions, like a factory that returns custom-built functions. +This technique can be even more useful when your method returns one of many possible functions, like a factory that returns custom-built functions. For instance, imagine that you want to write a method that returns functions that greet people in different languages. We’ll limit this to functions that greet in English or French, depending on a parameter that’s passed into the method. -A first thing you know is that you want to create a method that (a) takes a “desired language” as an input, and (b) returns a function as its result. Furthermore, because that function will print a string that it’s given, you know it has the type `String => Unit`. With that information you can start writing the method signature like this: +A first thing you know is that you want to create a method that (a) takes a “desired language” as an input, and (b) returns a function as its result. Furthermore, because that function prints a string that it’s given, you know it has the type `String => Unit`. With that information you write the method signature: ```scala def createGreetingFunction(desiredLanguage: String): String => Unit = ??? ``` -That’s a good start. Because you know that the possible functions you’ll return take a string and print it, you can write two anonymous functions like this for the English and French languages: +Next, because you know that the possible functions you’ll return take a string and print it, you can write two anonymous functions for the English and French languages: ```scala (name: String) => println(s"Hello, $name") (name: String) => println(s"Bonjour, $name") ``` -Inside a method it might be a little more readable if you give those anonymous functions some names, so let’s write them like this instead: +Inside a method it might be a little more readable if you give those anonymous functions some names, so let’s assign them to two variables: ```scala val englishGreeting = (name: String) => println(s"Hello, $name") @@ -503,7 +506,7 @@ val frenchGreeting = (name: String) => println(s"Bonjour, $name") Now all you need to do is (a) return `englishGreeting` if the `desiredLanguage` is English, and (b) return `frenchGreeting` if the `desiredLanguage` is French. One way to do that is with a `match` expression: -```scala +```tut def createGreetingFunction(desiredLanguage: String): String => Unit = val englishGreeting = (name: String) => println(s"Hello, $name") val frenchGreeting = (name: String) => println(s"Bonjour, $name") @@ -518,7 +521,7 @@ This is how `createGreetingFunction` builds a French-greeting function: ```scala val greetInFrench = createGreetingFunction("french") -greetInFrench("Raphael") // prints "Bonjour, Raphael" +greetInFrench("Jonathan") // prints "Bonjour, Jonathan" ``` And this is how it builds an English-greeting function: @@ -528,20 +531,21 @@ val greetInEnglish = createGreetingFunction("english") greetInEnglish("Joe") // prints "Hello, Joe" ``` -If this all makes sense — congratulations — you’ve just seen how to write methods that return functions. +If you’re comfortable with that code — congratulations — you now know how to write methods that return functions. -## Summary +## Key points -A higher-order function is often defined as a function that takes other functions as input parameters or returns a function as a result. In Scala this is possible because functions are first-class values. +That was a lengthy chapter, so let’s review it. + +A higher-order function is often defined as a function that takes other functions as input parameters or returns a function as its value. In Scala this is possible because functions are first-class values. In previous chapters you saw how to be a *consumer* of higher-order functions, and this chapter showed you how to be a *creator* of higher-order functions. Specifically, you saw: - How to write methods that take functions as input parameters - How to return a function from a method -A beneficial side effect of this chapter is that you saw many examples of how to declare type signatures for functions. The benefits of that are that you’ll use that same syntax to define function parameters, anonymous functions, and function variables, and it also becomes easier to read the Scaladoc for higher-order functions like `map`, `filter`, and others. - +A beneficial side effect of this chapter is that you saw many examples of how to declare type signatures for functions. The benefits of that are that you use that same syntax to define function parameters, anonymous functions, and function variables, and it also becomes easier to read the Scaladoc for higher-order functions like `map`, `filter`, and others. From f75875d1ea071b4139ed20e65a0f8e5c6fca8108 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Sun, 8 Nov 2020 19:01:47 -0700 Subject: [PATCH 028/169] =?UTF-8?q?Initial=20version=20of=20the=20?= =?UTF-8?q?=E2=80=98Functional=20Programming=E2=80=99=20document.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _overviews/overview/functional-programming.md | 563 ++++++++++++++++++ 1 file changed, 563 insertions(+) diff --git a/_overviews/overview/functional-programming.md b/_overviews/overview/functional-programming.md index e69de29bb2..745d35fe1d 100644 --- a/_overviews/overview/functional-programming.md +++ b/_overviews/overview/functional-programming.md @@ -0,0 +1,563 @@ +--- +title: Functional Programming with Scala 3 +description: This section provides an introduction to functional programming in Scala 3. +--- + + +Scala lets you write code in an object-oriented programming (OOP) style, a functional programming (FP) style, and also in a hybrid style — using both approaches in combination. [As Martin Odersky has stated](https://twitter.com/alexelcu/status/996408359514525696), the essence of Scala is a fusion of functional and object-oriented programming in a typed setting: + +- Functions for the logic +- Objects for the modularity + +This chapter assumes that you’re comfortable with OOP and less comfortable with FP, so it provides a gentle introduction to several main functional programming concepts: + +- What is functional programming? +- Immutable values +- Pure functions +- Functions are values +- Functional error handling + + + +## What is functional programming? + +[Wikipedia defines *functional programming*](https://en.wikipedia.org/wiki/Functional_programming) like this: + +>Functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that each return a value, rather than a sequence of imperative statements which change the state of the program. +>In functional programming, functions are treated as first-class citizens, meaning that they can be bound to names (including local identifiers), passed as arguments, and returned from other functions, just as any other data type can. This allows programs to be written in a declarative and composable style, where small functions are combined in a modular manner. + +It can also be helpful to know that experienced functional programmers have a strong desire to see their code as math, that combining pure functions together is like combining a series of algebraic equations. When you write functional code you feel like a mathematician, and once you understand the paradigm, you want to write pure functions that always return *values* — not exceptions or null values — so you can combine (compose) them together to create solutions. The feeling that you’re writing math-like equations (expressions) is the driving desire that leads you to use *only* pure functions and immutable values, because that’s what you use in algebra and other forms of math. + +Functional programming is a large topic, and there’s no simple way to condense the entire topic into one chapter, but hopefully the following section will provide an overview of the main topics, and show some of the tools Scala provides for writing functional code. + + + +## Immutable values + +In pure functional programming, only immutable values are used. In Scala this means: + +- All variables are created as `val` fields +- Only immutable collections classes are used, such as `List`, `Vector`, and the immutable `Map` and `Set` classes + +Using only immutable variables raises an interesting question: If everything is immutable, how does anything ever change? + +When it comes to using collections, one answer is that you don’t mutate an existing collection; instead, you apply a function to an existing collection to create a new collection. This is where higher-order functions like `map` and `filter` come in. + + +For example, imagine that you have a list of names — a `List[String]` — that are all in lowercase, and you want to find all the names that begin with the letter `"j"`, and then you want to capitalize those names. In FP you write this code: + +```tut +val a = List("jane", "jon", "mary", "joe") +val b = a.filter(_.startsWith("j")) + .map(_.capitalize) +``` + +As shown, you don’t mutate the original list `a`. Instead, you apply filtering and transformation functions to `a` to create a new collection, and assign that result to the new immutable variable `b`. + +Similarly, in FP you don’t create classes with mutable `var` constructor parameters. That is, you don’t write this: + +```scala +// don’t do this in FP +class Person(var firstName: String, var lastName: String) + --- --- +``` + +Instead, you typically create `case` classes, whose constructor parameters are `val` by default: + +```scala +case class Person(firstName: String, lastName: String) +``` + +Now you create a `Person` instance as a `val` field: + +```scala +val reginald = Person("Reginald", "Dwight") +``` + +Then, when you need to make a change to the data, you use the `copy` method that comes with a `case` class to “update the data as you make a copy,” like this: + +```scala +val elton = p.copy( + firstName = "Elton", // update the first name + lastName = "John" // update the last name +) +``` + +There are other techniques for working with immutable collections and variables, but hopefully these examples give you a taste of the techniques. + + +>Depending on your needs, you may create enums, traits, or classes instead of `case` classes. See the Data Modeling chapter for more details. + + + + +## Pure functions + + +Another feature that Scala offers to help you write functional code is the ability to write pure functions. In his book, *Functional Programming, Simplified*, Alvin Alexander defines a *pure function* like this: + +- A function’s output depends *only* on its input variables and its internal algorithm +- It doesn’t mutate any hidden state +- It doesn’t have any “back doors”: It doesn’t read data from the outside world (including the console, web services, databases, files, etc.), or write data to the outside world + +As a result of this definition, any time you call a pure function with the same input value(s), you’ll always get the same result. For example, you can call a `double` function an infinite number of times with the input value `2`, and you’ll always get the result `4`. + + +### Examples of pure functions + +Given that definition, as you can imagine, methods like these in the *scala.math._* package are pure functions: + +- `abs` +- `ceil` +- `max` + +These `String` methods are also pure functions: + +- `isEmpty` +- `length` +- `substring` + +Most methods on the Scala collections classes also work as pure functions, including `drop`, `filter`, `map`, and many more. + + +>In Scala, *functions* and *methods* are almost completely interchangeable, so even though we use the common industry term “pure function,” this term can be used to describe both functions and methods. + + +### Examples of impure functions + +Conversely, the following functions are *impure* because they violate the definition. + +The `foreach` method on collections classes is impure because it’s only used for its side effects, such as printing to STDOUT. + +>A great hint that `foreach` is impure is that it’s method signature declares that it returns the type `Unit`. Because it doesn’t return anything, logically the only reason you ever call it is to achieve some side effect. Similarly, *any* method that returns `Unit` is going to be an impure function. + +Date and time related methods like `getDayOfWeek`, `getHour`, and `getMinute` are all impure because their output depends on something other than their input parameters. Their results rely on some form of hidden I/O; *hidden inputs,* in these examples. + +Additionally, methods that interact with the console, files, databases, web services, sensors, etc., are all impure. + +In general, impure functions do one or more of these things: + +- Read hidden inputs, i.e., they access variables and data not explicitly passed into the function as input parameters +- Write hidden outputs +- Mutate the parameters they’re given, or mutate hidden variables, such as fields in their containing class +- Perform some sort of I/O with the outside world + + +### But impure functions are needed ... + +Of course an application isn’t very useful if it can’t read or write to the outside world, so people make this recommendation: + +>Write the core of your application using pure functions, and then write an impure “wrapper” around that core to interact with the outside world. As someone once said, this is like putting a layer of impure icing on top of a pure cake. + +It’s important to note that there are ways to make impure interactions with the outside world feel more pure. For instance, you’ll hear about using an `IO` Monad to deal with input and output. These topics are beyond the scope of this document, so to keep things simple it can help to think that FP applications have a core of pure functions that are wrapped with other functions to interact with the outside world. + + +### Writing pure functions + +**Note**: In this section the common industry term “pure function” is often used to refer to Scala methods. + +To write pure functions in Scala, just write them using Scala’s method syntax (though you can also use Scala’s function syntax, as well). For instance, here’s a pure function that doubles the input value it’s given: + +```scala +def double(i: Int): Int = i * 2 +``` + +If you’re comfortable with recursion, here’s a pure function that calculates the sum of a list of integers: + +```scala +def sum(xs: List[Int]): Int = xs match + case Nil => 0 + case head :: tail => head + sum(tail) +``` + +If you understand that code, you’ll see that it meets the pure function definition. + + +### Key points + +The first key point of this section is the definition of a pure function: + +>A *pure function* is a function that depends only on its declared inputs and its internal algorithm to produce its output. It does not read any other values from “the outside world” — the world outside of the function’s scope — and it doesn’t modify any values in the outside world. + +A second key point is that every real-world application interacts with the outside world. Therefore, a simplified way to think about functional programs is that they consist of a core of pure functions that are wrapped with other functions that interact with the outside world. + + + + +## Functions are values + +While every programming language ever created probably lets you write pure functions, a second important Scala FP feature is that *you can create functions as values*, just like you create `String` and `Int` values. + +This feature has many benefits, the most common of which are (a) you can define methods to accept function parameters, and (b) you can pass functions as parameters into methods. You’ve seen this in multiple places in this Overview, whenever methods like `map` and `filter` are demonstrated: + +```tut +val nums = (1 to 10).toList + +val doubles = nums.map(_ * 2) // double each value +val lessThanFive = nums.filter(_ < 5) // List(1,2,3,4) +``` + +In those examples, anonymous functions are passed into `map` and `filter`. + +>Anonymous functions are also known as *lambdas*. + +In addition to passing anonymous functions into `filter` and `map`, you can also supply them with *methods*: + +```scala +// two methods +def double(i: Int): Int = i * 2 +def underFive(i: Int): Boolean = i < 5 + +// pass those methods into filter and map +val doubles = nums.filter(underFive).map(double) +``` + +This ability to treat methods and functions as values is a powerful feature that functional programming languages provide. + +>Technically, a a function that takes another function as an input parameter is known as a *Higher-Order Function*. (If you like humor, as someone once wrote, that’s like saying that a class that takes an instance of another class as a constructor parameter is a Higher-Order Class.) + + +### Functions, anonymous functions, and methods + +As you saw in those examples, this is an anonymous function: + +```scala +_ * 2 +``` + + +As shown in the higher-order functions discussion, that’s a shorthand version of this syntax: + +```scala +(i: Int) => i * 2 +``` + +Functions like these are called “anonymous” because they don’t have names. If you want to give one a name, just assign it to a variable: + +```scala +val double = (i: Int) => i * 2 +``` + +Now you have a named function, one that’s assigned to a variable. You can use this function just like you use a method: + +```scala +double(2) // 4 +``` + +In most scenarios it doesn’t matter if `double` is a function or a method; Scala lets you treat them the same way. Behind the scenes, the Scala technology that lets you treat methods just like functions is known as [Eta Expansion](TODO:LINK). + +This ability to seamlessly pass functions around as variables is a distinguishing feature of functional programming languages like Scala. And as you’ve seen in the `map` and `filter` examples throughout this Overview, the ability to pass functions into other functions helps you create code that is concise and still readable — *expressive*. + +If you’re not comfortable with the process of passing functions as parameters into other functions, here are a few more examples you can experiment with: + +```tut +List("foo", "bar").map(_.toUpperCase) +List("foo", "bar").map(_.capitalize) +List("adam", "scott").map(_.length) +List(1,2,3,4,5).map(_ * 10) +List(1,2,3,4,5).filter(_ > 2) +List(5,1,3,11,7).takeWhile(_ < 6) +``` + + + + +## Functional error handling + +Functional programming is like writing a series of algebraic equations, and because algebra doesn’t have null values or throw exceptions, you don’t use these features in FP. This brings up an interesting question: In the situations where you might normally use a null value or exception in OOP code, what do you do? + +Scala’s solution is to use constructs like the `Option`/`Some`/`None` classes. This lesson provides an introduction to using these techniques. + +Two notes before we jump in: + +- The `Some` and `None` classes are subclasses of `Option`. +- Instead of repeatedly saying “`Option`/`Some`/`None`,” the following text generally just refers to “`Option`” or “the `Option` classes.” + + +### A first example + +While this first example doesn’t deal with null values, it’s a good way to introduce the `Option` classes, so we’ll start with it. + +Imagine that you want to write a method that makes it easy to convert strings to integer values, and you want an elegant way to handle the exception that’s thrown when your method gets a string like `"Hello"` instead of `"1"`. A first guess at such a method might look like this: + +```tut +def makeInt(s: String): Int = + try + Integer.parseInt(s.trim) + catch + case e: Exception => 0 +``` + +If the conversion works, this method returns the correct `Int` value, but if it fails, the method returns `0`. This might be okay for some purposes, but it’s not really accurate. For instance, the method might have received `"0"`, but it may have also received `"foo"`, `"bar"`, or an infinite number of other strings that will throw an exception. This is a real problem: How do you know when the method really received a `"0"`, or when it received something else? The answer is that with this approach, there’s no way to know. + + +### Using Option/Some/None + +A common solution to this problem in Scala is to use a trio of classes known as `Option`, `Some`, and `None`. The `Some` and `None` classes are subclasses of `Option`, so the solution works like this: + +- You declare that `makeInt` returns an `Option` type +- If `makeInt` receives a string it *can* convert to an `Int`, the answer is wrapped inside a `Some` +- If `makeInt` receives a string it *can’t* convert, it returns a `None` + +Here’s the revised version of `makeInt`: + +```tut +def makeInt(s: String): Option[Int] = + try + Some(Integer.parseInt(s.trim)) + catch + case e: Exception => None +``` + +This code can be read as, “When the given string converts to an integer, return the `Int` wrapped inside a `Some`, such as `Some(1)`. When the string can’t be converted to an integer, an exception is thrown and caught, and the method returns a `None` value.” + +These examples show how `makeInt` works: + +```scala +val a = makeInt("1") // Some(1) +val b = makeInt("one") // None +``` + +As shown, the string `"1"` results in a `Some(1)`, and the string `"one"` results in a `None`. This is the essence of the `Option` approach to error handling. As shown, this technique is used so methods can return *values* instead of *exceptions*. In other situations, `Option` values are also used to replace `null` values. + +Two notes: + +- You’ll find this approach used throughout Scala library classes, and in third-party Scala libraries. +- A key point of this example is that functional methods don’t throw exceptions; instead they return values like `Option`. + + +### Being a consumer of makeInt + +Now imagine that you’re a consumer of the `makeInt` method. You know that it returns a subclass of `Option[Int]`, so the question becomes, how do you work with these return types? + +There are two common answers, depending on your needs: + +- Use a `match` expression +- Use a `for` expression + + +>There are other approaches that can be used. See the Reference documentation for details on those approaches. + + +### Using a `match` expression + +One possible solution is to use a `match` expression: + +```scala +makeInt(x) match + case Some(i) => println(i) + 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. + + +### Using a `for` expression + +Another common solution is to use a `for` expression — i.e., the `for`/`yield` combination that was shown earlier in this Overview. For instance, imagine that you want to convert three strings to integer values, and then add them together. This is how you do that with a `for` expression and `makeInt`: + +```scala +val y = for + a <- makeInt(stringA) + b <- makeInt(stringB) + c <- makeInt(stringC) +yield + a + b + c +``` + +After that expression runs, `y` will be one of two things: + +- If *all* three strings convert to `Int` values, `y` will be a `Some[Int]`, i.e., an integer wrapped inside a `Some` +- If *any* of the three strings can’t be converted to an `Int`, `y` will be a `None` + +You can test this for yourself: + +```scala +val stringA = "1" +val stringB = "2" +val stringC = "3" + +val y = for { + a <- makeInt(stringA) + b <- makeInt(stringB) + c <- makeInt(stringC) +yield + a + b + c +``` + +With that sample data, the variable `y` will have the value `Some(6)`. + +To see the failure case, change any of those strings to something that won’t convert to an integer. When you do that, you’ll see that `y` is a `None`: + +```scala +y: Option[Int] = None +``` + +### Thinking of Option as a container + +Mental models can often help us understand new situations, so if you’re not familiar with the `Option` classes, one way to think about them is as a *container*: + +- `Some` is a container with one item in it +- `None` is a container, but it has nothing in it + +If you prefer to think of the `Option` classes as being like a box, `None` is like an empty box. It could have had something in it, but it doesn’t. + + + + + +### Using `Option` to replace `null` values + +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: + var street1: String, + var street2: 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( + "1 Main Street", + null, // <-- D’oh! A null value! + "North Pole", + "Alaska", + "99705" +) +``` + +Historically, developers have used blank strings and null values in this situation, both of which are hacks to work around the root problem: `street2` is an *optional* field. In Scala — and other modern languages — the correct solution is to declare up front that `street2` is optional: + +```scala +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( + "1 Main Street", + None, // 'street2' has no value + "North Pole", + "Alaska", + "99705" +) +``` + +or this: + +```scala +val santa = new Address( + "123 Main Street", + Some("Apt. 2B"), + "Talkeetna", + "Alaska", + "99676" +) +``` + + +### `Option` isn’t the only solution + +While this section focuses on the `Option` classes, Scala has a few other alternatives. + +For example, a trio of classes known as `Try`/`Success`/`Failure` work in the same manner, but (a) you primarily use these classes when your code can throw exceptions, and (b) you want to use the `Failure` class because it gives you access to the exception message. For example, these `Try` classes are commonly used when writing methods that interact with files, databases, and internet services, as those functions can easily throw exceptions. + +You can learn more about the `Try` classes and the similar `Either`/`Right`/`Left` classes in the Reference documentation. + + +### A quick review + +This section was long, so let’s give it a quick review: + +- Functional programmers don’t use `null` values +- A main replacement for `null` values is to use the `Option` classes +- Functional methods don’t throw exceptions; instead they return values like `Option`, `Try`, or `Either` +- Common ways to work with `Option` values are `match` and `for` expressions +- Options can be thought of as containers of one item (`Some`) and no items (`None`) +- Options can also be used for optional constructor or method parameters + + + +## Summary + +This chapter provides a high-level introduction to functional programming in Scala. The topics covered are: + +- What is functional programming? +- Immutable values +- Pure functions +- Functions are values +- Functional error handling + +As mentioned, functional programming is a big topic, so all we can do in this Overview is to touch on these introductory concepts. See the Reference documentation for more details. + + + + From 607dfd4ba61b8ff40999178140acac9cf5981194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachth=C3=A4user?= Date: Tue, 24 Nov 2020 18:17:47 +0100 Subject: [PATCH 029/169] Add option to present page as Scala 3 docs --- _data/scala3-doc-nav-header.yml | 9 +++++++++ _includes/navbar-inner.html | 20 ++++++++++++++++---- _layouts/inner-page-parent-dropdown.html | 9 +++++++-- _overviews/overview/introduction.md | 14 ++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 _data/scala3-doc-nav-header.yml diff --git a/_data/scala3-doc-nav-header.yml b/_data/scala3-doc-nav-header.yml new file mode 100644 index 0000000000..3eda2e1486 --- /dev/null +++ b/_data/scala3-doc-nav-header.yml @@ -0,0 +1,9 @@ +- title: API + url: "#" + submenu: + - title: Current + url: https://www.scala-lang.org/api/current/ + - title: Nightly + url: https://www.scala-lang.org/files/archive/nightly/2.13.x/api/2.13.x/ + - title: All Versions + url: "/api/all.html" diff --git a/_includes/navbar-inner.html b/_includes/navbar-inner.html index e565a2fd73..b8ca1c77ce 100644 --- a/_includes/navbar-inner.html +++ b/_includes/navbar-inner.html @@ -1,3 +1,9 @@ +{% if page.scala3 %} + {% assign navdata = site.data.scala3-doc-nav-header %} +{% else %} + {% assign navdata = site.data.doc-nav-header %} +{% endif %} + + +{% if page.scala3 %} +
      +{% else %}
      +{% endif %}
      diff --git a/_includes/navbar-inner.html b/_includes/navbar-inner.html index b8ca1c77ce..171d00924e 100644 --- a/_includes/navbar-inner.html +++ b/_includes/navbar-inner.html @@ -4,25 +4,7 @@ {% assign navdata = site.data.doc-nav-header %} {% endif %} - +{% include site-header.html %} {% if page.scala3 %}
      @@ -32,10 +14,14 @@