Scala

A collection of useful tips for Scala in combination with Metals and the official Neovim plugin. This can convert Neovim into a decent Scala editor that benefits from Metals’ rich feature set.

Create FAT jars

So called fat jars are self-contained jars that include all the Scala runtime stuff. They can run without a Scala installation using only a java VM. java -jar foo.jar where foo jar is a fat JAR generated with the sbt-assembly plugin.

  • https://www.baeldung.com/scala/sbt-fat-jar

Using Scala-CLI

Scala-CLI can be used to build and manage projects. It is not as powerful as sbt, but can be faster in some cases. It works well with metals for small single-module projects. Scala-CLI is still under heavy development and will see improvements in future releases. For large projects, particularly such with multiple modules, sbt might still be the better option.

Power mode

Power-mode is the synonym for the experimental mode in Scala-CLI. Power-mode offers more features, but some of them should be considered unstable and experimental. You can enable Power-mode with the --power command line argument or globally by running:

# enable power-mode globally (for the current user)
$ scala-cli config power true

Create a FAT Jar (aka assembly)

A fat JAR works exactly like in sbt. It creates a JAR that contains all dependencies including the Scala default library. It will run on any compatible JVM.

# build a fat JAR (aka assembly)
$ scala-cli package --force --assembly .

Note the --force parameter. It is generally needed by all Scala-CLI commands in order to overwrite existing builds.

Create a standalone Jar

This is basically the same as a FAT Jar, but there are some minor differences in the internal format. Like the FAT Jar, this is fully self-contained and will run on any compatible JVM using the -jar command line argument.

# build a standalone JAR
$ scala-cli package --force --standalone .

Using Scala-CLI to build native apps

Scala-CLI fully supports building native executables with scala-native. While this is still considered experimental software, it’s fairly complete and works fine for many smaller applications. Note that native does not always mean faster execution. In fact, some code could run faster when executed by the JVM, but native will almost always result in much faster startup times.

# build the application in the current directoy, using a scala-native optimized release build
$scala-cli package --force --native --native-mode release-full .

Using Scala-CLI to build native apps via Graal VM

This is a different way to produce standalone executables. It requires a compatible version of Graal which will be downloaded via coursier on-demand during the build process. This process cares about some issues related to Scala. It allows you to use Java libraries using reflection (a common problem when building native binaries) and also Scala’s LazyVal semantics. Scala-CLI will automatically generate the necessary manifest and configuration files for Graal’s native image generator.

scala-cli package --native-image --graalvm-version "21.0.2" --graalvm-java-version 21 .

Remember, use --force to overwrite an existing build. Use --graalvm-args to pass arguments to the Graal VM native image generator.

Language topics

Catching multiple exceptions with the same variable

This is possible via pattern alternatives, a detail of the very powerful pattern matching features in Scala.

try {
  ...
} catch {
  case e @ (_: IOException | _: RuntimeException) = { println(e.getMessage()) }
}

This will catch both exception types into e.

Time and Date stuff

// create a ZonedDateTime with a time zone information
val ts = ZonedDateTime.of(2000, 1, 6, 18, 14, 0, 0, ZoneId.of("UTC"))
// > 2000-01-06T18:14Z[UTC]
// translate into a different time zone
val translated = ts.withZoneSameInstant(ZoneId.of("Europe/Vienna"))
// 2000-01-06T19:14+01:00[Europe/Vienna]
val translated.toLocalDateTime()
// 2000-01-06T19:14:10