From 3c7c29b6a1b65cec45e617d11a3b3e87c3c943e3 Mon Sep 17 00:00:00 2001 From: Alvin Alexander Date: Wed, 23 Dec 2020 22:26:02 -0700 Subject: [PATCH 1/2] Small edits while reviewing a comment by Vinz --- .../scala3-book/domain-modeling-tools.md | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/_overviews/scala3-book/domain-modeling-tools.md b/_overviews/scala3-book/domain-modeling-tools.md index ff1a365204..c135e7819c 100644 --- a/_overviews/scala3-book/domain-modeling-tools.md +++ b/_overviews/scala3-book/domain-modeling-tools.md @@ -8,7 +8,7 @@ next-page: domain-modeling-oop --- -Scala 3 provides us with many different tools to model the world around us: +Scala 3 provides many different constructs so we can model the world around us: - Classes - Objects @@ -32,8 +32,9 @@ class Person(var name: String, var vocation: String) class Book(var title: String, var author: String, var year: Int) class Movie(var name: String, var director: String, var year: Int) ``` -The examples above show that Scala has a very lightweight way to declare classes. -The definition of the class `Person` roughly corresponds to the following, more explicit, version + +These examples show that Scala has a very lightweight way to declare classes. +The definition of the class `Person` roughly corresponds to the following, more explicit, version: ```scala class Person: @@ -49,7 +50,8 @@ class Person: name = _name vocation = _vocation ``` -defining the two fields `name` and `vocation` together with a constructor that accepts values for the two fields and assigns them. + +This version defines the two fields `name` and `vocation`, together with a constructor that accepts values for those two fields and assigns them. All of the parameters of our example classes are defined as `var` fields, which means they are mutable: you can read them, and also modify them. If you want them to be immutable---read only---create them as `val` fields instead. @@ -67,7 +69,7 @@ However, with [creator applications][creator] this isn’t required in Scala 3: val p = Person("Robert Allen Zimmerman", "Harmonica Player") ``` -Once you have an instance of a class, you can access its fields, which in this example are all constructor parameters: +Once you have an instance of a class such as `p`, you can access its fields, which in this example are all constructor parameters: ```scala p.name // "Robert Allen Zimmerman" @@ -227,7 +229,7 @@ This is shown in the previous `Socket` example. An object is a class that has exactly one instance. It’s initialized lazily when its members are referenced, similar to a `lazy val`. -Objects in Scala allow grouping methods and fields under one namespace, similar to how can use `static` members on a class in Java, Javascript (ES6) or `@staticmethod` in Python. +Objects in Scala allow grouping methods and fields under one namespace, similar to how you use `static` members on a class in Java, Javascript (ES6), or `@staticmethod` in Python. Declaring an `object` is similar to declaring a `class`. Here’s an example of a “string utilities” object that contains a set of methods for working with strings: @@ -245,11 +247,22 @@ We can use the object as follows: StringUtil.truncate("Chuck Bartowski", 5) // "Chuck" ``` -Importing in Scala is very flexible and allows us to import all members of an object: +Importing in Scala is very flexible, and allows us to import _all_ members of an object: ```scala import StringUtils._ -truncate("Chuck Bartowski", 5) // "Chuck" +truncate("Chuck Bartowski", 5) // "Chuck" +containsWhitespace("Sarah Walker") // true +isNullOrEmpty("John Casey") // false +``` + +or just _some_ members: + +```scala +import StringUtils.{truncate, containsWhitespace} +truncate("Charles Carmichael", 7) // "Charles" +containsWhitespace("Captain Awesome") // true +isNullOrEmpty("Morgan Grimes") // Not found: isNullOrEmpty (error) ``` Objects can also contain fields, which are also accessed like static members: @@ -266,7 +279,7 @@ println(MathConstants.PI) // 3.14159 ## Companion objects -An `object` that has the same name as a class, and is declared in the same file as the class, is called a _"companion object"_. +An `object` that has the same name as a class, and is declared in the same file as the class, is called a _"companion object_." Similarly, the corresponding class is called the object’s companion class. A companion class or object can access the private members of its companion. @@ -287,7 +300,7 @@ circle1.area ``` In this example the `area` method that’s available to each instance uses the `calculateArea` method that’s defined in the companion object. -If you’re familiar with Java, `calculateArea` is similar to a static method. +Once again, `calculateArea` is similar to a static method in Java. Also, because `calculateArea` is private, it can’t be accessed by other code, but as shown, it can be seen by instances of the `Circle` class. ### Other uses @@ -300,7 +313,7 @@ Companion objects can be used for several purposes: - They can contain `apply` methods, which---thanks to some syntactic sugar---work as factory methods to construct new instances - They can contain `unapply` methods, which are used to deconstruct objects, such as with pattern matching -Here’s a quick look at how `apply` methods that can be used as factory methods to create new objects: +Here’s a quick look at how `apply` methods can be used as factory methods to create new objects: ```scala class Person: @@ -436,8 +449,8 @@ class Dog(name: String, var age: Int) extends Pet(name): val d = Dog("Fido", 1) ``` -Traits are more flexible to compose (you can mix in multiple traits, but only extend one class) and should most of the time be preferred to classes. -The rule of thumb is to use classes whenever you want to create instances of a particular type and traits when you want to decompose and reuse behaviour. +Traits are more flexible to compose --- you can mix in multiple traits, but only extend one class --- and should be preferred to classes and abstract classes most of the time. +The rule of thumb is to use classes whenever you want to create instances of a particular type, and traits when you want to decompose and reuse behaviour. ## Enums @@ -466,7 +479,7 @@ import CrustSize._ val currentCrustSize = Small ``` -Enum values can be compared using equals (`==`) and matched on: +Enum values can be compared using equals (`==`), and also matched on: ```scala // if/then @@ -529,11 +542,11 @@ The section on [algebraic datatypes][adts] and the [reference documentation][ref ## Case classes Case classes are used to model immutable data structures. -Take the following example, +Take the following example: ```scala case class Person(name: String, relation: String) ``` -Since we declared `Person` as a case class, the fields `name` and `relation` are public and immutable by default. +Since we declare `Person` as a case class, the fields `name` and `relation` are public and immutable by default. We can create instances of case classes as follows: ```scala val christina = Person("Christina", "niece") @@ -544,9 +557,9 @@ christina.name = "Fred" // error: reassignment to val ``` Since the fields of a case class are assumed to be immutable, the Scala compiler can generate many helpful methods for you: * An `unapply` method is generated, which allows you to perform pattern matching on a case class (that is, `case Person(n, r) => ...`). -* A `copy` method is generated in the class, which is very useful to create modified copies of an instance +* A `copy` method is generated in the class, which is very useful to create modified copies of an instance. * `equals` and `hashCode` methods using structural equality are generated, allowing you to use instances of case classes in `Map`s. -* A default `toString` method is generated, which is helpful for debugging +* A default `toString` method is generated, which is helpful for debugging. These additional features are demonstrated in the below example: ```scala @@ -576,8 +589,8 @@ As mentioned, case classes support functional programming (FP): - In FP you try to avoid mutating data structures. It thus makes sense that constructor fields default to `val`. - Since instances of case classes are not changed, they can easily be shared without fearing mutation or race conditions. -- Instead of mutating one instance, you can use the `copy` method as a template to create a new (potentially changed) instance. + Since instances of case classes can’t be changed, they can easily be shared without fearing mutation or race conditions. +- Instead of mutating an instance, you can use the `copy` method as a template to create a new (potentially changed) instance. This process can be referred to as “update as you copy.” - Having an `unapply` method auto-generated for you also lets case classes be used in advanced ways with pattern matching. @@ -621,7 +634,7 @@ case Teacher(name, whatTheyTeach) => ``` Those patterns work because `Student` and `Teacher` are defined as case classes that have `unapply` methods whose type signature conforms to a certain standard. -Technically, the specific type of pattern matching shown in these examples is known as a *constructor pattern*. +Technically, the specific type of pattern matching shown in these examples is known as a _constructor pattern_. > The Scala standard is that an `unapply` method returns the case class constructor fields in a tuple that’s wrapped in an `Option`. > The “tuple” part of the solution was shown in the previous lesson. From 885e6e98c37e9631e7d8c675e316857aeb103289 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 6 Jan 2021 11:44:58 +0100 Subject: [PATCH 2/2] remove spaces around dashes --- _overviews/scala3-book/domain-modeling-tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/domain-modeling-tools.md b/_overviews/scala3-book/domain-modeling-tools.md index c135e7819c..a7db70196f 100644 --- a/_overviews/scala3-book/domain-modeling-tools.md +++ b/_overviews/scala3-book/domain-modeling-tools.md @@ -449,7 +449,7 @@ class Dog(name: String, var age: Int) extends Pet(name): val d = Dog("Fido", 1) ``` -Traits are more flexible to compose --- you can mix in multiple traits, but only extend one class --- and should be preferred to classes and abstract classes most of the time. +Traits are more flexible to compose---you can mix in multiple traits, but only extend one class---and should be preferred to classes and abstract classes most of the time. The rule of thumb is to use classes whenever you want to create instances of a particular type, and traits when you want to decompose and reuse behaviour.