Scala – Getting Started

Naively following instructions to create a Scala application works reasonably well. But, to understand the underlying interactions of the various components is not that simple. It can be especially frustrating for those from a conventional programming background, because a number of things happen automagically, and the documentation for it is sparse.

These are my notes as I went through the exercies of setting up a Scala development environment. Hope it makes it easier for someone trying to get started.

Introdcution
Linux and Windows
JDK
Example
Source
Scala Framework
Setup | Build | Run
sbt
Setup | Build | Run
Scala IDE
IntelliJ
  Setup | Build | Run
Eclipse
  Setup | Build | Run
Conclusion

Introduction

Scala is a JVM language, which means, like Java, it is compiled into bytecode and runs on a JVM. This implies that you need JRE to execute and JDK to develop. Though Scala is presented as an independent language, it would be more logical to think of Scala as a language framework on the Java Platform, which provides supporting libraries, a build tool and plugins for existing Java IDEs. The structure of the program, compilation and execution is similar to Java programming 1.

Some of the text below quoted from their official webpage gives clues to the design of Scala – is more of a language library built on the Java platform.

Compared to other programming languages, installing Scala is a bit unusual. Scala is unusual because it is usually installed for each of your Scala projects rather than being installed system-wide. sbt manages a specific Scala version per Scala project you create.

One of Scala’s strengths is that it makes it very easy to interact with Java code. All classes from the java.lang package are imported by default, while others need to be imported explicitly.

Scala runs on the JVM, so Java and Scala stacks can be freely mixed for totally seamless integration. 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.

Linux and Windows

Whenever possible, an archive file (tarball or zip) is used to keep the installations local and easy to cleanup. All the steps are similar to Linux and Windows with their expected differences – archive format, archive extraction tool, path seperator and setting environment variables. Significant differences are noted explicitly. All paths are relatie to the base directory scala-example.

JDK

There are different distributions available to install Java, OpenJDK, AdoptOpenJDK and OracleJDK, which are all technically equivalent. However, the Scala download page directs you to install either AdoptOpenJDK or OracleJDK.

Most Linux distributions have OpenJDK by default in their standard repositories. Though it is equivalent, Scala commands gives an error.
$ scala
cat: /release: No such file or directory
Welcome to Scala 2.12.11 (OpenJDK 64-Bit Server VM, Java 1.8.0_242).

This is because, it is looking for a file $JAVA_HOME/release which is not in the OpenJDK install but exists in the other distributions. Though it is not clear how critical this is, it is safer to use AdoptOpenJDK or OracleJDK.

Download AdoptOpenJDK or OracleJDK.

Extract or install as appropriate.
Set the JAVA_HOME environment variable and append it to the path.

Linux
~/scala-example$ tar -xzf ~/Downloads/jdk-8u241-linux-x64.tar.gz -C ./externals/java

export JAVA_HOME=~/scala-example/externals/java/jdk1.8.0_241
export PATH=$PATH:$JAVA_HOME/bin
Windows Install Java.

set JAVA_HOME="C:\Program Files\Java\jdk1.8.0_241"
set PATH=%PATH%;%JAVA_HOME%\bin

Example

The directory layout gives you a summary of all the components involved.

  • Scala applications prefer the Maven structure, with a directory named scala for Scala sources.
  • Scala sources should follow the Java convention for packages where each package should appear in its own directory with the same name.

The example code can be accessed on GitHub at https://github.com/cognitivewaves/scala-examples.

scala-example
+-- apps
|   +-- scalaApp
|       +-- build.sbt
|       +-- project
|       |   +-- build.properties
|       |   +-- plugins.sbt
|       +-- src
|           +-- main
|           |   +-- java
|           |   +-- resources
|           |   +-- scala
|           |       +-- cw
|           |           +-- HelloWorld.scala
|           +-- test
|               +-- java
|               +-- resources
|               +-- scala
+-- externals
|   +-- java
|   |   +-- jdk1.8.0_241
|   +-- sbt
|   |   +-- sbt-1.3.9
|   +-- scala
|       +-- scala-2.12.11
+-- env-vars.bat
+-- env-vars.sh

Source

The source code itself is quite trivial as shown below.

package cw

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, world!")
  }
}

Scala Framework

The Scala download page is a bit confusing as it directs you to download IntelliJ or SBT, with no mention of Scala itself. Scroll down to the bottom to see the links to actual scala libraries. Clicking on a select version takes you to version specific download page (e.g. scala-2.12.11), but again is confusing because it shows the same IntelliJ and SBT download. Scroll down to the bottom to find the Scala library archives.

Setup

Download scala-2.12.11 or any other version. Extract the downloaded tar/zip file.
Set the SCALA_HOME environment variable and append it to the path.

Linux
~/scala-example$ tar -xzf ~/Downloads/scala-2.12.11.tgz -C ./externals/scala

export SCALA_HOME=~/scala-example/externals/scala/scala-2.12.11
export PATH=$PATH:$SCALA_HOME/bin

When you invoke scalac or scala, if you get the following error, try setting the environment variable TERM=xterm.

[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
        :
        :
	at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
	at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Windows
set SCALA_HOME=d:\scala-example\externals\scala\scala-2.12.11
set PATH=%PATH%;%SCALA_HOME%\bin

Check the Scala compiler.

~/scala-example$ scalac -version
Scala compiler version 2.12.11 -- Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.

Check the Scala interpreter or REPL. Use :q to exit the interactive mode.

~/scala-example$ scala
Welcome to Scala 2.12.11 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_241).
Type in expressions for evaluation. Or try :help.

scala>

Build

Compile the code with scalac. To keep the generated build outside of the sources, create a directory target and direct the compilation output to it.

~$ cd scala-example/apps/scalaApp
~/scala-example/apps/scalaApp$ mkdir target
~/scala-example/apps/scalaApp$ scalac -d target src/main/scala/cw/HelloWorld.scala

It outputs a .class file under the target directory.

target
+-- cw
    +-- HelloWorld.class
    +-- HelloWorld$.class

Run

Run the code with scala.

~/dev/apps/scalaApp$ scala -classpath target cw.HelloWorld
Hello, world!

SBT

Invoking scalac to compile and scala to run works for simple applications with a few source files, which is typically not the case. For building larger applications in Scala, sbt is the preferred build tool.

C/C++ programs can use make to build with build instructions in Makefile(s). Java programs can use maven to build and manage dependencies with the instructions in pom.xml. Similarly, Scala programs use sbt 2 to build and manage dependencies with instructions in a build.sbt file. Until sbt 1.3.0 Apache Ivy is used to manage dependencies, after which Coursier implements the dependency manager.

  • It has two modes of operation, batch and interactive. Batch mode operates like a typical build tool for the most part. Interactive mode is out of scope for discussion here.
  • There are some nuanced behavior in specifying the compiler, which can throw off conventional developers.
    • Typically build.sbt only specifies the compiler version, and not point to any specific installation. sbt will download the appropriate compiler and dependent libraries.
    • If the Scala version is not specified, the version sbt was built against is used, which is a weird behavior. It is highly recommended to explicitly specify the version of Scala.
    • However, there is a provision to specify a local install by defining the scalaHome setting with the path to the local Scala home directory. sbt still requires scalaVersion to be set when a local Scala version is used. For example,
      scalaVersion := "2.12.11-local"
      scalaHome := Some(file("/path/to/scala/home/"))
  • By default, sbt also adds a dependency on the Scala library of the specified version, but can be disabled.
  • For projects, sbt uses the same directory structure as Maven for source files by default (all paths are relative to the base directory).

Setup

Download sbt.

Linux
~/scala-example$ tar -xzf ~/Downloads/sbt-1.3.9.tgz -C ./externals/sbt
~/scala-example$ mv ./externals/sbt/sbt ./externals/sbt/sbt-1.3.9

$ export SBT_HOME=~/scala-example/externals/sbt/sbt-1.3.9
$ export PATH=$PATH:$SBT_HOME/bin
Windows
set SBT_HOME=d:\scala-example\externals\sbt\sbt-1.3.9
set PATH=%PATH%;%SBT_HOME%\bin

The first time you run sbt, it can take some time as it downloads a lot of dependencies. For newbies, it can look intimidating, but just ignore it as long as you don’t see any errors. The downloaded dependencies are cached in various directories (.cache/coursier, .ivy2, .sbt) in the home directory. Use the exit command to leave the interactive mode.

~/scala-example/apps/scalaApp$ sbt
[info] [launcher] getting org.scala-sbt sbt 1.3.9  (this may take some time)...
:
:
[info] [launcher] getting Scala 2.12.10 (for sbt)...
:
:
[info] Fetched artifacts of 
[info] Loading settings for project scalaapp from build.sbt ...
[info] Set current project to scalaapp (in build file:/home/cw/scala-example/apps/scalaApp)
[info] sbt server started at local:///home/cw/.sbt/1.0/server/4c914015ec698f422b04/sock
sbt:scalaapp> exit
[info] shutting down sbt server

Build

There are two main build files for sbt, build.sbt and project/build.properties.sbt

scalaVersion := "2.12.11"

sbt.version = 1.3.9

Compile the example using sbt. It downloads some more dependencies only for the first compilation. Subsequent compilations are much quicker.

~/scala-example/apps/scalaApp$ sbt compile
[info] Loading settings for project scalaapp-build from plugins.sbt ...
[info] Loading project definition from /home/cw/scala-example/apps/scalaApp/project
[info] Loading settings for project scalaapp from build.sbt ...
[info] Set current project to scalaapp (in build file:/home/cw/scala-example/apps/scalaApp/)
[info] Executing in batch mode. For better performance use sbt's shell
[info] Updating 
:
:
https://repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.11/scala-library-2.12.11.jar
  100.0% [##########] 5.5 MiB (987.3 KiB / s)
https://repo1.maven.org/maven2/org/scala-lang/scala-reflect/2.12.11/scala-reflect-2.12.11.jar
  100.0% [##########] 4.4 MiB (514.4 KiB / s)
https://repo1.maven.org/maven2/org/scala-lang/scala-compiler/2.12.11/scala-compiler-2.12.11.jar
  100.0% [##########] 14.9 MiB (814.4 KiB / s)
[info] Fetched artifacts of 
:
:
[info] Compiling 1 Scala source to /home/cw/dev/apps/scalaApp/target/scala-2.11/classes ...
[success] Total time: 4 s, completed Apr 17, 2020 12:25:25 PM

It generates the .class files.

target/scala-2.12/
+-- classes
    +-- cw
        +-- HelloWorld.class
        +-- HelloWorld$.class

Alternatively, the compiled bytecode can be packaged into a jar.

~/scala-example/apps/scalaApp$ sbt package
[info] Loading settings for project scalaapp-build from plugins.sbt ...
[info] Loading project definition from /home/cw/scala-example/apps/scalaApp/project
[info] Loading settings for project scalaapp from build.sbt ...
[info] Set current project to scalaapp (in build file:/home/cw/scala-example/apps/scalaApp/)
[info] Compiling 1 Scala source to /home/cw/dev/apps/scalaApp/target/scala-2.11/classes ...
[success] Total time: 4 s, completed Apr 17, 2020 12:32:16 PM

It generates the .class files and packages them into a .jar.

target/scala-2.11/
+-- classes
|   +-- cw
|       +-- HelloWorld.class
|       +-- HelloWorld$.class
+-- scalaapp_2.11-0.1.0-SNAPSHOT.jar

Run

Running via sbt will execute the code.

~/scala-example/apps/scalaApp$ sbt run
[info] Loading settings for project scalaapp-build from plugins.sbt ...
[info] Loading project definition from /home/cw/scala-example/apps/scalaApp/project
[info] Loading settings for project scalaapp from build.sbt ...
[info] Set current project to scalaapp (in build file:/home/cw/scala-example/apps/scalaApp/)
[info] running scalaapp 
Hello, world!
[success] Total time: 1 s, completed Apr 3, 2020 12:16:03 PM

Alternatively, the packaged .jar file can be run directly by the scala interpreter.

~/scala-example/apps/scalaApp$ scala target/scala-2.12/scalaapp_2.12-0.1.0-SNAPSHOT.jar

Hello, world!

Scala IDE

A Scala IDE really means a plugin for an existing IDE (IntelliJ or Eclipse) which lets you edit Scala code. With syntax highlighting, code completion, debugging and other features, it is a value added development environment. Though it is possible to create, build and run Scala projects from scratch, it is typically used in the context of sbt. The command line sbt is for build automation, whereas Scala IDE is for development and debugging.

  • The official Scala download page recommends IntelliJ. It has a seamless integration with sbt.
  • You will find references online suggesting Eclipse, but the repository does not seem to be actively maintained. Also, Eclipse works indirectly with sbt, and requires a sbt plugin to generate its project files.

IntelliJ

IntelliJ is the preferred IDE for Scala development. It seamlessly integrates with sbt to import project setttings, compile Scala code and run Scala applications. See the documentation Discover Intellij IDEA for Scala for details.

Setup

Download the Linux tar or Windows zip and extract it to an appropriate location.

Launch it.

Linux ./intellij/idea-IC-193.6911.18/bin/idea.sh
Windows ideaIC-2020.1\bin\idea.exe

Build

Import the sbt project.

Import Project -> Select directory ~/scala-example/apps/scalaApp -> Import project from external model -> sbt -> <defaults> -> Finish.

It will download the necessary sbt version. In the build output window, notice that sbt is invoked.

You may see a message sbt projects need to be imported with the options Import Changes and Enable Auto-Import. Pick either depending on your preference. This is where the seamless integration with sbt works its magic. You don’t have to deal with any of IntelliJ’s native project settings.
Build -> Build Project

Run

A run configuration may get automatically created. If not, set it up.

Run -> Edit Configurations -> + button (top left hand corner) -> Application
Name: ScalaApp
Main class: cw.HelloWorld
Working directory: /home/cw/scala-example/apps/scalaApp
Use classpath of module: scalaapp

Run the app.
Run -> Run ScalaApp or toolbar button.

Or run in debug.
Run -> Debug ScalaApp or toolbar button.

Eclipse

www.scala-ide.org appears like an independent IDE for Scala, which is misleading. It’s really just a Scala plugin for Eclipse. The plugin provides a Scala specifc Eclipse Perspective which is set as the default.

sbt has its own plugin sbt-eclipse which generates Eclipse project files from the sbt build configuration. These generated project files are then imported into Eclipse. So the integration with sbt is not as tight as in IntelliJ.

Setup

Download pre-configured version of Eclipse.
Or install the plugin for an existing Eclipse.

There is no installation as such for sbt-eclipse because sbt downloads it during its initialization. It is specified in /project/plugins.sbt.

 addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4") 
~/scala-example/apps/scalaApp$ sbt eclipse

[info] Loading settings for project scalaapp-build from plugins.sbt ...
[info] Loading project definition from /home/cw/scala-example/apps/scalaApp/project
[info] Updating 
:
:
[info] Fetched artifacts of 
[info] Loading settings for project scalaapp from build.sbt ...
[info] Set current project to scalaapp (in build file:/home/cw/scala-example/apps/scalaApp/)
[info] About to create Eclipse project files for your project(s).
[info] Fetching artifacts of 
:
:
[info] Fetched artifacts of 
[info] Successfully created Eclipse project files for project(s):
[info] scalaapp

Build

Import the project directory.

File->Import
General->Existing Projects into Workspace
Select root directory as /home/cw/scala-example/apps/scalaApp
Finish

Check the diagnostics.

Scala->Setup Diagnostics
    Use recommended default settings

Run

Setup a run configuration and default launcher.

Run -> Run Configurations -> Scala Application -> New launch configuration (tiny button on the left hand top corner)
Name: Scala App (or something)
Project: ScalaApp
Main class: cw.HelloWorld

Window -> Preferences -> Run/Debug -> Default Launchers -> Scala Application -> [Debug] Scala Application Launcher
Set default launcher

Run the app
Run -> Run Configurations -> Scala Application -> Scala App -> Run or toolbar button

Debug the app
Run -> Debug Configurations -> Scala Application -> Scala App -> Run or toolbar button.

Conclusion

It almost seems like, in an attempt to simplify, they have actually complicated it – at least for beginners. Hope the document was helpful. Feel free to comment if you need any clarifications or further information.