Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Scala 2 Book migration - Scala Classes #3128

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions _overviews/scala3-book/domain-modeling-oop.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,61 @@ trait ComposedService extends GreetingService, TranslationService
Abstract members in one trait (such as `translate` in `GreetingService`) are automatically matched with concrete members in another trait.
This not only works with methods as in this example, but also with all the other abstract members mentioned above (that is, types, value definitions, etc.).

### Mixing traits in on the fly

Traits that have concrete methods can be mixed into classes on the fly. Given a class:

{% tabs traits_6 %}
{% tab 'Scala 2 and 3' %}
```scala
class MyService(name: String)
```
{% endtab %}
{% endtabs %}

you can create a `MyService` instance that mixes in the traits when you create it:

{% tabs traits_7 %}
{% tab 'Scala 2 and 3' %}
```scala
val s = new MyService("ComposedService") with GreetingService with TranslationService
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Intuitively, after seeing the syntax used above, someone could expect this to be defined in Scala 3 as:

val s = MyService("ComposedService") with GreetingService, TranslationService

It seems like only the Scala 2 syntax is accepted, though.

// --------------------------------------------
```
{% endtab %}
{% endtabs %}

The REPL shows that it works:

{% tabs traits_8 class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
scala> val s = new MyService("ComposedService") with GreetingService with TranslationService
|
val s: MyService with GreetingService with TranslationService = $anon$1@4ebd8d2

scala> s.translate("Text")
val res0: String = ...

scala> s.sayHello
val res1: String = ...
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
scala> val s = new MyService("ComposedService") with GreetingService with TranslationService
val s: MyService & GreetingService & TranslationService = anon$1@c5a2d5

scala> s.translate("Text")
val res0: String = ...

scala> s.sayHello
val res1: String = ...
```
{% endtab %}
{% endtabs %}

This example works because all the mixed in methods are defined in `GreetingService` and in `TranslationService`.

## Classes

Traits are great to modularize components and describe interfaces (required and provided).
Expand Down
187 changes: 179 additions & 8 deletions _overviews/scala3-book/domain-modeling-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,26 +97,100 @@ p.vocation = "Musician"
{% endtab %}
{% endtabs %}

### Fields and methods
### `val` makes fields read-only

Classes can also have methods and additional fields that are not part of constructors.
They are defined in the body of the class.
The body is initialized as part of the default constructor:
In that first example both fields were defined as `var` fields:

{% tabs class_val_fields_1 %}
{% tab 'Scala 2 and 3' %}
```scala
class Person(var name: String, var vocation: String)
```
{% endtab %}
{% endtabs %}

That makes those fields mutable. You can also define them as `val` fields, which makes them immutable:

{% tabs class_val_fields_2 %}
{% tab 'Scala 2 and 3' %}
```scala
class Person(val name: String, val vocation: String)
// --- ---
```
{% endtab %}
{% endtabs %}

If you now try to change the name of a `Person` instance, you’ll see an error:

{% tabs class_val_fields_3 class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
scala> class Person(val name: String, val vocation: String)
class Person

scala> val p = new Person("Robert Allen Zimmerman", "Harmonica Player")
val p: Person = Person@1c58d7be

scala> p.name = "Bob Dylan"
^
error: reassignment to val
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
scala> class Person(val name: String, val vocation: String)
// defined class Person

scala> val p = Person("Robert Allen Zimmerman", "Harmonica Player")
val p: Person = Person@779228dc

scala> p.name = "Bob Dylan"
-- [E052] Type Error: ----------------------------------------------------------
1 |p.name = "Bob Dylan"
|^^^^^^^^^^^^^^^^^^^^
|Reassignment to val name
|
| longer explanation available when compiling with `-explain`
1 error found
```
{% endtab %}
{% endtabs %}

### Class constructors

In Scala, the primary constructor of a class is a combination of:

- The constructor parameters
- Methods that are called in the body of the class
- Statements and expressions that are executed in the body of the class

Defining parameters in the primary constructor automatically creates fields in the class, and fields declared in the body
of a Scala class are handled in a manner similar to Java; they are assigned when the class is first instantiated.

This `Person` class demonstrates several of the things you can do inside the body of a class:

{% tabs method class=tabs-scala-version %}
{% tab 'Scala 2' %}

```scala
class Person(var firstName: String, var lastName: String) {

println("initialization begins")
// 'public' access by default
val fullName = firstName + " " + lastName

// a private field
private val HOME = System.getProperty("user.home")

// a class method
def printFullName: Unit =
// access the `fullName` field, which is created above
println(fullName)

def printHome: Unit = println(s"HOME = $HOME")

// an overridden method
override def toString(): String = fullName

printFullName
println("initialization ends")
}
Expand All @@ -128,15 +202,23 @@ class Person(var firstName: String, var lastName: String) {

```scala
class Person(var firstName: String, var lastName: String):

println("initialization begins")
// 'public' access by default
val fullName = firstName + " " + lastName

// a private field
private val HOME = System.getProperty("user.home")

// a class method
def printFullName: Unit =
// access the `fullName` field, which is created above
println(fullName)

def printHome: Unit = println(s"HOME = $HOME")

// an overridden method
override def toString(): String = fullName

printFullName
println("initialization ends")
```
Expand All @@ -153,10 +235,16 @@ scala> val john = new Person("John", "Doe")
initialization begins
John Doe
initialization ends
val john: Person = Person@55d8f6bb
val john: Person = John Doe

scala> john.printFullName
John Doe

scala> john.printHome
HOME = /Users/al

scala> println(john)
John Doe
````
{% endtab %}
{% tab 'Scala 3' %}
Expand All @@ -165,10 +253,16 @@ scala> val john = Person("John", "Doe")
initialization begins
John Doe
initialization ends
val john: Person = Person@55d8f6bb
val john: Person = John Doe

scala> john.printFullName
John Doe

scala> john.printHome
HOME = /Users/al

scala> println(john)
John Doe
````
{% endtab %}
{% endtabs %}
Expand Down Expand Up @@ -788,6 +882,83 @@ val d = IrishSetter("Big Red") // "Big Red is a Dog"
{% endtab %}
{% endtabs %}

### Overriding an implemented method

A class can also override a method that is defined in a trait. Here is an example:
Comment on lines +885 to +887
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This covers both Overriding an implemented method and Mixing in multiple traits that have behaviors parts from the Scala 2 version.


{% tabs traits_6 class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
class Cat extends HasLegs with HasTail {
val numLegs = 4
val tailColor = "Tabby"
def walk() = println("I’m not walking")
// override 'stop'
override def stop() = println("I'm still not walking")
}
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
class Cat extends HasLegs, HasTail:
val numLegs = 4
val tailColor = "Tabby"
def walk() = println("I’m not walking")
// override 'stop'
override def stop() = println("I'm still not walking")
```
{% endtab %}
{% endtabs %}

The REPL shows how this works:

{% tabs traits_7 class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
scala> val c = new Cat
val c: Cat = Cat@3855b27e

scala> c. walk()
I’m not walking

scala> c.stop()
I'm still not walking
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
scala> val c = Cat()
val c: Cat = Cat@539ee811

scala> c.walk()
I’m not walking

scala> c.stop()
I'm still not walking
```
{% endtab %}
{% endtabs %}

You can also override a method when creating an instance of a class:

{% tabs traits_8 class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
val c = new Cat {
override def walk() = println("Run")
override def stop() = println("I will continue to run")
}
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
val c = new Cat:
override def walk() = println("Run")
override def stop() = println("I will continue to run")
```
{% endtab %}
{% endtabs %}

This is just a taste of what you can accomplish with traits.
For more details, see the remainder of these modeling lessons.

Expand Down
9 changes: 7 additions & 2 deletions _overviews/scala3-book/fun-partial-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,13 @@ val res = List(1, 2, 3).collect({ case i if i % 2 == 1 => i * 2 }) // List(2, 6)

You can define a default value for arguments not in domain with `applyOrElse`:

{% tabs fun-partial-5 %}
{% tab 'Scala 2 and 3' %}
{% tabs fun-partial-5 class=tabs-scala-version%}
{% tab 'Scala 2' %}
```scala
doubledOdds.applyOrElse(4, (i: Int) => i + 1) // 5
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A small fix which I omitted last time somehow.

```
{% endtab %}
{% tab 'Scala 3' %}
```scala
doubledOdds.applyOrElse(4, _ + 1) // 5
```
Expand Down