Elm – Funktionale Webentwicklung ohne Laufzeitfehler
Elm ist eine rein funktionale Sprache, die zu JavaScript kompiliert und speziell für zuverlässige Web-Oberflächen entwickelt wurde. Ihr Markenzeichen: Der Compiler fängt fast alle Fehler bereits zur Übersetzungszeit ab – zur Laufzeit treten praktisch keine Ausnahmen mehr auf. Elm eignet sich hervorragend, um JavaScript-Frontends durch eine wartbare, typsichere Alternative zu ersetzen.
Die wichtigsten Elm-Befehle und Werkzeuge
elm init– legt ein neues Projekt mitelm.jsonundsrc/an.elm reactor– startet einen lokalen Entwicklungsserver mit Live-Neuladen (Standard:http://localhost:8000).elm make src/Main.elm --output=main.js– kompiliert ein Modul zu JavaScript; mit--optimizefür den Produktions-Build.elm repl– interaktive Konsole zum Experimentieren mit Ausdrücken und Typen.elm install elm/http– installiert ein Paket aus dem offiziellen Elm-Paketkatalog.elm bump– berechnet die nächste Versionsnummer nach semantischer Versionierung.elm review– statische Analyse mit Regeln (ähnlich einem Linter).elm-test– führt Unit- und Fuzz-Tests aus (eigene CLI).
Grundkonzepte
- Model-Update-View: Jede Elm-App besteht aus einem Zustandsmodell, einer
update- und einerview-Funktion – der Elm Architecture folgend. - Unveränderliche Daten: Werte können nicht verändert, nur neu erzeugt werden. Das macht Nebenläufigkeit und Debugging einfacher.
- Typsystem: Jeder Wert hat einen festen Typ;
MaybeundResultersetzennullund Exceptions.
Beispiel: Counter
module Main exposing (main)
import Browser
import Html exposing (button, div, text)
import Html.Events exposing (onClick)
type Msg = Inkrement | Dekrement
update msg model =
case msg of
Inkrement -> model + 1
Dekrement -> model - 1
view model =
div []
[ button [ onClick Dekrement ] [ text "-" ]
, text (String.fromInt model)
, button [ onClick Inkrement ] [ text "+" ]
]
main = Browser.sandbox { init = 0, update = update, view = view }
Wann Elm sinnvoll ist
Elm glänzt bei mittelgroßen, langfristig gepflegten Frontends: Formularen, Dashboards und interaktiven Visualisierungen. Für sehr große Teams ist das Ökosystem kleiner als das von TypeScript, dafür bietet Elm durch sein Modell mehr Sicherheit.
Weiterführend: Haskell – die Mutter der rein funktionalen Sprachen und der Glossar-Eintrag Kompilieren.
F# – Funktionale und objektorientierte Programmierung auf .NET
F# ist eine Multi-Paradigmen-Sprache auf der .NET-Plattform: Sie kombiniert funktionale Programmierung (unveränderliche Daten, Funktionen als Werte) mit Objektorientierung und Imperativem Stil, wenn es nötig ist. Entwickelt bei Microsoft Research, eignet sich F# besonders für Datenanalyse, wissenschaftliches Rechnen und Domain-Driven Design – und sie läuft überall, wo .NET läuft (Windows, Linux, macOS).
Die wichtigsten F#-Befehle
dotnet new console -lang F#– erzeugt ein neues F#-Konsolenprojekt.dotnet new classlib -lang F#– erzeugt eine F#-Bibliothek.dotnet run– baut und startet das Projekt.dotnet build– kompiliert ohne zu starten;dotnet testführt Tests aus.dotnet fsi– startet die F# Interactive REPL.dotnet add package FSharp.Data– fügt ein NuGet-Paket hinzu.dotnet publish -c Release– erzeugt ein Release-Deployment.
Grundkonzepte
- Let-Bindings:
let x = 42bindet einen Namen an einen Wert. Alles ist ein Ausdruck, der einen Wert liefert. - Unveränderlichkeit: Standardmäßig sind Werte unveränderlich (
let mutablemacht sie änderbar). - Pipelines: Der Pipe-Operator
|>reicht Daten von links nach rechts durch Funktionen:values |> List.map f |> List.sum. - Pattern Matching:
matchzerlegt Werte und ersetzt längere if-else-Ketten. - Discriminated Unions: eigener Typ für klar definierte Alternativen – ideal für Domain-Modelle.
Beispiel: Fibonacci mit Pattern Matching
let rec fib n =
match n with
| 0 -> 0
| 1 -> 1
| n -> fib (n - 1) + fib (n - 2)
[ 0 .. 10 ] |> List.map fib |> printfn "%A"
F# vs. C#
Wo C# bei großen Enterprise-Anwendungen und Frameworks dominiert, punktet F# mit knapper, ausdrucksstarker Syntax und typsicheren Domain-Modellen. Beide Sprachen teilen dieselbe Laufzeit – F#-Code kann C#-Bibliotheken direkt nutzen und umgekehrt.
Weiterführend: Haskell: Pure Functional Programming und R für Datenanalyse.
CMake – der Standard-Build-Generator für C und C++
CMake ist kein Compiler, sondern ein Build-System-Generator: Aus einer plattformunabhängigen Beschreibung (CMakeLists.txt) erzeugt es Makefiles, Ninja-Dateien oder Projektdateien für Visual Studio und Xcode. Damit lässt sich derselbe Quellcode auf Linux, macOS und Windows bauen – die Grundlage fast aller großen C/C++-Projekte, von LLVM bis zu Qt.
Die wichtigsten CMake-Befehle
cmake -S . -B build– konfiguriert das Projekt: Quellverzeichnis-S, Build-Verzeichnis-B.cmake --build build– baut das Projekt im konfigurierten Verzeichnis.cmake --build build --target install– baut und installiert ein Ziel.cmake -DCMAKE_BUILD_TYPE=Release -S . -B build– setzt Konfigurationsvariablen.cmake -G Ninja -S . -B build– wählt den Generator (hier Ninja, deutlich schneller als Make).ctest --test-dir build– führt registrierte Tests aus.cpack– erzeugt Installationspakete (deb, rpm, zip, DMG).cmake --install build --prefix /usr/local– installiert ohne Build-Schritt.
Minimales CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(HalloWelt C)
add_executable(hallo main.c)
target_compile_features(hallo PRIVATE c_std_11)
cmake_minimum_required– legt die minimale CMake-Version fest.project– Projektname und Sprachen (C, CXX, Fortran …).add_executable/add_library– definiert Build-Ziele.target_link_libraries– verknüpft Ziele und Bibliotheken.find_package– findet externe Pakete (z. B.find_package(OpenSSL REQUIRED)).
Praxis-Tipps
- Out-of-Source-Builds: Immer in ein separates
build/-Verzeichnis bauen – nie im Quellbaum. Aufräumen =rm -rf build. - Presets:
CMakePresets.jsonspeichert häufige Konfigurationen (cmake --preset release). - Ninja: Der Generator
Ninjabaut parallel und ist deutlich schneller als Make – Standard bei modernen Projekten. - Cache:
cmake --build build --target helpzeigt alle Ziele;cmake-guibietet eine grafische Konfigurationsoberfläche.
Weiterführend: C und C++: Schnellreferenz und Kompilieren einfach erklärt.
Scala-Befehle – Schnellreferenz für die JVM
Scala vereint objektorientierte und funktionale Programmierung auf der Java Virtual Machine (JVM). Code in Scala ist oft deutlich kompakter als in Java, läuft aber im selben Ökosystem. Bekannt wurde Scala durch Apache Spark für Big Data und Akka für nebenläufige Systeme.
Erste Schritte: REPL und Projekt
scala # REPL starten
scala-cli run app.scala # Skript direkt ausführen
sbt new scala/scala3.g8 # neues Projekt (sbt-Build-Tool)
Grundlagen: val, var und Typen
val name: String = "KIT" // unveränderlich (immutable)
var count: Int = 0 // veränderlich (selten nötig)
val list = List(1, 2, 3) // Typ-Inferenz
Funktionale Bausteine
list.map(_ * 2) // [2, 4, 6]
list.filter(_ > 1) // [2, 3]
list.foldLeft(0)(_ + _) // 6
// Lambda-Funktion
val double: Int => Int = x => x * 2
Case Classes und Pattern Matching
case class Punkt(x: Int, y: Int)
val p = Punkt(3, 4)
p match {
case Punkt(0, 0) => "Ursprung"
case Punkt(x, y) => s"x=$x, y=$y"
}
Warum Scala?
- Interoperabel – nutzt Java-Bibliotheken direkt, läuft auf der JVM.
- Unveränderliche Datenstrukturen standardmäßig – weniger Nebenwirkungen, besser parallelisierbar.
- Nebenläufigkeit – Akka-Actors machen Concurrency handhabbar, siehe Glossar Thread und Process.
- Domain-Specific Languages – durch Operator-Überladung und Currying.
Alternativen im Vergleich
- Kotlin – moderner, pragmatischer, ebenfalls JVM.
- Elm – rein funktional, aber im Browser statt auf der JVM.
- F# – funktional auf .NET.
Weiterführend: Compiler, Garbage Collection und JVM.
Elixir und Erlang – Schnellreferenz
Erlang wurde bei Ericsson für Telefonvermittlungen entwickelt: extrem robust, fehlertolerant, nebenläufig. Elixir ist eine moderne Sprache auf der gleichen VM (BEAM), die Erlangs Stärken mit einer angenehmeren Syntax verbindet – bekannt durch das Web-Framework Phoenix und LiveView.
Erste Schritte
iex # Elixir-REPL starten
erl # Erlang-Shell
mix new my_app # neues Projekt anlegen
mix run # Projekt starten
Unveränderliche Daten und Pattern Matching
name = "Devon" # Neuzuweisung erzeugt neuen Wert
[head | tail] = [1, 2, 3] # head = 1, tail = [2, 3]
case {1, 2} do
{a, b} when a < b -> "kleiner"
_ -> "unbekannt"
end
Pipe-Operator: Datenfluss lesbar machen
"Hanse Computing"
|> String.upcase()
|> String.split(" ")
# ["HANSE", "COMPUTING"]
Der Pipe-Operator erinnert an Pipes in der Shell – das Ergebnis jeder Funktion wird als erstes Argument der nächsten übergeben.
Prozesse: das Herz von BEAM
# Prozess starten
pid = spawn(fn -> IO.puts("Hallo") end)
# Nachricht senden und empfangen
send(pid, {:hallo, "KIT"})
receive do
{:hallo, msg} -> IO.puts("Erhalten: " <> msg)
end
Millionen leichtgewichtiger Prozesse laufen parallel – kein Thread-Chaos wie in klassischen Sprachen. Dazu kommt OTP mit Supervisoren, die Prozesse bei Abstürzen automatisch neu starten („Let it crash").
Typische Befehle
mix test– Tests ausführenmix phx.new app– Phoenix-Projekt erzeugenmix deps.get– Abhängigkeiten laden (Hex-Paketmanager)mix format– Code formatieren
Verwandte Themen
Mehr zu Concurrency und Nebenläufigkeit: Process, Thread, Queue. Funktional programmiert auch Elm und Scala.