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
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 namedscala
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 [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/"))
- Typically
- 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 asbt
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.