From 62e554e6cd5756a8be448a06e37d63e5a54545e7 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Fri, 7 Oct 2022 22:57:36 +0800 Subject: [PATCH 1/9] add code tabs in num24. --- _overviews/scala3-book/methods-most.md | 315 +++++++++++++++++++++++-- 1 file changed, 297 insertions(+), 18 deletions(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index e8d540b8cf..378055c2fc 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -23,6 +23,20 @@ Scala methods have many features, including these: Some of these features are demonstrated in this section, but when you’re defining a “simple” method that doesn’t use those features, the syntax looks like this: +{% tabs method_1 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_1 %} + +```scala +def methodName(param1: Type1, param2: Type2): ReturnType = { + // the method body + // goes here +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_1 %} + ```scala def methodName(param1: Type1, param2: Type2): ReturnType = // the method body @@ -30,6 +44,9 @@ def methodName(param1: Type1, param2: Type2): ReturnType = end methodName // this is optional ``` +{% endtab %} +{% endtabs %} + In that syntax: - The keyword `def` is used to define a method @@ -42,27 +59,40 @@ In that syntax: 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: +{% tabs method_2 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_2 %} + ```scala def add(a: Int, b: Int): Int = a + b def add(a: Int, b: Int) = a + b ``` +{% endtab %} +{% endtabs %} + It is recommended to annotate publicly visible methods with their return type. Declaring the return type can make it easier to understand it when you look at it months or years later, or when you look at another person’s code. - - ## Calling methods Invoking a method is straightforward: +{% tabs method_3 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_3 %} + ```scala val x = add(1, 2) // 3 ``` +{% endtab %} +{% endtabs %} + The Scala collections classes have dozens of built-in methods. These examples show how to call them: +{% tabs method_4 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_4 %} + ```scala val x = List(1, 2, 3) @@ -71,18 +101,34 @@ x.contains(1) // true x.map(_ * 10) // List(10, 20, 30) ``` +{% endtab %} +{% endtabs %} + 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: +{% tabs method_5 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_5 %} + +```scala +def addThenDouble(a: Int, b: Int): Int = { + // imagine that this body requires multiple lines + val sum = a + b + sum * 2 +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_5 %} + ```scala def addThenDouble(a: Int, b: Int): Int = // imagine that this body requires multiple lines @@ -90,6 +136,9 @@ def addThenDouble(a: Int, b: Int): Int = sum * 2 ``` +{% endtab %} +{% endtabs %} + In that method: - `sum` is an immutable local variable; it can’t be accessed outside of the method @@ -97,20 +146,32 @@ In that method: When you paste that code into the REPL, you’ll see that it works as desired: +{% tabs method_6 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_6 %} + ```scala scala> addThenDouble(1, 1) res0: Int = 4 ``` +{% endtab %} +{% endtabs %} + Notice that there’s no need for a `return` statement at the end of the method. Because almost everything in Scala is an _expression_---meaning that each line of code returns (or _evaluates to_) a value---there’s no need to use `return`. This becomes more clear when you condense that method and write it on one line: +{% tabs method_7 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_7 %} + ```scala def addThenDouble(a: Int, b: Int): Int = (a + b) * 2 ``` +{% endtab %} +{% endtabs %} + The body of a method can use all the different features of the language: - `if`/`else` expressions @@ -123,6 +184,21 @@ The body of a method can use all the different features of the language: As an example of a real-world multiline method, this `getStackTraceAsString` method converts its `Throwable` input parameter into a well-formatted `String`: +{% tabs method_8 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_8 %} + +```scala +def getStackTraceAsString(t: Throwable): String = { + val sw = StringWriter() + t.printStackTrace(PrintWriter(sw)) + sw.toString +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_8 %} + ```scala def getStackTraceAsString(t: Throwable): String = val sw = StringWriter() @@ -130,32 +206,57 @@ def getStackTraceAsString(t: Throwable): String = sw.toString ``` +{% endtab %} +{% endtabs %} + In that method: - The first line assigns a new instance of `StringWriter` to the value binder `sw` - The second line stores the stack trace content into the `StringWriter` - The third line yields the `String` representation of the stack trace - ## Default parameter values Method parameters can have default values. In this example, default values are given for both the `timeout` and `protocol` parameters: +{% tabs method_9 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_9 %} + +```scala +def makeConnection(timeout: Int = 5_000, protocol: String = "http") = { + println(f"timeout = ${timeout}%d, protocol = ${protocol}%s") + // more code here ... +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_9 %} + ```scala def makeConnection(timeout: Int = 5_000, protocol: String = "http") = println(f"timeout = ${timeout}%d, protocol = ${protocol}%s") // more code here ... ``` +{% endtab %} +{% endtabs %} + Because the parameters have default values, the method can be called in these ways: +{% tabs method_10 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_10 %} + ```scala makeConnection() // timeout = 5000, protocol = http makeConnection(2_000) // timeout = 2000, protocol = http makeConnection(3_000, "https") // timeout = 3000, protocol = https ``` +{% endtab %} +{% endtabs %} + 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` @@ -164,13 +265,14 @@ Here are a few key points about those examples: Notice that by using default parameter values, it appears to the consumer that they can use three different overridden methods. - - ## 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: +{% tabs method_11 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_11 %} + ```scala makeConnection(timeout=10_000) makeConnection(protocol="https") @@ -178,15 +280,27 @@ makeConnection(timeout=10_000, protocol="https") makeConnection(protocol="https", timeout=10_000) ``` +{% endtab %} +{% endtabs %} + In some frameworks named parameters are heavily used. They’re also very useful when multiple method parameters have the same type: +{% tabs method_12 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_12 %} + ```scala engage(true, true, true, false) ``` +{% endtab %} +{% endtabs %} + Without help from an IDE that code can be hard to read, but this code is much more clear and obvious: +{% tabs method_13 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_13 %} + ```scala engage( speedIsSet = true, @@ -196,7 +310,8 @@ engage( ) ``` - +{% endtab %} +{% endtabs %} ## A suggestion about methods that take no parameters @@ -209,30 +324,56 @@ When you create arity-0 methods: For example, this method performs a side effect, so it’s declared with empty parentheses: +{% tabs method_14 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_14 %} + ```scala def speak() = println("hi") ``` +{% endtab %} +{% endtabs %} + Doing this requires callers of the method to use open parentheses when calling the method: +{% tabs method_15 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_15 %} + ```scala speak // error: "method speak must be called with () argument" speak() // prints "hi" ``` +{% endtab %} +{% endtabs %} + While this is just a convention, following it dramatically improves code readability: It makes it easier to understand at a glance that an arity-0 method performs side effects. {% comment %} Some of that wording comes from this page: https://docs.scala-lang.org/style/method-invocation.html {% endcomment %} - - ## Using `if` 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 `isTruthy` that implements the Perl definitions of `true` and `false`: +{% tabs method_16 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_16 %} + +```scala +def isTruthy(a: Any) = { + if (a == 0 || a == "" || a == false) + false + else + true +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_16 %} + ```scala def isTruthy(a: Any) = if a == 0 || a == "" || a == false then @@ -241,8 +382,14 @@ def isTruthy(a: Any) = true ``` +{% endtab %} +{% endtabs %} + These examples show how that method works: +{% tabs method_17 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_17 %} + ```scala isTruthy(0) // false isTruthy("") // false @@ -250,28 +397,61 @@ isTruthy("hi") // true isTruthy(1.0) // true ``` - +{% endtab %} +{% endtabs %} ## Using `match` as a method body A `match` expression can also be used as the entire method body, and often is. Here’s another version of `isTruthy`, written with a `match` expression : +{% tabs method_18 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_18 %} + +```scala +def isTruthy(a: Matchable) = a match { + case 0 | "" | false => false + case _ => true +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_18 %} + ```scala def isTruthy(a: Matchable) = a match case 0 | "" | false => false case _ => true ``` +{% endtab %} +{% endtabs %} + This method works just like the previous method that used an `if`/`else` expression. We use `Matchable` instead of `Any` as the parameter's type to accept any value that supports pattern matching. For more details on the `Matchable` trait, see the [Reference documentation][reference_matchable]. - ## Controlling visibility in classes In classes, objects, traits, and enums, Scala methods are public by default, so the `Dog` instance created here can access the `speak` method: +{% tabs method_19 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_19 %} + +```scala +class Dog { + def speak() = println("Woof") +} + +val d = new Dog +d.speak() // prints "Woof" +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_19 %} + ```scala class Dog: def speak() = println("Woof") @@ -280,9 +460,29 @@ val d = new Dog d.speak() // prints "Woof" ``` +{% endtab %} +{% endtabs %} + Methods can also be marked as `private`. This makes them private to the current class, so they can’t be called nor overridden in subclasses: +{% tabs method_20 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_20 %} +```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") +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_20 %} + ```scala class Animal: private def breathe() = println("I’m breathing") @@ -292,8 +492,38 @@ class Cat extends Animal: override def breathe() = println("Yo, I’m totally breathing") ``` +{% endtab %} +{% endtabs %} + If you want to make a method private to the current class and also allow subclasses to call it or override it, mark the method as `protected`, as shown with the `speak` method in this example: +{% tabs method_21 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_21 %} + +```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 +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_21 %} + ```scala class Animal: private def breathe() = println("I’m breathing") @@ -311,13 +541,15 @@ cat.speak() cat.breathe() // won’t compile because it’s private ``` +{% endtab %} +{% endtabs %} + 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 - ## Objects can contain methods Earlier you saw that traits and classes can have methods. @@ -325,6 +557,38 @@ The Scala `object` keyword is used to create a singleton class, and an object ca This is a nice way to group a set of “utility” methods. For instance, this object contains a collection of methods that work on strings: +{% tabs method_22 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_22 %} + +```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.*") + +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=method_1 %} + ```scala object StringUtils: @@ -350,7 +614,8 @@ object StringUtils: end StringUtils ``` - +{% endtab %} +{% endtabs %} ## Extension methods @@ -359,12 +624,21 @@ Their main purpose is to let you add new functionality to closed classes. As shown in that section, imagine that you have a `Circle` class, but you can’t change its source code. For instance, it may be defined like this in a third-party library: +{% tabs method_23 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_23 %} + ```scala case class Circle(x: Double, y: Double, radius: Double) ``` +{% endtab %} +{% endtabs %} + When you want to add methods to this class, you can define them as extension methods, like this: +{% tabs method_24 class=tabs-scala-version %} +{% tab 'Scala 3 only' for=method_24 %} + ```scala extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 @@ -372,17 +646,24 @@ extension (c: Circle) def area: Double = math.Pi * c.radius * c.radius ``` +{% endtab %} +{% endtabs %} + Now when you have a `Circle` instance named `aCircle`, you can call those methods like this: +{% tabs method_25 class=tabs-scala-version %} +{% tab 'Scala 2 and 3' for=method_25 %} + ```scala aCircle.circumference aCircle.diameter aCircle.area ``` -See the [Extension methods section][reference_extension_methods] of this book, and the [“Extension methods” Reference page][reference] for more details. - +{% endtab %} +{% endtabs %} +See the [Extension methods section][reference_extension_methods] of this book, and the [“Extension methods” Reference page][reference] for more details. ## Even more @@ -399,8 +680,6 @@ There’s even more to know about methods, including how to: See the [Reference documentation][reference] for more details on these features. - - [extension]: {% link _overviews/scala3-book/ca-extension-methods.md %} [reference_extension_methods]: {{ site.scala3ref }}/contextual/extension-methods.html [reference]: {{ site.scala3ref }}/overview.html From d6aa71d4a6e98b2a994d525a49954cfbe0e8696e Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Fri, 7 Oct 2022 23:05:55 +0800 Subject: [PATCH 2/9] correct 2&3 and 3 only tab. --- _overviews/scala3-book/methods-most.md | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index 378055c2fc..1ccb1c3fa3 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -59,7 +59,7 @@ In that syntax: 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: -{% tabs method_2 class=tabs-scala-version %} +{% tabs method_2 %} {% tab 'Scala 2 and 3' for=method_2 %} ```scala @@ -77,7 +77,7 @@ Declaring the return type can make it easier to understand it when you look at i Invoking a method is straightforward: -{% tabs method_3 class=tabs-scala-version %} +{% tabs method_3 %} {% tab 'Scala 2 and 3' for=method_3 %} ```scala @@ -90,7 +90,7 @@ val x = add(1, 2) // 3 The Scala collections classes have dozens of built-in methods. These examples show how to call them: -{% tabs method_4 class=tabs-scala-version %} +{% tabs method_4 %} {% tab 'Scala 2 and 3' for=method_4 %} ```scala @@ -146,7 +146,7 @@ In that method: When you paste that code into the REPL, you’ll see that it works as desired: -{% tabs method_6 class=tabs-scala-version %} +{% tabs method_6 %} {% tab 'Scala 2 and 3' for=method_6 %} ```scala @@ -162,7 +162,7 @@ Because almost everything in Scala is an _expression_---meaning that each line o This becomes more clear when you condense that method and write it on one line: -{% tabs method_7 class=tabs-scala-version %} +{% tabs method_7 %} {% tab 'Scala 2 and 3' for=method_7 %} ```scala @@ -245,7 +245,7 @@ def makeConnection(timeout: Int = 5_000, protocol: String = "http") = Because the parameters have default values, the method can be called in these ways: -{% tabs method_10 class=tabs-scala-version %} +{% tabs method_10 %} {% tab 'Scala 2 and 3' for=method_10 %} ```scala @@ -270,7 +270,7 @@ Notice that by using default parameter values, it appears to the consumer that t 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: -{% tabs method_11 class=tabs-scala-version %} +{% tabs method_11 %} {% tab 'Scala 2 and 3' for=method_11 %} ```scala @@ -286,7 +286,7 @@ makeConnection(protocol="https", timeout=10_000) In some frameworks named parameters are heavily used. They’re also very useful when multiple method parameters have the same type: -{% tabs method_12 class=tabs-scala-version %} +{% tabs method_12 %} {% tab 'Scala 2 and 3' for=method_12 %} ```scala @@ -298,7 +298,7 @@ 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: -{% tabs method_13 class=tabs-scala-version %} +{% tabs method_13 %} {% tab 'Scala 2 and 3' for=method_13 %} ```scala @@ -324,7 +324,7 @@ When you create arity-0 methods: For example, this method performs a side effect, so it’s declared with empty parentheses: -{% tabs method_14 class=tabs-scala-version %} +{% tabs method_14 %} {% tab 'Scala 2 and 3' for=method_14 %} ```scala @@ -336,7 +336,7 @@ def speak() = println("hi") Doing this requires callers of the method to use open parentheses when calling the method: -{% tabs method_15 class=tabs-scala-version %} +{% tabs method_15 %} {% tab 'Scala 2 and 3' for=method_15 %} ```scala @@ -387,7 +387,7 @@ def isTruthy(a: Any) = These examples show how that method works: -{% tabs method_17 class=tabs-scala-version %} +{% tabs method_17 %} {% tab 'Scala 2 and 3' for=method_17 %} ```scala @@ -624,7 +624,7 @@ Their main purpose is to let you add new functionality to closed classes. As shown in that section, imagine that you have a `Circle` class, but you can’t change its source code. For instance, it may be defined like this in a third-party library: -{% tabs method_23 class=tabs-scala-version %} +{% tabs method_23 %} {% tab 'Scala 2 and 3' for=method_23 %} ```scala @@ -636,7 +636,7 @@ case class Circle(x: Double, y: Double, radius: Double) When you want to add methods to this class, you can define them as extension methods, like this: -{% tabs method_24 class=tabs-scala-version %} +{% tabs method_24 %} {% tab 'Scala 3 only' for=method_24 %} ```scala @@ -651,7 +651,7 @@ extension (c: Circle) Now when you have a `Circle` instance named `aCircle`, you can call those methods like this: -{% tabs method_25 class=tabs-scala-version %} +{% tabs method_25 %} {% tab 'Scala 2 and 3' for=method_25 %} ```scala From 7cd41b3392b16b43aaaf9ca563f93c80ca5b1fea Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Wed, 12 Oct 2022 19:26:38 +0800 Subject: [PATCH 3/9] Update _overviews/scala3-book/methods-most.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/methods-most.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index 1ccb1c3fa3..32c1744a6d 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -587,7 +587,7 @@ object StringUtils { {% endtab %} -{% tab 'Scala 3' for=method_1 %} +{% tab 'Scala 3' for=method_22 %} ```scala object StringUtils: From 1fff15e0e4ccda8e4ea9173013dc343a85ce7d86 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 15 Oct 2022 02:48:36 +0800 Subject: [PATCH 4/9] changed to scal 3 Only. --- _overviews/scala3-book/methods-most.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index 32c1744a6d..3b2fda7443 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -637,7 +637,7 @@ case class Circle(x: Double, y: Double, radius: Double) When you want to add methods to this class, you can define them as extension methods, like this: {% tabs method_24 %} -{% tab 'Scala 3 only' for=method_24 %} +{% tab 'Scala 3 Only' for=method_24 %} ```scala extension (c: Circle) @@ -652,7 +652,7 @@ extension (c: Circle) Now when you have a `Circle` instance named `aCircle`, you can call those methods like this: {% tabs method_25 %} -{% tab 'Scala 2 and 3' for=method_25 %} +{% tab 'Scala 3 Only' for=method_25 %} ```scala aCircle.circumference From 5f4a2d88b024dc6de0bf7c077d77b87068680308 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Mon, 17 Oct 2022 21:29:10 +0800 Subject: [PATCH 5/9] Update _overviews/scala3-book/methods-most.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/methods-most.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index 3b2fda7443..73579c5cf2 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -190,7 +190,7 @@ As an example of a real-world multiline method, this `getStackTraceAsString` met ```scala def getStackTraceAsString(t: Throwable): String = { val sw = StringWriter() - t.printStackTrace(PrintWriter(sw)) + t.printStackTrace(new PrintWriter(sw)) sw.toString } ``` From aded22ce92fc16197c83a0742941cfcbd80f56eb Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Mon, 17 Oct 2022 21:29:20 +0800 Subject: [PATCH 6/9] Update _overviews/scala3-book/methods-most.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/methods-most.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index 73579c5cf2..c306db375a 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -189,7 +189,7 @@ As an example of a real-world multiline method, this `getStackTraceAsString` met ```scala def getStackTraceAsString(t: Throwable): String = { - val sw = StringWriter() + val sw = new StringWriter() t.printStackTrace(new PrintWriter(sw)) sw.toString } From 03e71d48d34bb40ad01a0b2df7991aa3542e118d Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Mon, 17 Oct 2022 21:35:19 +0800 Subject: [PATCH 7/9] Update _overviews/scala3-book/methods-most.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/methods-most.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index c306db375a..25c28a1853 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -409,7 +409,7 @@ Here’s another version of `isTruthy`, written with a `match` expression : {% tab 'Scala 2' for=method_18 %} ```scala -def isTruthy(a: Matchable) = a match { +def isTruthy(a: Any) = a match { case 0 | "" | false => false case _ => true } From 43a0a4e94c4d5ea745f634484bb2866a936d0de3 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Wed, 19 Oct 2022 13:35:03 +0800 Subject: [PATCH 8/9] move comments to scala3 tab as review. --- _overviews/scala3-book/methods-most.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index 25c28a1853..ed6abebb0b 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -425,12 +425,12 @@ def isTruthy(a: Matchable) = a match case _ => true ``` -{% endtab %} -{% endtabs %} +> This method works just like the previous method that used an `if`/`else` expression. We use `Matchable` instead of `Any` as the parameter's type to accept any value that supports pattern matching. -This method works just like the previous method that used an `if`/`else` expression. We use `Matchable` instead of `Any` as the parameter's type to accept any value that supports pattern matching. +> For more details on the `Matchable` trait, see the [Reference documentation][reference_matchable]. -For more details on the `Matchable` trait, see the [Reference documentation][reference_matchable]. +{% endtab %} +{% endtabs %} ## Controlling visibility in classes From 4d631c08327cad587787bcd8564efd3d1383f7e5 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 19 Oct 2022 11:02:14 +0200 Subject: [PATCH 9/9] handle scala 2 extension methods --- _overviews/scala3-book/methods-most.md | 41 ++++++++++++++++++-------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/_overviews/scala3-book/methods-most.md b/_overviews/scala3-book/methods-most.md index ed6abebb0b..3551a96fe0 100644 --- a/_overviews/scala3-book/methods-most.md +++ b/_overviews/scala3-book/methods-most.md @@ -19,7 +19,7 @@ Scala methods have many features, including these: - Multiple parameter groups - Context-provided parameters - By-name parameters -- ... +- and more... Some of these features are demonstrated in this section, but when you’re defining a “simple” method that doesn’t use those features, the syntax looks like this: @@ -429,6 +429,7 @@ def isTruthy(a: Matchable) = a match > For more details on the `Matchable` trait, see the [Reference documentation][reference_matchable]. +[reference_matchable]: {{ site.scala3ref }}/other-new-features/matchable.html {% endtab %} {% endtabs %} @@ -619,10 +620,9 @@ end StringUtils ## Extension methods -Extension methods are discussed in the [Extension methods section][extension] of the Contextual Abstraction chapter. -Their main purpose is to let you add new functionality to closed classes. -As shown in that section, imagine that you have a `Circle` class, but you can’t change its source code. -For instance, it may be defined like this in a third-party library: +There are many situations where you would like to add functionality to closed classes. +For example, imagine that you have a `Circle` class, but you can’t change its source code. +It could be defined like this in a third-party library: {% tabs method_23 %} {% tab 'Scala 2 and 3' for=method_23 %} @@ -636,8 +636,20 @@ case class Circle(x: Double, y: Double, radius: Double) When you want to add methods to this class, you can define them as extension methods, like this: -{% tabs method_24 %} -{% tab 'Scala 3 Only' for=method_24 %} +{% tabs method_24 class=tabs-scala-version %} +{% tab 'Scala 2' for=method_24 %} + +```scala +implicit class CircleOps(c: Circle) { + def circumference: Double = c.radius * math.Pi * 2 + def diameter: Double = c.radius * 2 + def area: Double = math.Pi * c.radius * c.radius +} +``` +In Scala 2 use an `implicit class`, find out more details [here](/overviews/core/implicit-classes.html). + +{% endtab %} +{% tab 'Scala 3' for=method_24 %} ```scala extension (c: Circle) @@ -645,14 +657,17 @@ extension (c: Circle) def diameter: Double = c.radius * 2 def area: Double = math.Pi * c.radius * c.radius ``` +In Scala 3 use the new `extension` construct. For more details see chapters in [this book][extension], or the [Scala 3 reference][reference-ext]. +[reference-ext]: {{ site.scala3ref }}/contextual/extension-methods.html +[extension]: {% link _overviews/scala3-book/ca-extension-methods.md %} {% endtab %} {% endtabs %} Now when you have a `Circle` instance named `aCircle`, you can call those methods like this: {% tabs method_25 %} -{% tab 'Scala 3 Only' for=method_25 %} +{% tab 'Scala 2 and 3' for=method_25 %} ```scala aCircle.circumference @@ -663,8 +678,6 @@ aCircle.area {% endtab %} {% endtabs %} -See the [Extension methods section][reference_extension_methods] of this book, and the [“Extension methods” Reference page][reference] for more details. - ## Even more There’s even more to know about methods, including how to: @@ -678,9 +691,11 @@ There’s even more to know about methods, including how to: - Write methods that have multiple parameter groups (partially-applied functions) - Create methods that have generic type parameters -See the [Reference documentation][reference] for more details on these features. +{% comment %} +Jamie: there really needs better linking here - previously it was to the Scala 3 Reference, which doesnt cover any +of this +{% endcomment %} +See the other chapters in this book for more details on these features. -[extension]: {% link _overviews/scala3-book/ca-extension-methods.md %} [reference_extension_methods]: {{ site.scala3ref }}/contextual/extension-methods.html -[reference]: {{ site.scala3ref }}/overview.html [reference_matchable]: {{ site.scala3ref }}/other-new-features/matchable.html