Blog

Scala Native - Bare Metal Programmierung

Dennis Vriend

Aktualisiert Oktober 21, 2025
6 Minuten

In letzter Zeit scheint es einen Trend in Richtung Systems Level Programming (SLP) zu geben. SLP bietet dem Programmierer mehr Kontrolle als verwaltete Programmiersprachen wie Java, C# oder Python. Da SLP-Sprachen keine verwaltete Laufzeitumgebung benötigen, werden sie zu einer statischen Binärdatei kompiliert, die nativ auf einer Plattform ausgeführt werden kann. In diesem Blog werfen wir einen Blick auf Scala-native, einen optimierenden Ahead-of-Time-Compiler und eine leichtgewichtige verwaltete Laufzeitumgebung, die speziell für die Programmiersprache Scala entwickelt wurde.

Traditionelle SLP

Traditionell war SLP die Domäne von Sprachen wie C und C++, die direkt mit dem Betriebssystem oder manchmal sogar direkt mit der Hardware kommunizieren. Diese Anwendungen mussten alle Aspekte der Ausführung einer Anwendung verwalten, wie z.B. Speicherverwaltung, Socket- und Dateizuweisungen und Multi-Threading. Da es für diese Sprachen kaum Kontrollen durch einen Compiler oder eine verwaltete Laufzeit gibt, handelt es sich bei den in diesen Sprachen geschriebenen Programmen meist um Betriebssysteme, Kernel-Treiber, Hochleistungs-/Echtzeitanwendungen oder Anwendungen, die auf Hardware wie medizinische Geräte spezialisiert sind.

Fehlende moderne Funktionen

Das Problem mit C oder C++ ist, dass diese Sprachen keine Dienste anbieten, die moderne Programmiersprachen bieten, wie z.B. Typüberprüfung, Typinferenz, Abstraktionen auf höherer Ebene, unveränderliche Datenstrukturen, Primitive für Nebenläufigkeit, Erkennung von Wettläufen und im Guten wie im Schlechten, ein gutes Build-Tool. In C oder C++ geschriebene Programme verwenden meistens Makefile(s), um eine Binärdatei zu erstellen, und ohne hier in die Tiefe zu gehen, sind diese Build-Definitionen notorisch ineffizient.
Selbst auf moderner Hardware kann ein Build sehr viel Zeit in Anspruch nehmen. Erschwerend kommt hinzu, dass C- und C++-Programme aufgrund fehlender Sicherheitsfunktionen notorisch schwierig zu schreiben, zu warten und zu testen sind. Weil Menschen Fehler machen und weil C- und C++-Software fast die gesamte Hardware antreibt, bedeutet dies, dass potenziell ein großer Teil der Hardware in der realen Welt unsicher ist!
Glücklicherweise sind neue SLP auf dem Vormarsch, die Sicherheitsgarantien bieten. Gleichzeitig sind diese Sprachen nicht nur für Betriebssysteme, Kernel-Treiber oder eingebettete Systeme nützlich. Vielmehr können diese Sprachen auch für die Entwicklung von Geschäftsdiensten, serverseitigen Anwendungen und Webservern verwendet werden. Das ist gut!

Moderne SLP

Moderne SLP bieten Funktionen, die meine modernen Programmiersprachen bieten und bei denen sich alles um Leistung, Sicherheit und Wartbarkeit dreht. Moderne SLP-Sprachen wie Rust, Scala-Native, Go, um nur einige zu nennen, bieten Kontrolle auf niedriger Ebene und gleichzeitig Sicherheit durch eine höhere Abstraktionsebene und Typüberprüfungen. Bei Bedarf bieten diese Sprachen auch unsichere Kontrolle, aber nur, wenn Sie dies wünschen. In der Tat stellen moderne SLP das Modell auf den Kopf. Hohe Abstraktion und Sicherheit als Standard und unsichere vollständige Kontrolle auf niedriger Ebene, wenn es nötig ist.

Scala nativ

Scala-Native ist eine moderne SLP, die noch experimentell ist. Es wurde erstmals im März 2017 in v0.10 veröffentlicht und zum Zeitpunkt der Erstellung dieses Artikels ist die neueste Version v0.3.8 Juli 2018. Scala nativ ist, nun ja, Scala. Wenn Sie die Sprache kennen, können Sie sofort loslegen. Lassen Sie Scala Native installieren!

Einrichtung

Wir müssen das Scala Build Tool (SBT) und Java 8 installiert haben. Wir brauchen Java, um SBT zu betreiben, das unseren nativen Code erstellen wird. Lassen Sie uns alle notwendigen Dinge installieren.

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

HalloWelt

Das Beispiel-Repository enthält den gesamten Code, der folgen wird. Wir müssen mit Hello World beginnen:

package com.github.binxio

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

Lassen Sie die native Binärdatei laufen:

# 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

Wie wir sehen können, ist Hello World eine statische Binärdatei von 2MB, die auf einem Mac ausgeführt werden kann.

Scala Native Bibliotheken

Scala Native arbeitet nicht mit Java- oder Scala-Bibliotheken. Scala Native bietet seine eigene native Implementierung des JDK. Für Bibliotheken auf höherer Ebene wie Test-Frameworks, generische Programmierung, CLI-Anwendungsbibliotheken, JSON-Serialisierer muss die Scala Native-Gemeinschaft einen Beitrag leisten. Bekannte Scala- und Java-Bibliotheken, Frameworks und Toolkits müssen portiert werden. Glücklicherweise beweisen einige Projekte, dass sie Scala Native unterstützen. Die unten aufgeführten Bibliotheken sind die, die ich im Beispiel verwendet habe.

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"

Sammlungen

Scala native ist ein Scala, so dass alle Scala-Sammlungen verfügbar sind.

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")
  }
}

Ausgänge:

$ ./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)

Zum Verstehen

Scala unterstützt die 'for-comprehension'. Die for-Komprehension macht Scala einzigartig und wird unterstützt:

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")
  }
}

Ausgänge:

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

Funktionen

Scala nativ unterstützt Funktionen:

package com.github.binxio

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

Optionale Werte

Scala nativ unterstützt optionale Werte:

package com.github.binxio

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

Fazit

Scala Native ist noch experimentell, aber sehr brauchbar. Obwohl die asynchrone Unterstützung noch nicht verfügbar ist (z.B. Scala Futures), ist Scala Native für Konsolenanwendungen und sequenzielle Automatisierung sehr gut geeignet. Bibliotheksunterstützung für Projekte wie Akka, Playframework und Lagom ist nicht verfügbar und wird vorerst auf der JVM ausgeführt. Ich denke, dass Scala Native, wenn es ausgereifter ist, eine brauchbare Alternative für die moderne Programmierung auf Systemebene sein wird!

Verfasst von

Dennis Vriend

Contact

Let’s discuss how we can support your journey.