Scala Environment

This notebook builds a reusable environment for Scala Jupyter runtimes, based on the Minimal Python 3 environment. Nextjournal's Scala environment uses the Almond Jupyter kernel, and has Scala versions 2.13, 2.12, and 2.11 installed, running on OpenJDK 8.

Learn more about environments on Nextjournal.

You can use this environment by remixing the Nextjournal Scala template. To change the Scala version (see image below):

  • Scroll to the bottom of the notebook.
  • Expand the Runtime Languages section.
  • Access the ··· menu, then Configure...
  • In the expanded panel, re-select Scala in the top menu.
  • Once loaded, select the Scala version you want to use in the lower menu.
  • Click the Save & Add button to insert a cell with the new Scala runtime.

1. Showcase

1.1. Importing Scala Artifacts

Scala dependencies and compiler plugins can be loaded with either of two syntaxes supported by the Ammonite API:

  • interp.load.ivy() and interp.load.plugin.ivy()
  • import $ivy and import $plugin.$ivy

The former allows for more verbosity and adjusting parameters...

interp.load.ivy(
  coursier.Dependency(
    module = coursier.Module("org.platanios", "tensorflow_2.12"),
    version = "0.4.1",
    // replace with linux-gpu-x86_64 on linux with nvidia gpu or with darwin-cpu-x86_64 on macOS
    attributes = coursier.Attributes("", "linux-cpu-x86_64")
  )
)

...while the latter syntax allows use of the imported artifact immediately after, within the same code cell:

import $plugin.$ivy.`org.spire-math::kind-projector:0.9.9`

// example of use
trait T[F[_]]
type T2 = T[Either[String, ?]]

1.2. Plotting

Two easy supported graphical libraries are Plotly and Vegas. Plotly currently requires Scala 2.12 or 2.11, while Vegas requires 2.11.

1.2.1. Plotly

The Scala Plotly library supports a subset of the full plotly.js API, including most common 2D plot types as well as multi-axis and subplot capabilities.

import $ivy.`org.plotly-scala::plotly-almond:0.7.0`
import plotly._, plotly.element._, plotly.layout._, plotly.Almond._

def rlist(n:Int) : Sequence = { 
  return for (i <- 1 to n) yield scala.util.Random.nextInt(42) 
}

var plen = 7
val trace1 = Scatter( 1 to plen, rlist(plen) )
val trace2 = Scatter( 1 to plen, rlist(plen), 
  xaxis = AxisReference.X2, yaxis = AxisReference.Y2)

Seq(trace1, trace2).plot(title = "Mulitple Custom Sized Subplots",
    xaxis  = Axis(anchor = AxisAnchor.Reference(AxisReference.Y1),
      domain = (0, 0.45)),
    yaxis  = Axis(anchor = AxisAnchor.Reference(AxisReference.X1)),
    xaxis2 = Axis(anchor = AxisAnchor.Reference(AxisReference.Y2),
      domain = (0.55, 1)),
    yaxis2 = Axis(anchor = AxisAnchor.Reference(AxisReference.X2)))

1.2.2. Vegas

The Vegas library is a wrapper around Vega-Lite.

interp.load.ivy("org.vegas-viz" %% "vegas" % "0.3.11")

Vegas enables echoing to the console, so we'll override that with a withOut wrapper. For rendering, we'll write the Vega-Lite JSON to a file in /results, to be parsed by Nextjournal's built-in handler.

// disable console echo
scala.Console.withOut(new PrintStream(new FileOutputStream("/dev/null"))) {
import vegas._
import vegas.data.External._
import java.io._

var plot = Vegas("A simple bar chart with embedded data.",
                 width=600,height=300).
  withData(Seq(
    Map("a" -> "A", "b" -> 28), Map("a" -> "B", "b" -> 55), 
    Map("a" -> "C", "b" -> 43), Map("a" -> "D", "b" -> 91), 
    Map("a" -> "E", "b" -> 81), Map("a" -> "F", "b" -> 53),
    Map("a" -> "G", "b" -> 19), Map("a" -> "H", "b" -> 87), 
    Map("a" -> "I", "b" -> 52)
  )).
  encodeX("a", Ordinal).
  encodeY("b", Quantitative).
  mark(Bar)

// Write Vega-Lite JSON to file
val pw = new PrintWriter(new File("/results/out.vl.json"))
pw.write(plot.toJson)
pw.close
}
Loading viewer…

2. Setup

2.1. Java & Scala

apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends \
  openjdk-8-jdk-headless
java -version
mkdir -p /tmp/scalas
cd /tmp/scalas
for scala in $SCALA_VERSION $SCALA_EX_VERSIONS; do
  FILENAME="scala-${scala}.tgz"
  FILEURL="https://downloads.lightbend.com/scala/${scala}/$FILENAME"

  wget --progress=dot:mega $FILEURL
done

tar -cf /results/scalas.tar scala-*.tgz
rm -r /tmp/scalas
scalas.tar
mkdir -p /opt/scalas
cd /opt/scalas
tar -xf 
scalas.tar
for scala in scala-*.tgz; do tar -zxf $scala done ln -sf scala-${SCALA_VERSION} default rm scala-*.tgz
ls -l /opt/scalas

Install Coursier.

curl -Lo /opt/scalas/default/bin/coursier https://git.io/coursier-cli
chmod +x /opt/scalas/default/bin/coursier
mkdir -p /opt/scalas/coursier
scala -version

2.2. Almond

Install Jupyter.

pip install --upgrade jupyter jupyter_client

Install the Almond Jupyter kernel for all Scala versions.

cd /tmp
almond_inst () {
  coursier bootstrap -r jitpack -i user -o almond \
    -I user:sh.almond:scala-kernel-api_$1:$2 \
    sh.almond:scala-kernel_$1:$2
  ./almond --install --id $3 --display-name "Scala $1"
  rm almond
}
almond_inst 2.13.0  0.7.0 scala213
almond_inst 2.12.8  0.7.0 scala212
almond_inst 2.11.12 0.6.0 scala211

Check.

du -hsx /
jupyter kernelspec list

2.3. Test

case class Hello(i: Int, s0: String)
defined class Hello
Hello(3, "Hello Scala!")
res1: Hello = Hello(3, "Hello Scala!")