From 4563c7f6c6f1d5d4015bfae948730a1fced81649 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 16 Aug 2022 16:56:18 +0200 Subject: [PATCH 1/2] Update singleton-objects.md to use scala 2/3 tabs --- _tour/singleton-objects.md | 105 +++++++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 3 deletions(-) diff --git a/_tour/singleton-objects.md b/_tour/singleton-objects.md index 037e855b0b..dfca309250 100644 --- a/_tour/singleton-objects.md +++ b/_tour/singleton-objects.md @@ -16,23 +16,52 @@ As a top-level value, an object is a singleton. As a member of an enclosing class or as a local value, it behaves exactly like a lazy val. # Defining a singleton object An object is a value. The definition of an object looks like a class, but uses the keyword `object`: + + +{% tabs object-definition-box %} + +{% tab 'Scala 2 and 3' for=object-definition-box %} ```scala mdoc object Box ``` +{% endtab %} + +{% endtabs %} Here's an example of an object with a method: -``` +{% tabs singleton-logger-example class=tabs-scala-version %} + +{% tab 'Scala 2' for=singleton-logger-example %} + +```scala package logging object Logger { def info(message: String): Unit = println(s"INFO: $message") } ``` +{% endtab %} + +{% tab 'Scala 3' for=singleton-logger-example %} + +```scala +package logging + +object Logger: + def info(message: String): Unit = println(s"INFO: $message") +``` +{% endtab %} + +{% endtabs %} + The method `info` can be imported from anywhere in the program. Creating utility methods like this is a common use case for singleton objects. Let's see how to use `info` in another package: +{% tabs singleton-usage-example class=tabs-scala-version %} -``` +{% tab 'Scala 2' for=singleton-usage-example %} + +```scala import logging.Logger.info class Project(name: String, daysToComplete: Int) @@ -43,6 +72,24 @@ class Test { info("Created projects") // Prints "INFO: Created projects" } ``` +{% endtab %} + +{% tab 'Scala 3' for=singleton-usage-example %} + +```scala +import logging.Logger.info + +class Project(name: String, daysToComplete: Int) + +class Test: + val project1 = new Project("TPS Reports", 1) + val project2 = new Project("Website redesign", 5) + info("Created projects") // Prints "INFO: Created projects" +``` +{% endtab %} + +{% endtabs %} + The `info` method is visible because of the import statement, `import logging.Logger.info`. @@ -53,7 +100,10 @@ Note: If an `object` is not top-level but is nested in another class or object, ## Companion objects An object with the same name as a class is called a _companion object_. Conversely, the class is the object's companion class. A companion class or object can access the private members of its companion. Use a companion object for methods and values which are not specific to instances of the companion class. -``` +{% tabs companion-object-circle class=tabs-scala-version %} + +{% tab 'Scala 2' for=companion-object-circle %} +```scala import scala.math._ case class Circle(radius: Double) { @@ -69,10 +119,34 @@ val circle1 = Circle(5.0) circle1.area ``` +{% endtab %} + +{% tab 'Scala 3' for=companion-object-circle %} +```scala +import scala.math._ + +case 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 +``` +{% endtab %} + +{% endtabs %} The `class Circle` has a member `area` which is specific to each instance, and the singleton `object Circle` has a method `calculateArea` which is available to every instance. The companion object can also contain factory methods: +{% tabs companion-object-email class=tabs-scala-version %} + +{% tab 'Scala 2' for=companion-object-email %} ```scala mdoc class Email(val username: String, val domainName: String) @@ -95,6 +169,31 @@ scalaCenterEmail match { case None => println("Error: could not parse email") } ``` +{% endtab %} + +{% tab 'Scala 3' for=companion-object-email %} +```scala mdoc +class Email(val username: String, val domainName: String) + +object Email: + def fromString(emailString: String): Option[Email] = + emailString.split('@') match + case Array(a, b) => Some(new Email(a, b)) + case _ => None + +val scalaCenterEmail = Email.fromString("scala.center@epfl.ch") +scalaCenterEmail match + case Some(email) => println( + s"""Registered an email + |Username: ${email.username} + |Domain name: ${email.domainName} + """.stripMargin) + case None => println("Error: could not parse email") +``` +{% endtab %} + +{% endtabs %} + The `object Email` contains a factory `fromString` which creates an `Email` instance from a String. We return it as an `Option[Email]` in case of parsing errors. Note: If a class or object has a companion, both must be defined in the same file. To define companions in the REPL, either define them on the same line or enter `:paste` mode. From aadd3b7250865af5c5b15a423dad7ecb64846829 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 17 Aug 2022 09:11:50 +0200 Subject: [PATCH 2/2] Fix syntax in new examples --- _tour/singleton-objects.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/_tour/singleton-objects.md b/_tour/singleton-objects.md index dfca309250..db2cd27de8 100644 --- a/_tour/singleton-objects.md +++ b/_tour/singleton-objects.md @@ -82,8 +82,8 @@ import logging.Logger.info class Project(name: String, daysToComplete: Int) class Test: - val project1 = new Project("TPS Reports", 1) - val project2 = new Project("Website redesign", 5) + val project1 = Project("TPS Reports", 1) + val project2 = Project("Website redesign", 5) info("Created projects") // Prints "INFO: Created projects" ``` {% endtab %} @@ -104,7 +104,7 @@ An object with the same name as a class is called a _companion object_. Converse {% tab 'Scala 2' for=companion-object-circle %} ```scala -import scala.math._ +import scala.math.pow case class Circle(radius: Double) { import Circle._ @@ -123,10 +123,10 @@ circle1.area {% tab 'Scala 3' for=companion-object-circle %} ```scala -import scala.math._ +import scala.math.pow case class Circle(radius: Double): - import Circle._ + import Circle.* def area: Double = calculateArea(radius) object Circle: @@ -172,13 +172,13 @@ scalaCenterEmail match { {% endtab %} {% tab 'Scala 3' for=companion-object-email %} -```scala mdoc +```scala class Email(val username: String, val domainName: String) object Email: def fromString(emailString: String): Option[Email] = emailString.split('@') match - case Array(a, b) => Some(new Email(a, b)) + case Array(a, b) => Some(Email(a, b)) case _ => None val scalaCenterEmail = Email.fromString("scala.center@epfl.ch")