What is the difference between Scala runner and Scala CLI
Piotr Chabelski
Scala Engineer @ Scala CLI
Published: Mar 22, 2023|40 min read40 minutes read
Some of you may have heard that as of SIP-46, Scala CLI is to become the new default runner that will replace the old scala script. Upon receiving this information, many questions arise. Namely, what is Scala CLI and what is it good for? What does this change mean for the Scala community? Or, as many everyday Scala users could probably ask – what even is the scala runner?
We will try to answer these questions in the following run-through of the situation.
Please note that for the reader’s convenience, we will differentiate the old runner as scala and the new runner (Scala CLI) as scala-cli to give examples in this post, unless otherwise stated. Please also note that the new runner is already available as scala under the scala-experimental installation at the time of writing this post.
Since you are reading this, it is safe to assume you use Scala in some capacity in your life, be it for your job, university, pet project or any other environment. That means you probably also have the language installed on your local machine.
The average Scala installation gives you access to the following command-line applications:
scalac– the app used to interact with the Scala compiler
scalap – the Scala class file decoder
scaladoc – the command-line utility for generating Scaladoc
scala– the runner we’re discussing in this article
As the name implies, the scala runner is a command-line app that runs Scala code.
Given a simple HelloWorld.scala example:
1object Hello extends App {
2 println("Hello world")
3}
Using the runner, you could run the file straight from the command line like this:
1scala HelloWorld.scala
2# Hello world
Another commonly used feature would be entering the repl:
1scala
2# Welcome to Scala 3.2.1 (17.0.5, Java OpenJDK 64-Bit Server VM).
3# Type in expressions for evaluation. Or try :help.
Until now, the scala runner used to be primarily a power user tool. Its full scope of usefulness was not widely known. Its features have been relatively limited, not offering much beyond launching the repl and running .scala files. To do anything non-basic with Scala, you would immediately have to turn to a build tool, like SBT or Mill. And even when it comes to launching the REPL, most Scala coders have been using it from SBT, skipping the runner altogether. As a result, the utility has gradually been getting less and less attention. It seems like an unused opportunity since we are talking about an app installed along with the compiler on many Scala coders’ machines.
Scala CLI is meant to be a game changer, being the runner “with batteries included”, targeting not just power users but the whole Scala community.
Scala CLI is a relatively new open-source command-line tool from VirtusLab, developed since mid-2021. Its core use cases include prototyping, education, scripting, and single-module projects. It allows users to not just run their Scala code from the command line but also compile, package and more.
Scala CLI is not meant to be a replacement for full-fledged build tools (like Mill or SBT), but is perfectly sufficient to act as one for simple projects consisting of a single module.
Beyond the JVM platform, the tool also plays nicely with Scala Native and Scala.js.
Its features are divided into sub-commands. Here is a list of some of the more important ones:
Now, that’s all nice and good, but when it comes down to the actual runner functionalities, how is Scala CLI better than its predecessor?
Simpler setup
First, the setup is a lot easier. We mean the language setup and especially the whole development environment. Normally, you would have to separately install the appropriate Java version, scalafmt (a Scala formatter used in a majority of projects) and other things, potentially including a build tool. Scala CLI does all that for the user.
Want to work with a particular Scala version other than the one available by default on your PATH? Nothing simpler. You can change it just by specifying it with an option.
1scala-cli -S 2.13
2# Welcome to Scala 2.13.10 (OpenJDK 64-Bit Server VM, Java 17.0.5).
3# Type in expressions for evaluation. Or try :help.
4#
5# scala>
1Similarly, picking a Java version doesn’t require downloading and installing it separately. The tool handles that as well.
In fact, there is no need to install Java separately at all. It’s quite okay to let Scala CLI do it. scalafmt is built in as well, so no need to worry about that.
1scala-cli fmt .
Providing a .scalafmt.conf file (configuration for scalafmt) isn’t necessary, either. If it’s not present, Scala CLI will assume default settings.
Separately installing a build tool is also optional unless you’re planning to build a multi-module project. And even then, nothing’s stopping you from using Scala CLI during the prototyping phase and then migrating once the need arises. The export sub-command makes it relatively easy to convert a Scala CLI project to Mill or SBT.
Faster compilation times
Scala CLI uses Bloop under the hood, which makes compilation times a lot faster.
It’s also one of the easier ways to interact with Bloop. So using Scala CLI’s compile sub-command may be favorable over calling scalac directly if you care about getting something compiled quickly.
Introduction of using directives
Scala CLI introduces the concept of using directives, which allows for defining configuration information in sources.
This means that things like dependencies or Scala version can be defined straight up in a Scala file without the need for a separate configuration file.
As an example, to define a simple pwd.sc script using Scala 3.2.1 and os-lib 0.9.0, you have to type:
1//> using scala "3.2.1"
2//> using lib "com.lihaoyi::os-lib:0.9.0
3println(os.pwd)
1scala-cli pwd.sc
2# Compiling project (Scala 3.2.1, JVM)
3# Compiled project (Scala 3.2.1, JVM)
4# ~/scala-cli-tests/demo
This is particularly useful for replicating and reporting bugs, as the exact configuration and code used in a given build can be put in a single code block. Defining dependencies right in the source, where they are immediately used, also speeds up prototyping.
Additionally, using directives can be used in any input accepted by Scala CLI, enabling applying it creatively. For example, nothing stops you from using them in a .java source:
However, please note that Scala CLI does not provide any sandboxing when writing this article, so make sure you trust any remote sources you decide to run.
Scala CLI has official support for IDEs through BSP (Build Server Protocol), working with IDEA IntelliJ and Metals (with Visual Studio Code or other editors supported by Metals). This allows you to use the runner from the comfort of an IDE’s graphical interface instead of having to use the command line for everything.
Some of the features delivered by Scala CLI were deemed a bit advanced for the average target user of the scala runner but are nonetheless available behind the --power launcher option.
A good example of such a power user feature is the package sub-command. It enables packaging code in various formats, like JARs, docker containers, or native images.
For example, the following command packages a project to a GraalVM native image:
As mentioned earlier, even though SIP-46 is still in its implementation phase, you can try the new runner early. You can do so by using the scala-experimental installation.
Currently, only two installation methods are supported, with more to come later.