Scala runs on...

  • JVM
  • JavaScript in your browser
  • Natively with LLVM beta

Scala in a Nutshell

click the boxes below to see Scala in action!

Seamless Java Interop

Scala runs on the JVM, so Java and Scala stacks can be freely mixed for totally seamless integration.

Type Inference

So the type system doesn’t feel so static. Don’t work for the type system. Let the type system work for you!

Concurrency & Distribution

Use data-parallel operations on collections, use actors for concurrency and distribution, or futures for asynchronous programming.

Author.scala
class Author(val firstName: String,
    val lastName: String) extends Comparable[Author] {

  override def compareTo(that: Author) = {
    val lastNameComp = this.lastName compareTo that.lastName
    if (lastNameComp != 0) lastNameComp
    else this.firstName compareTo that.firstName
  }
}

object Author {
  def loadAuthorsFromFile(file: java.io.File): List[Author] = ???
}
App.java
import static scala.collection.JavaConversions.asJavaCollection;

public class App {
    public List<Author> loadAuthorsFromFile(File file) {
        return new ArrayList<Author>(asJavaCollection(
            Author.loadAuthorsFromFile(file)));
    }

    public void sortAuthors(List<Author> authors) {
        Collections.sort(authors);
    }

    public void displaySortedAuthors(File file) {
        List<Author> authors = loadAuthorsFromFile(file);
        sortAuthors(authors);
        for (Author author : authors) {
            System.out.println(
                author.lastName() + ", " + author.firstName());
        }
    }
}

Combine Scala and Java seamlessly

Scala classes are ultimately JVM classes. You can create Java objects, call their methods and inherit from Java classes transparently from Scala. Similarly, Java code can reference Scala classes and objects.


In this example, the Scala class Author implements the Java interface Comparable<T> and works with Java Files. The Java code uses a method from the companion object Author, and accesses fields of the Author class. It also uses JavaConversions to convert between Scala collections and Java collections.

Type inference
scala> class Person(val name: String, val age: Int) {
     |   override def toString = s"$name ($age)"
     | }
defined class Person

scala> def underagePeopleNames(persons: List[Person]) = {
     |   for (person <- persons; if person.age < 18)
     |     yield person.name
     | }
underagePeopleNames: (persons: List[Person])List[String]

scala> def createRandomPeople() = {
     |   val names = List("Alice", "Bob", "Carol",
     |       "Dave", "Eve", "Frank")
     |   for (name <- names) yield {
     |     val age = (Random.nextGaussian()*8 + 20).toInt
     |     new Person(name, age)
     |   }
     | }
createRandomPeople: ()List[Person]

scala> val people = createRandomPeople()
people: List[Person] = List(Alice (16), Bob (16), Carol (19), Dave (18), Eve (26), Frank (11))

scala> underagePeopleNames(people)
res1: List[String] = List(Alice, Bob, Frank)

Let the compiler figure out the types for you

The Scala compiler is smart about static types. Most of the time, you need not tell it the types of your variables. Instead, its powerful type inference will figure them out for you.

In this interactive REPL session (Read-Eval-Print-Loop), we define a class and two functions. You can observe that the compiler infers the result types of the functions automatically, as well as all the intermediate values.

Concurrent/Distributed
val x = Future { someExpensiveComputation() }
val y = Future { someOtherExpensiveComputation() }
val z = for (a <- x; b <- y) yield a*b
for (c <- z) println("Result: " + c)
println("Meanwhile, the main thread goes on!")

Go Concurrent or Distributed with Futures & Promises

In Scala, futures and promises can be used to process data asynchronously, making it easier to parallelize or even distribute your application.

In this example, the Future{} construct evaluates its argument asynchronously, and returns a handle to the asynchronous result as a Future[Int]. For-comprehensions can be used to register new callbacks (to post new things to do) when the future is completed, i.e., when the computation is finished. And since all this is executed asynchronously, without blocking, the main program thread can continue doing other work in the meantime.

Traits

Combine the flexibility of Java-style interfaces with the power of classes. Think principled multiple-inheritance.

Pattern Matching

Think “switch” on steroids. Match against class hierarchies, sequences, and more.

Higher-order functions

Functions are first-class objects. Compose them with guaranteed type safety. Use them anywhere, pass them to anything.

Traits
abstract class Spacecraft {
  def engage(): Unit
}
trait CommandoBridge extends Spacecraft {
  def engage(): Unit = {
    for (_ <- 1 to 3)
      speedUp()
  }
  def speedUp(): Unit
}
trait PulseEngine extends Spacecraft {
  val maxPulse: Int
  var currentPulse: Int = 0
  def speedUp(): Unit = {
    if (currentPulse < maxPulse)
      currentPulse += 1
  }
}
class StarCruiser extends Spacecraft
                     with CommandoBridge
                     with PulseEngine {
  val maxPulse = 200
}

Flexibly Combine Interface & Behavior

In Scala, multiple traits can be mixed into a class to combine their interface and their behavior.

Here, a StarCruiser is a Spacecraft with a CommandoBridge that knows how to engage the ship (provided a means to speed up) and a PulseEngine that specifies how to speed up.

Switch on the structure of your data

In Scala, case classes are used to represent structural data types. They implicitly equip the class with meaningful toString, equals and hashCode methods, as well as the ability to be deconstructed with pattern matching.


In this example, we define a small set of case classes that represent binary trees of integers (the generic version is omitted for simplicity here). In inOrder, the match construct chooses the right branch, depending on the type of t, and at the same time deconstructs the arguments of a Node.

Pattern matching
// Define a set of case classes for representing binary trees.
sealed abstract class Tree
case class Node(elem: Int, left: Tree, right: Tree) extends Tree
case object Leaf extends Tree

// Return the in-order traversal sequence of a given tree.
def inOrder(t: Tree): List[Int] = t match {
  case Node(e, l, r) => inOrder(l) ::: List(e) ::: inOrder(r)
  case Leaf          => List()
}

Go Functional with Higher-Order Functions

In Scala, functions are values, and can be defined as anonymous functions with a concise syntax.

Scala
val people: Array[Person]

// Partition `people` into two arrays `minors` and `adults`.
// Use the anonymous function `(_.age < 18)` as a predicate for partitioning.
val (minors, adults) = people partition (_.age < 18)
Java
List<Person> people;

List<Person> minors = new ArrayList<Person>(people.size());
List<Person> adults = new ArrayList<Person>(people.size());
for (Person person : people) {
    if (person.getAge() < 18)
        minors.add(person);
    else
        adults.add(person);
}

Run Scala in your browser

Scastie is Scala + sbt in your browser! You can use any version of Scala, or even alternate backends such as Dotty, Scala.js, Scala Native, and Typelevel Scala. You can use any published library. You can save and share Scala programs/builds with anybody.

Run Scala code interactively

Online Courses

Functional Programming Principles in Scala

  • Free (optional paid certificate)
  • New sessions starting every 2 weeks!

Functional Program Design in Scala

  • Free (optional paid certificate)
  • New sessions starting every 2 weeks!

Parallel Programming

  • Free (optional paid certificate)
  • New sessions starting every 2 weeks!

Big Data Analysis with Scala and Spark

  • Free (optional paid certificate)
  • New sessions starting every 2 weeks!

Functional Programming in Scala Capstone

  • Free (optional paid certificate)
  • New sessions starting every 2 weeks!

Programming Reactive Systems

  • Free (optional paid certificate)
  • New sessions starting every 2 weeks!

Upcoming Training

Scala ecosystem

The Scala Library Index (or Scaladex) is a representation of a map of all published Scala libraries. With Scaladex, a developer can now query more than 175,000 releases of Scala libraries. Scaladex is officially supported by Scala Center.

The Scala Library Index

What’s New

BLOG

Import Suggestions in Scala 3

Tuesday, May 5, 2020

Implicits let the compiler “write” significant parts of a program for you. For instance, the compiler can summon JSON serializers and deserializers for a complete type hierarchy.

However, working with implicits can be a difficult experience. Thankfully, the Scala 3 compiler dramatically improves the quality of the error messages shown in case of missing implicits so that it is easier to see where an implicit argument could not be inferred by the compiler, and how to fix the problem.

This article shows these improvements in action in concrete examples of code.

Motivation

In the 2018 Scala developer survey, the word “implicit” showed up in the question “In learning Scala, what was the biggest challenge you faced?”

cloud of words

We also saw that 35% of the respondents of the 2019 developer survey signaled that dealing with missing implicits was a main pain point in their daily workflow. Furthermore, they signaled that the two most painful issues they had when working with implicits were “finding which parameters have been inferred”, and “fixing ‘implicit not found’ errors”. Last but not least, the word that was most mentioned by the respondents to describe their other pain points related to implicits is the word “import”.

A few months ago, Jamie Thompson engaged a discussion with the community to understand better the problem. We identified that “conditional” implicits were probably involved in most of the issues. Conditional implicits are implicit definitions that themselves take implicit parameters. For instance, an implicit Ordering[List[A]] instance requiring that there is an implicit Ordering[A] instance:

implicit def orderingList[A](implicit orderingA: Ordering[A]): Ordering[List[A]]

Consider what happens when you call a method that requires an implicit Ordering[List[Int]]. The compiler searches for such an implicit definition and finds that the implicit definition orderingList could be a good candidate provided that there is an implicit instance of type Ordering[Int]. The compiler searches for such an implicit definition (which it finds in the Ordering companion object) and summons the initial Ordering[List[Int]] implicit argument by supplying the Ordering[Int] instance to the implicit definition orderingList. In this example we have only two implicit definitions involved, but in practice conditional implicit definitions can form longer chains.

Now, let’s have a look at what happens in Scala 2 if something fails somewhere in the chain. For example, when we call a method that requires an implicit Ordering[List[Foo]] but there is no implicit Ordering[Foo] instance:

class Foo

List(List(new Foo)).sorted

The Scala 2 compiler produces the following error:

No implicit Ordering defined for List[Foo].

The error message says that no implicit Ordering instance for type List[Foo] could be found. However, this message is not very precise. The actual reason of the failure is that there was no implicit Ordering instance for type Foo. Because of that, no implicit Ordering instance for type List[Foo] could be summoned by the compiler.

This is the first concrete problem we identified: error messages don’t tell precisely where in the chain was the missing implicit.

The second problem we identified is that issues related to implicits are often due to missing imports, but finding what to import is hard.

The next sections show how Scala 3 addresses both issues by providing more detailed error messages and actionable feedback.

Showing Where the Problem Is

In case an implicit argument could not be found in a chain of implicit definitions, the Scala 3 compiler now shows the complete chain it could build until an argument could not be found. Here is an example that mimics the Ordering[List[A]] problem mentioned above:

// `Order` type class definition, similar to the `Ordering` type class of
// the standard library
trait Order[A] {
  def compare(a1: A, a2: A): Int
}

object Order {
  // Provides an implicit instance of type `Order[List[A]]` under the condition
  // that there is an implicit instance of type `Order[A]`
  implicit def orderList[A](implicit orderA: Order[A]): Order[List[A]] = ???
}

// Sorts a `list` of elements of type `A` with their implicit `order` relation
def sort[A](list: List[A])(implicit order: Order[A]): List[A] = ???

// A class `Foo`
class Foo

// Let’s try to sort a `List[List[Foo]]`
sort(List(List(new Foo)))

The Scala 3 compiler gives the following error message:

Error:
|    sort(List(List(new Foo)))
|                             ^
|no implicit argument of type Order[List[Foo]] was found for parameter order of method sort.
|I found:
|
|    Order.orderList[A](/* missing */implicitly[Order[Foo]])
|
|But no implicit values were found that match type Order[Foo].

The error message now shows how far the compiler went by chaining implicit definitions, and where it eventually stopped because an implicit argument could not be found. In our case, we see that the compiler tried the definition orderList but then didn’t find an implicit Order[Foo]. So, we know that to fix the problem we need to implement an implicit Order[Foo].

For the record, the idea of showing the complete chain of implicits was pioneered by Torsten Schmits in the splain compiler plugin, which is available in Scala 2.

Suggesting How to Fix the Problem

In case the missing implicit arguments are defined somewhere but need to be imported, the Scala 3 compiler suggests to you import clauses that might fix the problem.

Here is an example that illustrates this:

// A class `Bar`
class Bar

// An implicit `Order[Bar]`
// (note that it is _not_ in the `Bar` companion object)
object Implicits {
  implicit def orderBar: Order[Bar] = ???
}

// Let’s try to sort a `List[Bar]`
sort(List(new Bar))

The compiler produces the following error:

Error:
|    sort(List(new Bar))
|                       ^
|no implicit argument of type Order[Bar] was found for parameter order of method sort
|
|The following import might fix the problem:
|
|  import Implicits.orderBar

Instead of just reporting that an implicit argument was not found, the Scala 3 compiler looks for implicit definitions that could have provided the missing argument. In our case, the compiler suggests importing Implicits.orderBar, which does fix the compilation error.

A More Sophisticated Example

An iconic example is the operation traverse from the library cats. This operation is defined as a conditional extension method on any type F[A] for which there exists an implicit Traverse[F] instance. The operation takes a function A => G[B] and an implicit parameter of type Applicative[G].

In practice, this very generic operation is used in various specific contexts. For instance, to turn a list of validation results into a single validation result containing a list, or to turn an optional asynchronous result into an asynchronous optional result. However, because it is a conditional extension method, and because it takes an implicit parameter, finding the correct imports to make it work can be difficult.

You don’t need to be familiar with the type classes Traverse and Applicative to understand the remaining of this article. There are only two things to know about the operation traverse:

  1. it is available on a value of type List[A] if there is an implicit instance of type Traverse[List] (it is a conditional extension method),
  2. the operation itself takes an implicit parameter of type Applicative.

This can be modeled as follows in Scala 3, using extension methods:

// The `Traverse` type class, which provides a `traverse` operation as an extension method
trait Traverse[F[_]] {
  def [G[_], A, B](fa: F[A]).traverse(f: A => G[B])(implicit applicative: Applicative[G]): G[B]
}

// The applicative type class (its actual definition does not matter for the example)
trait Applicative[F[_]]

Let’s assume that a given instance of type Traverse[List] and a given instance of type Applicative[Option] are defined in an object Givens (given instances are the new way to define implicit instances in Scala 3):

object Givens {
  given traverseList as Traverse[List] = ???
  given applicativeOption as Applicative[Option] = ???
}

Now that we have set the context, let’s see a concrete example of use of traverse.

First, consider a function parseUser, that parses a User from a String (e.g., containing a JSON object):

def parseUser(string: String): Option[User]

The return type of the function is Option[User], which can represent a parsing failure with None, or a parsing success with Some.

We can use the operation traverse and the function parseUser (which parses one user) to implement a function parseUsers, which parses a list of users. The signature of this function is the following:

def parseUsers(strings: List[String]): Option[List[User]]

Again, the result type is Option[List[User]] so that a parsing failure can be represented (it returns None if any of the strings failed to be parsed).

The function can be implemented as follows:

def parseUsers(strings: List[String]): Option[List[User]] =
  strings.traverse(parseUser)

However, if we try to compile this code with Scala 2 we get the following error:

value traverse is not a member of List[String]
did you mean reverse?

The error message doesn’t help to find a solution.

Compiling with Scala 3, on the other hand, provides much better assistance:

[E008] Not Found Error:
|    strings.traverse(parseUser)
|    ^^^^^^^^^^^^^^^^
|value traverse is not a member of List[String], but could be made available as an extension method.
|
|The following import might make progress towards fixing the problem:
|
|  import Givens.traverseList

Let’s apply the suggestion and import Givens.traverseList. Now, the compiler provides the following error:

Error:
|    strings.traverse(parseUser)
|                               ^
|no implicit argument of type Applicative[Option] was found for parameter applicative of method traverse in trait Traverse
|
|The following import might fix the problem:
|
|  import Givens.applicativeOption

If we apply the new suggestion (importing Givens.applicativeOption) our program compiles!

The Scala 3 compiler first suggested importing Givens.traverseList, so that the extension method traverse becomes available. Then, it suggested importing Givens.applicativeOption, which was required to call the traverse operation.

Summary

Dealing with “implicit not found” errors in Scala 2 can be difficult, in particular because developers don’t see precisely which implicit argument could not be found in a chain of implicit definitions, or because they don’t know what are the required imports to add to their program.

Scala 3 addresses these two pain points by:

  • providing more precise error messages, showing exactly which implicit argument could not be found in a chain of implicit definitions,
  • providing actionable feedback, suggesting import clauses that might provide the missing implicits.

You can already try this feature in Dotty 0.24.0-RC1.

Twitter Feed

See more tweets, or

Follow Scala on twitter

The Scala language is maintained by

  • Scala Center
  • Lightbend

Scala Center is supported by

EPFL IBM Verizon Goldman Sachs 47 Degrees SAP Twitter Your company