My favorite new features of Scala 2.13 standard library

Linas Medžiūnas
Wix Engineering
Published in
4 min readJul 17, 2019

A new release of Scala programming language, 2.13, has been announced recently. It is time to look at what improvements it has brought to us. In this post I will focus on the changes in the standard library, so this can be considered a follow up to my previous post, The little gems of Scala standard library. Many of the features described here are not mentioned in the official release notes (where you can find a broad description of general language, collection and compiler improvements).

1. scala.util.Using

This finally gives us an implementation of “Loan” pattern, allowing safe and automatic closing of single (or multiple) resources that are used within some block of code. In this context, a resource is anything that implements either scala.util.Using.Releasable or java.lang.AutoCloseable. Normally you would use it in the same situations as you would use try-with-resources in Java.

Example:

import scala.util.Using
import java.io.{FileReader, FileWriter}
Using.resources(
new FileReader("input.txt"),
new FileWriter("output.txt")) { (reader, writer) =>
???
}

Note: if you are not moving to Scala 2.13 just yet, but would like to have a decent implementation of try-with-resource in Scala, I suggest reading this useful post by my colleague.

2. minOption / maxOption (also minByOption / maxByOption)

Until 2.13, we had min / max / minBy / maxBy which could only be used on non empty collections (throwing an UnsupportedOperationException if the collection was empty). In many cases this was inconvenient and required non-emptiness check before making those calls. The new xxOption methods are safe to call on any collections, and return a None in case of an empty input. Also, this makes the API more consistent, since there already has been headOption and lastOption for a long time.

3. scala.collection.Stepper

Stepper is a newer generation of Iterator abstraction, exposing similar semantics (hasStep instead of hasNext, nextStep instead of next). Unlike the well known Iterator, we have specialized variations of it (IntStepper, LongStepper, DoubleStepper) which enable efficient iterating over collections that hold unboxed primitives.

Also, Stepper provides interoperability with some of the modern features of Java introduced in version 8, such as java.util.Stream, java.util.PrimitiveIterator and java.util.Spliterator.

You can get an instance of Stepper based on any Scala collection, simply by calling a method .stepper on it.

4. sizeIs

Some collection types, such as linked List and BitSet, do not have an efficient (constant time) way of getting their size. And in many cases, the size itself is not needed, we may only be interested to compare the size to some concrete number (or to the size of another collection). sizeIs allows doing size comparisons with time complexity O(n min m) instead of O(n), by doing only a limited traversal of the collection (until the given condition can be checked).

Example that validates the size of linked list, without fully traversing it:

val xs = List.fill(5000)(scala.util.Random.nextInt)if (xs.sizeIs > 100) { // traverses no more than 101 element
throw new IllegalArgumentException("Too many numbers!")
}

5. mapInPlace

Supported on some of the mutable collections. Probably relevant if you have performance (or memory) concerns (which is very likely the case, if you have decided to use mutable collections). Updates the original collection in place, instead of allocating a new one (like map does).

6. minAfter / maxBefore

The new methods minAfter / maxBefore of TreeSet / TreeMap classes support efficient (O(log n), thanks to the underlying implementation of RedBlackTree) lookup of the smallest value that is greater or equal (or the greatest value that is less or equal) the given value (same as higher / lower methods of java.util.TreeSet).

7. scala.collection.mutable.ArrayDeque

An implementation of double-ended queue that provides functionality similar to java.util.ArrayDeque, just with much richer API, as we are used to have in other Scala collections.

8. Deprecated Map.filterKeys and Map.mapValues

I know, it is strange to have deprecation listed as one of the top features. So let me explain. For whatever historical reasons, these two methods on (otherwise great) implementation of Scala Map are not strict (meaning that the lambda function passed as an argument to these methods is not being evaluated immediately, but on every access to the resulting Map instead). And in some cases, such lazy evaluation would actually be useful. The problem is that this is not consistent with other methods of Map. Also, it is not apparent from method names or signatures (even though return type has been changed from Map to MapView in 2.13). As a result, someone using those methods without checking the documentation would risk into getting some unexpected, hard to understand effects. In the worst case, this could cause some side effects (or some expensive calls) to happen more than once. Personally, I have always considered this to be a design issue of Scala Map. Each time I had to explain this (as in, “the bad parts”) when onboarding a Scala newbie, I felt uncomfortable. So, I’m really happy to see this issue being addressed in the new release.

Many of the features mentioned in this post were not covered by the official release notes. Most likely there still is something more that I have missed. If you discover more interesting “hidden” features of Scala 2.13, please share them in the comments!

P. S. Release notes of Scala 2.13 have been updated to include the changes mentioned in this post. I was really happy to hear that, even though it made some of my statements above no longer accurate 😌.

--

--

Linas Medžiūnas
Wix Engineering

Software engineer at Chronosphere.io; unretired competitive programmer; 2x IOI gold medal winner; curious about Quantum algorithms.