Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learn Scala Programming

You're reading from   Learn Scala Programming A comprehensive guide covering functional and reactive programming with Scala 2.13, Akka, and Lagom

Arrow left icon
Product type Paperback
Published in Oct 2018
Publisher Packt
ISBN-13 9781788836302
Length 498 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Slava Schmidt Slava Schmidt
Author Profile Icon Slava Schmidt
Slava Schmidt
Arrow right icon
View More author details
Toc

Table of Contents (19) Chapters Close

Preface 1. An Introduction to Scala 2.13 2. Understanding Types in Scala FREE CHAPTER 3. Deep Dive into Functions 4. Getting to Know Implicits and Type Classes 5. Property-Based Testing in Scala 6. Exploring Built-In Effects 7. Understanding Algebraic Structures 8. Dealing with Effects 9. Familiarizing Yourself with Basic Monads 10. A Look at Monad Transformers and Free Monad 11. An Introduction to the Akka and Actor Models 12. Building Reactive Applications with Akka Typed 13. Basics of Akka Streams 14. Project 1 - Building Microservices with Scala 15. Project 2 - Building Microservices with Lagom 16. Preparing the Environment and Running Code Samples 17. Assessments 18. Other Books You May Enjoy

New features of Scala 2.13

In this section, we will discuss a few small improvements in the new version, which are not related to the collections topic and don't really belong to some bigger topic, such as optional parsing for string literals, adding names-reporting functions to case classes, methods for chaining operations, and automatic resource-management.

Optional parsing for string literals

In Scala 2.13, StringOps has been extended with methods that return Option for string-literals parsing. Supported types include all numeric types and Boolean.

The new methods can greatly simplify the processing of user-provided data without the need to wrap the calls with the exception-handling, as shown in the following example:

scala> "10".toIntOption
res3: Option[Int] = Some(10)
scala> "TrUe".toBooleanOption
res4: Option[Boolean] = Some(true)
scala> val bool = "Not True"
bool: String = Not True
scala> bool.toBooleanOption
res5: Option[Boolean] = None

The optional Boolean parsing ignores the case of the argument the same way the exception-throwing toBoolean method does.

Products can report the names of their element

This feature probably will be mostly useful for the case classes as it makes possible some generic programming without the need to resort to reflection or macros.

The following examples demonstrate how the new productElementName(idx) method can be used to build a naive JSON serializer for simple case classes:

case class User(name: String, surname: String, email: String)

def naiveToJsonString(p: Product): String =
(for { i <- 0 until p.productArity } yield
s""""${p.productElementName(i)}": "${p.productElement(i)}"""")
.mkString("{ ", ", ", " }")

Obviously, this simple iteration does not take nesting and escaping into account, but it already can produce valid results in elementary cases:

scala> val user = User("John", "Doe", "jd@mail.me")
user: User = User(John,Doe,jd@mail.me)
scala> naiveToJsonString(user)
res1: String = { "name": "John", "surname": "Doe", "email": "jd@mail.me" }

Unfortunately, the method taking an index of the element throws an exception in the case that the index is invalid:

scala> user.productElementName(3)
java.lang.IndexOutOfBoundsException: 3
at User.productElementName(<console>:1)
... 38 elided

We will discuss why throwing exceptions is not the best approach, as well as viable alternatives, in Chapter 6, Exploring Built-In Effects.

Added methods for chaining operations

Via import scala.util.chaining._, it is now possible to add tap and pipe methods to instances of any type. The functionality is provided by an implicit conversion to ChainingOps. We will look at implicits in detail in Chapter 4, Getting to Know Implicits and Type Classes.

The pipe method applies a given function to the value and returns the result. It might be helpful in situations where it is desirable to convert nested function calls into the fluid-interface-like code. The following snippet shows an example of an imaginary user database with nested function calls chained via pipe.

Consider the following the database interface:

object UserDb {
def getById(id: Long): User = ???
def update(u: User): User = ???
def save(u: User): Boolean = ???
}

We could apply all three actions to the user at once:

import UserDb._
val userId = 1L
save(update(getById(userId)))

pipe allows us to represent this in a more readable format:

getById(userId).pipe(update).pipe(save)

Arguably the same (or an even clearer) result could be achieved by combining functions before applying them:

val doEverything = (getById _).andThen(update).andThen(save)
doEverything(userId)

We will look at functions in general, and function composition in particular, in Chapter 3, Deep Dive into Functions.

tap applies a function given as an argument solely for the side-effects it produces and returns the original value. It might be useful, for example, for logging purposes and the simplest kind of performance measurements.

The next snippet demonstrates an elementary side-effect-causing performance-tracking implementation that utilizes a global variable:

scala> import scala.util.chaining._
import scala.util.chaining._
scala> val lastTick = new java.util.concurrent.atomic.AtomicLong(0)
lastTick: java.util.concurrent.atomic.AtomicLong = 0
scala> def measure[A](a: A): Unit = {
| val now = System.currentTimeMillis()
| val before = lastTick.getAndSet(now)
| println(s"$a: ${now-before} ms elapsed")
| }
measure: [A](a: A)Unit
scala> def start(): Unit = lastTick.set(System.currentTimeMillis())
start: ()Unit
scala> start()
scala> val result = scala.io.StdIn.readLine().pipe(_.toIntOption).tap(measure)
None: 291 ms elapsed
result: Option[Int] = None
scala> val anotherResult = scala.io.StdIn.readLine().pipe(_.toIntOption).tap(measure)
Some(3456): 11356 ms elapsed
anotherResult: Option[Int] = Some(3456)

Here, we defined a global value of the AtomicLong type to store the last measured timestamp. Then we define a polymorphic measure method that captures the time between the moment of the last measurement and now, and a start method to reset the clock. After that, we can use the tap method to track the execution times of our actions.

We will talk about types and polymorphism in Chapter 2, Understanding Types in Scala, side-effects and more general concept of effects in Chapter 8, Dealing with Effects, and show drawbacks of having global variables and a global state in Chapter 9, Familiarizing Yourself with Basic Monads.

Automatic Resource Management

Scala 2.13 adds a practical way to automatically manage resources. We will discuss other ways to manage resources and implement dependency-injection in Chapter 9, Familiarizing Yourself with Basic Monads and 10. scala.util.Using allows us to do this in a familiar side-effecting way. All operations on the resource are wrapped in a Try, which we'll talk about in Chapter 6, Exploring Built-In Effects. If Exceptions is thrown, the first one is returned within a Try. The exception-handling is quite sophisticated in some corner cases and we invite the reader to consult ScalaDoc for a detailed description of it.

Using is a class that takes some resources as a by-name parameter. The resource can be anything that has a type class instance for scala.util.Resource available. Such an instance for java.lang.AutoCloseable is provided in the standard library. We will study type classes in Chapter 4, Getting to Know Implicits and Type Classes. Using also has a monadic interface, which allows us to combine multiple resources in for-comprehensions. We'll discuss monads in Chapter 9, Familiarizing Yourself with Basic Monads.

Here is an example of the practical application of Using. We will define a resource that implements AutoCloseable and a few of these resources in for-comprehension as a source of the data:

scala> import scala.util.{Try, Using}
import scala.util.{Try, Using}
scala> final case class Resource(name: String) extends AutoCloseable {
| override def close(): Unit = println(s"Closing $name")
| def lines = List(s"$name line 1", s"$name line 2")
| }
defined class Resource
scala> val List(r1, r2, r3) = List("first", "2", "3").map(Resource)
r1: Resource = Resource(first)
r2: Resource = Resource(2)
r3: Resource = Resource(3)

scala> val lines: Try[Seq[String]] = for {
| u1 <- Using(r1)
| u2 <- Using(r2)
| u3 <- Using(r3)
| } yield {
| u1.lines ++ u2.lines ++ u3.lines
| }
Closing 3
Closing 2
Closing first
lines: scala.util.Try[Seq[String]] = Success(List(first line 1, first line 2, 2 line 1, 2 line 2, 3 line 1, 3 line 2))

The output in the console demonstrates that the result contains lines from all of the resources, and the resources themselves are automatically closed in the reverse order.

Now, after this small warm-up, we are ready to dive into the foundation of version 2.13—the new collection library.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime