Blog

Scala Native – Bare Metal Programming

27 Nov, 2018
Xebia Background Header Wave

As of late, there seems to be a trend towards Systems Level Programming (SLP). SLP provides more control to the programmer than managed programming languages like Java, C# or Python provide. Because SLP languages do not need a managed runtime, they compile to a static binary that can be run natively on a platform. In this blog we’ll look at Scala-native, an optimizing ahead-of-time compiler and lightweight managed runtime designed specifically for the Scala programming language.

Traditional SLP

Traditionally, SLP was the realm of languages like C and C++ that communicate directly with the operating system or sometimes even with directly with the hardware. These applications had to manage all aspects of running an application like like memory management, socket and file allocations and multi threading. Because there is hardly any checks from either a compiler or managed runtime for these languages, programs written in these languages are most often operating systems, kernel drivers, high performance/real-time applications or applications specialized for hardware like medical equipment.

Lacking Modern Features

The problem with C or C++ is that these languages do not provide services that modern programming languages do provide like type checking, type inference, higher level abstractions, immutable data structures, concurrency primitives, race detections and for better or for worse, a good build tool. Programs written in C or C++ most often than not use Makefile(s) to build a binary and without going into depth here, these build definitions are notoriously inefficient.
Even on modern hardware, a build can take a lot of time. To make things worse, by not providing safety features, C and C++ programs are notoriously difficult to write, maintain and test. Because people make mistakes and because C and C++ software powers almost all hardware, means that potentially a lot of hardware in the real world is unsafe!
Fortunately, there are new SLP on the rise that do provide safety guarantees. At the same time these languages are not only useful for operating systems, kernel drivers or embedded systems. In fact, these languages can be used to develop business services, server side applications and web servers. Good things!

Modern SLP

Modern SLP provide features that are provided my modern programming languages and are all about performance, safety and maintainability. Modern SLP languages like Rust, Scala-Native, Go to name a few, provide low level control and at the same time, safety by means of a higher level of abstraction, and type checks. When necessary these languages also provide unsafe control, but only when you choose so. In fact, modern SLP turn the model upside down. High abstractions and safety by default, and low level unsafe full control, when it is necessary.

Scala Native

Scala-Native is a modern SLP that is still experimental. It is first released in v0.10 March of 2017, and at the time of writing the latest release is v0.3.8 July 2018. Scala native is, well, Scala. If you know the language you immediately can get going. Let install Scala Native!

Setup

We need to have the Scala Build Tool (SBT) installed and Java 8. We need Java to power SBT that will build our native code. Lets install all the necessary things.

$ brew install sbt llvm bdw-gc re2
$ brew cask install java8 

HelloWorld

The example repository contain all the code that will follow. We need to start with Hello World:

package com.github.binxio

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello World " + args.mkString(","))
  }
}

Lets run the native binary:

# compile and link
$ sbt nativeLink

# run the binary
$ ./target/scala-2.11/blog-scala-native-out a b c
Hello World a,b,c

# determine file type
$ file ./target/scala-2.11/blog-scala-native-out
./target/scala-2.11/blog-scala-native-out: Mach-O 64-bit executable x86_64

# determine file size
$ ls -alh ./target/scala-2.11/blog-scala-native-out
-rwxr-xr-x  1 dennis  staff   2.0M Nov 28 14:30 ./target/scala-2.11/blog-scala-native-out

As we can see, Hello World is a static binary of 2MB that can be run on a Mac.

Scala Native Libraries

Scala Native does not work with Java or Scala libraries. Scala Native provides its own native implementation of the JDK. For higher level libraries like testing frameworks, generic programming, CLI application libraries, JSON serializers the Scala Native community needs to contribute. Well known Scala and Java ibraries, frameworks and toolkits need to be ported. Fortunately some projects are proving support for Scala Native. The libraries below are the ones I used in the example.

libraryDependencies += "org.scalaz" %%% "scalaz-core" % "7.2.27"
libraryDependencies += "com.softwaremill.sttp" %%% "core" % "1.5.0"
libraryDependencies += "org.rogach" %%% "scallop" % "3.1.5"
libraryDependencies += "com.chuusai" %%% "shapeless" % "2.3.3"
libraryDependencies += "org.scala-native" %%% "test-interface" % "0.3.8"
libraryDependencies += "io.crashbox" %%% "spray-json" % "1.3.4-1"
libraryDependencies += "xyz.driver" %% "spray-json-derivation" % "0.7.0"
libraryDependencies += "org.scalactic" %%% "scalactic" % "3.2.0-SNAP10"
libraryDependencies += "org.scalatest" %%% "scalatest" % "3.2.0-SNAP10" % "test"

Collections

Scala native is a Scala, so all the Scala collections are available.

package com.github.binxio

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello World " + args.mkString(","))

    val xs = List(1, 2, 3, 4)
    val ys = Map("a" -> 1, "b" -> 2, "c" -> 3)
    val zs = Set(1, 1, 2, 2, 3, 3)
    println(s"xs=$xs, ys=$ys, zs=$zs")
  }
}

Outputs:

$ ./target/scala-2.11/blog-scala-native-out a b c
Hello World a,b,c
xs=List(1, 2, 3, 4), ys=Map(a -> 1, b -> 2, c -> 3), zs=Set(1, 2, 3)

For Comprehension

Scala supports the ‘for-comprehension’. The for-comprehension makes Scala unique and is supported:

package com.github.binxio

import scala.util.Try

object HelloWorld {
  def main(args: Array[String]): Unit = {
    val xs = for {
      x <- 1 to 2
      y <- 2 to 3
    } yield x + y

    val z = for {
      a <- Try(1)
      b <- Try(2)
    } yield a + b

    println(s"xs=$xs, z=$z")
  }
}

Outputs:

$ ./target/scala-2.11/blog-scala-native-out
xs=Vector(3, 4, 4, 5), z=Success(3)

Functions

Scala native supports functions:

package com.github.binxio

object HelloWorld {
  def main(args: Array[String]): Unit = {
    val addOne = (_: Int) + 1
    print(addOne(1))
  }
}

Optional Values

Scala native supports optional values:

package com.github.binxio

object HelloWorld {
  def main(args: Array[String]): Unit = {
    val x: Option[Int] = Option.empty[Int]
    println(x.fold("empty")(_.toString))
  }
}

Conclusion

Scala Native is still experimental but is very usable. Although asynchronous support is still not available. ie. Scala Futures, Scala native is very usable for console applications and sequential automation. Library support for projects like Akka, Playframework and Lagom is not available and will be run on the JVM for the moment. I think Scala native, when it is more mature will be a viable alternative for modern systems level programming!

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts