Skip to content

Commit 55f9e70

Browse files
authored
Merge pull request #2581 from benluo/_overview/scala3-book/num9-code-tabs
add code tabs in num9
2 parents 425cb51 + 85e2cae commit 55f9e70

File tree

1 file changed

+216
-20
lines changed

1 file changed

+216
-20
lines changed

_overviews/scala3-book/taste-modeling.md

Lines changed: 216 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@ next-page: taste-methods
1313
NOTE: I kept the OOP section first, assuming that most readers will be coming from an OOP background.
1414
{% endcomment %}
1515

16-
1716
Scala supports both functional programming (FP) and object-oriented programming (OOP), as well as a fusion of the two paradigms.
1817
This section provides a quick overview of data modeling in OOP and FP.
1918

20-
21-
2219
## OOP Domain Modeling
2320

2421
When writing code in an OOP style, your two main tools for data encapsulation are _traits_ and _classes_.
@@ -38,6 +35,29 @@ Later, when you want to create concrete implementations of attributes and behavi
3835

3936
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:
4037

38+
{% tabs traits class=tabs-scala-version %}
39+
{% tab 'Scala 2' for=traits %}
40+
41+
```scala
42+
trait Speaker {
43+
def speak(): String // has no body, so it’s abstract
44+
}
45+
46+
trait TailWagger {
47+
def startTail(): Unit = println("tail is wagging")
48+
def stopTail(): Unit = println("tail is stopped")
49+
}
50+
51+
trait Runner {
52+
def startRunning(): Unit = println("I’m running")
53+
def stopRunning(): Unit = println("Stopped running")
54+
}
55+
```
56+
57+
{% endtab %}
58+
59+
{% tab 'Scala 3' for=traits %}
60+
4161
```scala
4262
trait Speaker:
4363
def speak(): String // has no body, so it’s abstract
@@ -51,26 +71,80 @@ trait Runner:
5171
def stopRunning(): Unit = println("Stopped running")
5272
```
5373

74+
{% endtab %}
75+
{% endtabs %}
76+
5477
Given those traits, here’s a `Dog` class that extends all of those traits while providing a behavior for the abstract `speak` method:
5578

79+
{% tabs traits-class class=tabs-scala-version %}
80+
{% tab 'Scala 2' for=traits-class %}
81+
82+
```scala
83+
class Dog(name: String) extends Speaker with TailWagger with Runner {
84+
def speak(): String = "Woof!"
85+
}
86+
```
87+
88+
{% endtab %}
89+
90+
{% tab 'Scala 3' for=traits-class %}
91+
5692
```scala
5793
class Dog(name: String) extends Speaker, TailWagger, Runner:
5894
def speak(): String = "Woof!"
5995
```
6096

97+
{% endtab %}
98+
{% endtabs %}
99+
61100
Notice how the class extends the traits with the `extends` keyword.
62101

63102
Similarly, here’s a `Cat` class that implements those same traits while also overriding two of the concrete methods it inherits:
64103

104+
{% tabs traits-override class=tabs-scala-version %}
105+
{% tab 'Scala 2' for=traits-override %}
106+
107+
```scala
108+
class Cat(name: String) extends Speaker with TailWagger with Runner {
109+
def speak(): String = "Meow"
110+
override def startRunning(): Unit = println("Yeah ... I don’t run")
111+
override def stopRunning(): Unit = println("No need to stop")
112+
}
113+
```
114+
115+
{% endtab %}
116+
117+
{% tab 'Scala 3' for=traits-override %}
118+
65119
```scala
66120
class Cat(name: String) extends Speaker, TailWagger, Runner:
67121
def speak(): String = "Meow"
68122
override def startRunning(): Unit = println("Yeah ... I don’t run")
69123
override def stopRunning(): Unit = println("No need to stop")
70124
```
71125

126+
{% endtab %}
127+
{% endtabs %}
128+
72129
These examples show how those classes are used:
73130

131+
{% tabs traits-use class=tabs-scala-version %}
132+
{% tab 'Scala 2' for=traits-use %}
133+
134+
```scala
135+
val d = new Dog("Rover")
136+
println(d.speak()) // prints "Woof!"
137+
138+
val c = new Cat("Morris")
139+
println(c.speak()) // "Meow"
140+
c.startRunning() // "Yeah ... I don’t run"
141+
c.stopRunning() // "No need to stop"
142+
```
143+
144+
{% endtab %}
145+
146+
{% tab 'Scala 3' for=traits-use %}
147+
74148
```scala
75149
val d = Dog("Rover")
76150
println(d.speak()) // prints "Woof!"
@@ -81,15 +155,35 @@ c.startRunning() // "Yeah ... I don’t run"
81155
c.stopRunning() // "No need to stop"
82156
```
83157

158+
{% endtab %}
159+
{% endtabs %}
160+
84161
If that code makes sense---great, you’re comfortable with traits as interfaces.
85162
If not, don’t worry, they’re explained in more detail in the [Domain Modeling][data-1] chapter.
86163

87-
88164
### Classes
89165

90166
Scala _classes_ are used in OOP-style programming.
91167
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:
92168

169+
{% tabs class_1 class=tabs-scala-version %}
170+
{% tab 'Scala 2' for=class_1 %}
171+
172+
```scala
173+
class Person(var firstName: String, var lastName: String) {
174+
def printFullName() = println(s"$firstName $lastName")
175+
}
176+
177+
val p = new Person("John", "Stephens")
178+
println(p.firstName) // "John"
179+
p.lastName = "Legend"
180+
p.printFullName() // "John Legend"
181+
```
182+
183+
{% endtab %}
184+
185+
{% tab 'Scala 3' for=class_1 %}
186+
93187
```scala
94188
class Person(var firstName: String, var lastName: String):
95189
def printFullName() = println(s"$firstName $lastName")
@@ -100,15 +194,32 @@ p.lastName = "Legend"
100194
p.printFullName() // "John Legend"
101195
```
102196

197+
{% endtab %}
198+
{% endtabs %}
199+
103200
Notice that the class declaration creates a constructor:
104201

202+
{% tabs class_2 class=tabs-scala-version %}
203+
{% tab 'Scala 2' for=class_2 %}
204+
205+
```scala
206+
// this code uses that constructor
207+
val p = new Person("John", "Stephens")
208+
```
209+
210+
{% endtab %}
211+
212+
{% tab 'Scala 3' for=class_2 %}
213+
105214
```scala
106215
// this code uses that constructor
107216
val p = Person("John", "Stephens")
108217
```
109218

110-
Constructors and other class-related topics are covered in the [Domain Modeling][data-1] chapter.
219+
{% endtab %}
220+
{% endtabs %}
111221

222+
Constructors and other class-related topics are covered in the [Domain Modeling][data-1] chapter.
112223

113224
## FP Domain Modeling
114225

@@ -118,23 +229,59 @@ I didn’t include that because I didn’t know if enums are intended
118229
to replace the Scala2 “sealed trait + case class” pattern. How to resolve?
119230
{% endcomment %}
120231

121-
When writing code in an FP style, you’ll use these constructs:
232+
When writing code in an FP style, you’ll use these concepts:
122233

123-
- Enums to define ADTs
124-
- Case classes
125-
- Traits
234+
- Algebraic Data Types to define the data
235+
- Traits for functionality on the data.
126236

237+
### Enumerations and Sum Types
127238

128-
### Enums
239+
Sum types are one way to model algebraic data types (ADTs) in Scala.
240+
241+
They are used when data can be represented with different choices.
129242

130-
The `enum` construct is a great way to model algebraic data types (ADTs) in Scala 3.
131243
For instance, a pizza has three main attributes:
132244

133245
- Crust size
134246
- Crust type
135247
- Toppings
136248

137-
These are concisely modeled with enums:
249+
These are concisely modeled with enumerations, which are sum types that only contain singleton values:
250+
251+
{% tabs enum_1 class=tabs-scala-version %}
252+
{% tab 'Scala 2' for=enum_1 %}
253+
254+
In Scala 2 `sealed` classes and `case object` are combined to define an enumeration:
255+
256+
```scala
257+
sealed abstract class CrustSize
258+
object CrustSize {
259+
case object Small extends CrustSize
260+
case object Medium extends CrustSize
261+
case object Large extends CrustSize
262+
}
263+
264+
sealed abstract class CrustType
265+
object CrustType {
266+
case object Thin extends CrustType
267+
case object Thick extends CrustType
268+
case object Regular extends CrustType
269+
}
270+
271+
sealed abstract class Topping
272+
object Topping {
273+
case object Cheese extends Topping
274+
case object Pepperoni extends Topping
275+
case object BlackOlives extends Topping
276+
case object GreenOlives extends Topping
277+
case object Onions extends Topping
278+
}
279+
```
280+
281+
{% endtab %}
282+
{% tab 'Scala 3' for=enum_1 %}
283+
284+
Scala 3 offers the `enum` construct for defining enumerations:
138285

139286
```scala
140287
enum CrustSize:
@@ -147,7 +294,31 @@ enum Topping:
147294
case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions
148295
```
149296

150-
Once you have an enum you can use it in all of the ways you normally use a trait, class, or object:
297+
{% endtab %}
298+
{% endtabs %}
299+
300+
Once you have an enumeration you can import its members as ordinary values:
301+
302+
{% tabs enum_2 class=tabs-scala-version %}
303+
{% tab 'Scala 2' for=enum_2 %}
304+
305+
```scala
306+
import CrustSize._
307+
val currentCrustSize = Small
308+
309+
// enums in a `match` expression
310+
currentCrustSize match {
311+
case Small => println("Small crust size")
312+
case Medium => println("Medium crust size")
313+
case Large => println("Large crust size")
314+
}
315+
316+
// enums in an `if` statement
317+
if (currentCrustSize == Small) println("Small crust size")
318+
```
319+
320+
{% endtab %}
321+
{% tab 'Scala 3' for=enum_2 %}
151322

152323
```scala
153324
import CrustSize.*
@@ -163,20 +334,42 @@ currentCrustSize match
163334
if currentCrustSize == Small then println("Small crust size")
164335
```
165336

166-
Here’s another example of how to create and use an ADT with Scala:
337+
{% endtab %}
338+
{% endtabs %}
339+
340+
Here’s another example of how to create a sum type with Scala, this would not be called an enumeration because the `Succ` case has parameters:
341+
342+
{% tabs enum_3 class=tabs-scala-version %}
343+
{% tab 'Scala 2' for=enum_3 %}
344+
345+
```scala
346+
sealed abstract class Nat
347+
object Nat {
348+
case object Zero extends Nat
349+
case class Succ(pred: Nat) extends Nat
350+
}
351+
```
352+
353+
Sum Types are covered in detail in the [Domain Modeling]({% link _overviews/scala3-book/domain-modeling-tools.md %}) section of this book.
354+
355+
{% endtab %}
356+
{% tab 'Scala 3' for=enum_3 %}
167357

168358
```scala
169359
enum Nat:
170360
case Zero
171361
case Succ(pred: Nat)
172362
```
173363

174-
Enums are covered in detail in the [Domain Modeling][data-1] section of this book, and in the [Reference documentation]({{ site.scala3ref }}/enums/enums.html).
364+
Enums are covered in detail in the [Domain Modeling]({% link _overviews/scala3-book/domain-modeling-tools.md %}) section of this book, and in the [Reference documentation]({{ site.scala3ref }}/enums/enums.html).
175365

366+
{% endtab %}
367+
{% endtabs %}
176368

177-
### Case classes
369+
### Product Types
370+
371+
A product type is an algebraic data type (ADT) that only has one shape, for example a singleton object, represented in Scala by a `case` object; or an immutable structure with accessible fields, represented by a `case` class.
178372

179-
The Scala `case` class lets you model concepts with immutable data structures.
180373
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.
181374
When the compiler sees the `case` keyword in front of a `class` it has these effects and benefits:
182375

@@ -187,7 +380,6 @@ When the compiler sees the `case` keyword in front of a `class` it has these eff
187380
- `equals` and `hashCode` methods are generated to implement structural equality.
188381
- A default `toString` method is generated, which is helpful for debugging.
189382

190-
191383
{% comment %}
192384
NOTE: Julien had a comment about how he decides when to use case classes vs classes. Add something here?
193385
{% endcomment %}
@@ -196,6 +388,9 @@ You _can_ manually add all of those methods to a class yourself, but since those
196388

197389
This code demonstrates several `case` class features:
198390

391+
{% tabs case-class %}
392+
{% tab 'Scala 2 and 3' for=case-class %}
393+
199394
```scala
200395
// define a case class
201396
case class Person(
@@ -219,8 +414,9 @@ val p2 = p.copy(name = "Elton John")
219414
p2 // : Person = Person(Elton John,Singer)
220415
```
221416

222-
See the [Domain Modeling][data-1] sections for many more details on `case` classes.
223-
417+
{% endtab %}
418+
{% endtabs %}
224419

420+
See the [Domain Modeling][data-1] sections for many more details on `case` classes.
225421

226422
[data-1]: {% link _overviews/scala3-book/domain-modeling-tools.md %}

0 commit comments

Comments
 (0)