Suche
Terra incognita

Funktionale Programmierung leicht gemacht: JVM-Sprache Eta stellt sich vor [Pirates of the JVM]

Redaktion JAXenter

Auf unserer Schatzsuche nach JVM-Juwelen stechen wir heute in den funktionalen Ozean: Dort liegt die noch weitgehend unerforschte Insel namens Eta. Die von Haskell inspirierte Programmiersprache vereint funktionale Reinheit mit einer starken Typisierung. Schauen wir uns an, wie das geht!

Funktionale Programmierung auf der JVM

Funktional auf der JVM programmieren – das geht mittlerweile mit einer Reihe von Sprachen. Clojure und Scala sind hier die wichtigsten Sprachinseln – und selbst Java ragt mit den Lambda-Ausdrücken in den funktionalen Ozean hinein.

Die funktioanlen Programmiersprachen der JVM

Klick auf die Karte für die komplette Infografik.

Heute steuern wir indes eine noch weitgehend unerforschte Insel namens Eta an. Erst 2017 von Rahul Muttineni entdeckt, schickt sich die Sprache an, die Vorzüge von Clojure und Scala miteinander zu verbinden. Ein gefundenes Fressen für JVM-Piraten!

Eta – die 5 Grundzüge der Programmiersprache

Eta ist eine funktionale Programmiersprache für die Java-Plattform, die stark von Haskell inspiriert ist. Die folgenden fünf Prinzipien machen den Charakter der Sprache aus:

  • Rein funktional (Purity)
  • Lazy
  • Starke Typisierung
  • Java-Interoperabilität
  • Nebenläufiges Programmieren

Purity

Eta ist eine rein funktionale Sprache. Das bedeutet, dass der Aufruf einer Funktion mit denselben Argumenten stets dasselbe Ergebnis zurückgibt. Funktionen können somit wie Gleichungen behandelt und substituiert werden, wie es auch in der Mathematik üblich ist. Das folgende Beispiel zeigt, wie durch funktionale Methoden-Aufrufe nebenläufiges Programmieren über das Konzept des „Software Transactional Memory“ möglich wird:

-- Purity allows safe usage of methods like Software Transactional
-- Memory, used to make concurrency safe, easy, and enjoyable.
import Control.Concurrent.STM

type Account = TVar USD
type USD = Double

transferMoney :: USD -> Account -> Account -> IO ()
transferMoney amount sender receiver =
  atomically $ transact amount sender receiver

transact :: USD -> Account -> Account -> STM ()
transact amount sender receiver = do
  senderBalance <- readTVar sender
  receiverBalance <- readTVar receiver
  writeTVar sender (senderBalance - amount)
  writeTVar receiver (receiverBalance + amount)

Laziness

Eta ist per Default „Lazy“, d.h. Daten verweilen in einem nicht-evaluierten Zustand, bis eine Funktion sie tatsächlich benötigt.

-- This program is similar to the well-known Unix utility `head`,
-- which prints the first `n` lines of its input.
main :: IO ()
main = do
  -- Extract the first command-line argument.
  ("-n":arg:_) <- getArgs

  -- Convert it to an integer.
  let n = read arg :: Int

  -- Read the contents of standard input lazily.
  contents <- getContents

  -- Process the first `n` lines in a pipeline.
  let newContents = unlines . take n . lines $ contents

  -- Print the result.
  putStr newContents

Type-Safety

Eta verfügt über ein starkes Typensystem, durch das der Compiler Zusatz-Informationen über den Quellcode erhält. Dabei beherrscht Eta Typen-Inferenz, d.h. der Compiler erkennt den Typ eines Ausdrucks, auch wenn er nicht explizit angegeben wurde. Dadurch werden weitreichende Compiler-basierte Refactorings möglich.

-- Declare a datatype for measurements in Feet and Meters.
newtype Feet = Feet Double
newtype Meters = Meters Double

-- Conversions between measurements.
feet2Meters :: Feet -> Meters
feet2Meters (Feet ft) = Meters $ ft * 0.3048

meters2Feet :: Feet -> Meters
meters2Feet (Meters m) = Feet $ m / 0.3048

-- Feet and Meters have the exact same representation
-- but the compiler will not allow Feet to passed
-- into this function.
volumeOfShippingBox :: Meters -> Meters
volumeOfShippingBox (Meters sideLength) =
  Meters $ sideLength * sideLength * sideLength

Java Interoperabilität

Eta ist als JVM-Sprache auf eine gute Interoperabilität mit Java ausgelegt. So lassen sich Java-Bibliotheken in Eta-Projekten verwenden, genauso wie Eta-Module in Java-Projekten. Darüberhinaus können Java-Funktionen typensicher importiert und viele Java Features wie Vererbung und Generics auch in Eta genutzt werden.

-- Type-safe import of a Java method that is null-safe.
foreign import java unsafe "@static java.lang.System.getenv"
  getEnv :: String -> IO (Maybe String)

{- Checks the environment for the HOME environment
variable and prints it out if it exists. -}
main :: IO ()
main = do
  home <- getEnv "HOME"
  case home of
    Just homePath ->
      putStrLn $ "Your home directory is " ++ homePath ++ "!"
    Nothing ->
      putStrLn "Your HOME environment variable is not set"

Concurrency & Parallelism

Wie bereits im ersten Code-Beispiel gezeigt, begünstigt Etas Immutability als rein funktionale Programmiersprache nebenläufiges Programmieren. Eta bietet verschiedene Strategien für Nebenläufigkeit, darunter die Par-Monade für feingranulare Parallelität und Software Transactional Memory für wiederverwendbares nebenläufiges Programmieren ohne Locks. Die Eta Runtime nutzt zudem leichtgewichtige Green Threads, die hochgradig nebenläufige Web Server unterstützen. Darüber hinaus erlaubt das Monaden-Design-Pattern, asynchrone Programme in einem sequentiellen Stil zu schreiben.
 

Was hat Eta, was andere Sprachen nicht haben?

Wir haben uns mit Eta-Erfinder Rahul Muttineni über die Hintergründe und den aktuellen Stand der Sprache unterhalten.

JAXenter: Was hat dich dazu bewogen, mit Eta eine neue Programmiersprache für die JVM zu entwerfen?

Rahul Muttineni – Erfinder von Eta

Rahul Muttineni: Schon als Kind habe ich es geliebt, neue Programmiersprachen zu erforschen. Vor fünf Jahren habe ich dann Haskell entdeckt, und es hat mich einfach umgehauen. Ich wusste, dass diese Sprache – oder eine Variante davon – die Zukunft des Programmierens sein würde. Denn ich kannte den Schmerz, der mit dem Bau von großen, sich schnell verändernden Systemen verbunden ist: Mit 14 habe ich bereits an einem Online Multiplayer Game gearbeitet und musste dort ständig neue Features einbauen.

Haskell hat mich einfach umgehauen.

Vor Kurzem habe ich als Consultant mit Clojure gearbeitet und dabei die Wichtigkeit der Java-Plattform erkannt. Allerdings musste ich feststellen, dass die gegenwärtigen JVM-Sprachen nicht die gleichen Vorzüge bieten, wie ich es von Haskell gewohnt war. So habe ich mich dran gemacht herauszufinden, wie ich Haskell auf der JVM zum Laufen bringe könnte. Zudem habe ich die Sprache in neue Richtungen weiterentwickelt, um sie für Mainstream-Programmierer angenehmer zu machen. Eta ist als Resultat dieser Anstrengungen entstanden.

JAXenter: Welche Vorteile bietet Eta, die andere Programmiersprachen nicht haben?

Rahul Muttineni: Eta ist eine rein funktionale Sprache auf der JVM, die einem vernünftige Hilfestellung bei der Software-Entwicklung bietet. Man kann besser über den Code nachdenken und weiß, was vor sich geht. Mit Eta ist es einfacher, die zentrale Geschäftslogik zu sehen, ohne im Morast der Details zu versinken. Der Compiler verhindert, dass man dumme Fehler macht – viel mehr als bei anderen statisch typisierten Sprachen.

Wir sind Menschen, und egal wie viel Expertise wir im Programmieren auch haben – an einem schlechten Tag machen wir Fehler. Und diese Fehler können teuer werden.

Die statische Typisierung in Eta macht es zudem einfacher, neue Entwickler in die Sprache einzuführen. Denn sie folgt natürlich der Mathematik, wo man auch keine Variablen hat, deren Werte sich über die Zeit verändern. Das ist ein Aspekt imperativer Sprachen, der, wie es sich gezeigt hat, Programmieren für einige Leute schwer zugänglich macht.

Lesen Sie auch: Frege – das Haskell für die JVM: Eine Einführung

Scala und Clojure, die beiden anderen wichtigen funktionalen Sprachen auf der JVM, bieten nur einen Teil der oben erwähnten Vorzüge. Clojure bietet Immutability-by-Default, was zwar dabei hilft, Code zu verstehen. Aber gleichzeitig ist Clojure dynamisch typisiert, was bedeutet, dass es keine statischen Checks gibt – also viele Fehler an schlechten Tagen.

Eta vereint die Vorzüge von Scala und Clojure in einer Sprache.

Scala andererseits hat ein ausgefeiltes Typen-System und versucht beides, funktionale und objektorientierte Paradigmen, in einer Sprache zu vereinen. Dadurch wird aber auch die Mächtigkeit des Typen-Systems beeinträchtigt. Außerdem ist Scala nicht Immutable-by-Default, weil man diesen Aspekt für jede Variablen-Deklaration  spezifizieren muss.

Eta zielt darauf ab, die Vorzüge beider Sprachen zu nutzen. Sowohl Clojure als auch Scala sind von Haskell beeinflusst und unterstützen weiterreichende Ideen davon. Wir haben also verstanden, dass wir zur Quelle gehen müssen, um die Programmiersprachen auf JVM besser zu machen: zu Haskell!

JAXenter: Für welche Art von Anwendungen eignet sich Eta besonders gut? Für welche eher weniger?

Rahul Muttineni: Eta ist GPL-Sprache (General-purpose Language), sodass man grundsätzlich alles Mögliche damit bauen kann. Allerdings machen es Etas Kernprinzipien für Anfänger einfach, hochgradig nebenläufige Systeme zu programmieren, ohne sich selbst dabei in den Fuß zu schießen.

Zudem stellt Etas Typensystem eine Art Test-Layer zur Compile-Zeit zur Verfügung, was die Anzahl an nötigen Tests für die Wartung von Anwendungen verringern kann. Der Compiler führt bei jedem Kompiliervorgang eine Menge statischer Checks durch. Das bedeutet, dass man schnell wachsenden Anwendungen problemlos neue Features hinzufügen kann, ohne die existierenden kaputt zu machen. Eine Reihe der besten Software-Entwicklungspraktiken werden bereits auf der Ebene der Programmiersprache erzwungen.

Weil Eta „Immutable-by-Default“ und „Lazy“ ist (Storing Suspended Expressions), kann allerdings auch eine große Menge an Garbage anfallen, was Druck auf den Garbage Collector ausübt. Das bedeutet, dass Eta für Anwendungen, für die die reine Performance wirklich wichtig ist, wahrscheinlich keine gute Wahl wäre.

Nichtsdestotrotz lässt sich Eta für Anwendungen, die eine geringe Latenz benötigen, tunen. Beispielsweise kann man die GC Settings entsprechend anpassen und die Regeln überschreiben, um dem Compiler eine aggressivere Optimierung des Codes abzuverlangen.

Auch interessant: Der Vergleich von imperativen und funktionalen Algorithmen in Java 8

Kurz: Für große Anwendungen, die langfristig stabil laufen müssen und zudem oft erweitert werden oder stark nebenläufig sind, ist Eta perfekt. Für Anwendungen, die eine Bare-Metal Performance brauchen, passt Eta nicht so gut.

JAXenter: Was ist der gegenwärtige Status Quo der Sprache?

Rahul Muttineni: Die Sprache ist in der Alpha-Phase, aber der Compiler und das Runtime-System selbst sind bereits recht stabil. Das Tooling und die Bibliotheken sind immer noch ein wenig unreif, aber wir wachsen schnell. Die Integration mit Java ist sehr gelungen, sodass man ziemlich einfach Bindings zu Libraries wie JavaFX, JDBC, Apache Spark, etc. erstellen kann. Darüber hinaus haben wir für ein ausreichendes Maß an Kompatibilität zu Haskell gesorgt, was es uns ermöglicht, auch eine Vielzahl von Haskell Libraries zu nutzen.

Für alle, die sich ein bisschen mehr Code ansehen möchten, seien die folgenden Links zu Eta-Beispielen auf GitHub empfohlen:

Das folgende Beispiel ist besonders interessant, weil dort ein Konzept namens „Functional Reactive Programming“ zum Einsatz kommt. Die Library, die das Konzept umsetzt, stammt von Haskell, kann von Eta aber dennoch kompiliert werden. Darüber hinaus existiert ein Binding zur JavaFX Canvas API, um die Grafiken des Spiels zu zeichnen:

Dieses Beispiel demonstriert recht gut, dass der Zugriff auf das Ökosystem von Haskell und Java problemlos möglich ist.

JAXenter: Wie sehen deine Pläne für Eta aus?

Rahul Muttineni: Die Aspekte, auf die wir uns fokussieren wollen, sind:

  • Concurrent runtime
  • Interactive REPL
  • Metaprogramming (TemplateHaskell support)
  • Core library support
  • Boilerplate generation for Java FFI imports
  • Platform-specific installers

Weitere Deatils zu den einzelnen Punkten finden sich auf der Eta-GitHub-Seite. Wir haben um die Eta-Sprache herum eine Firma namens TypeLead aufgebaut, durch die wir kommerziellen Support und Trainings für Unternehmen anbieten, die Eta benutzen möchten.

Für den Einstieg in Eta würde ich abschließend noch den Getting-Started-Guide auf www.eta-lang.org empfehlen.

JAXenter: Vielen Dank für dieses Interview!

 

Rahul Muttineni ist Mitbegründer der Unternehmens TypeLead und Erfinder der Programmiersprache Eta. Seit seiner Kindheit interessiert er sich für die Verbesserung von Programmierkonzepten und -sprachen. Rahul hat einschlägige Erfahrung mit Online-Multiplayer-Spielen, Augmented Reality, Echtzeit-Anwendungen und vielem mehr. Seine Leidenschaft ist es, die Wunder der reinen funktionalen Programmierung in die Mainstream-Entwickler-Community einzubringen.

 

Die Pirates of the JVM

Gehen wir auf Schatzsuche! In der Infografik „Pirates of the JVM“ verbirgt sich so manches Programmiersprachen-Juwel, das inspirierte Entwickler auf der Java-Plattform für sich entdecken werden. Doch sehen Sie selbst…

Haben Sie Lust auf Schatzsuche zu gehen? In unserem Gewinnspiel „Pirates of the JVM“ ist Ihr Rätseltalent gefragt. Belohnt werden Sie mit 3 tollen Preisen…

PIRATES OF THE JVM – DAS GEWINNSPIEL

Piratenflagge via Shutterstock.com / katyam1983

Verwandte Themen:

Geschrieben von
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.