16 Punkte von GN⁺ 2025-02-25 | 3 Kommentare | Auf WhatsApp teilen
  • Clojure ist keine der Mainstream-Programmiersprachen und manchen Menschen möglicherweise unbekannt
  • Die wichtigsten Vorteile von Clojure
    • Produktivität für Entwickler: Clojure ist interaktiv, reduziert Wiederholungsarbeit und bietet eine effiziente Entwicklungsumgebung. Entwickler können zufrieden arbeiten und Produkte schnell veröffentlichen
    • Langfristige Wartbarkeit: Die Sprache Clojure und ihr Ökosystem sind ausgereift und stabil. So lassen sich hochwertige Systeme bauen und gleichzeitig Wartungskosten senken
    • Ideenzentrierte Kultur: Die Clojure-Community untersucht Ideen aus Vergangenheit und Gegenwart sowie aus Wissenschaft und Industrie, um bessere Wege der Softwareentwicklung zu finden. Das bietet Entwicklern neue Herausforderungen und Lernmöglichkeiten

(hello 'clojure)

  • Clojure ist eine Sprache aus der Lisp-Familie, deren Wurzeln in den 1950er-Jahren liegen
    • Lisp begann als theoretisches Modell, bietet aber auch in der praktischen Programmierung eine hohe konzeptionelle Eleganz
    • Da die Syntax des Codes direkt mit Datenstrukturen übereinstimmt, bietet sie viele Vorteile gegenüber Sprachen mit komplexer Grammatik
    • Während des früheren AI-Booms wurde Lisp als wichtige Sprache eingesetzt
  • Sprachen aus der Lisp-Familie wurden immer wieder populär und verschwanden dann wieder, doch in den letzten zehn Jahren hat Clojure besondere Aufmerksamkeit erhalten
  • Clojure läuft auf der JVM von Java und greift moderne Programmierkonzepte auf
    • starke Unterstützung für unveränderliche Datenstrukturen
    • ein auf Nebenläufigkeit ausgelegtes Design
  • Obwohl die Community klein ist und ohne Unterstützung großer Konzerne gewachsen ist, besitzt die Sprache starke Eigenschaften
  • Clojure kann in verschiedenen Umgebungen ausgeführt werden
    • ClojureScript: wird zu JavaScript kompiliert und dort ausgeführt
    • ClojureCLR: läuft in der .NET-Umgebung
    • Babashka: schneller Skript-Interpreter auf Basis von GraalVM
    • Jank: unterstützt Native Compilation
  • Wer Clojure lernt, kann dasselbe Wissen in vielen Umgebungen nutzen

Interaktive Entwicklungsweise

  • Programmierung ist ein wiederholter Prozess aus Code schreiben und verifizieren
    • Ohne Feedback ist es schwer, sicher zu sein, ob der Code korrekt funktioniert
    • Übliche Formen von Feedback:
      • wiederholtes Ausführen von Skripten
      • UI-Interaktion und Log-Ausgaben
      • Einsatz von Unit-Tests
      • Nutzung von Compilern und statischen Analysewerkzeugen
    • Schnelles Feedback hat großen Einfluss auf die Produktivität von Entwicklern
      • Je langsamer das Feedback, desto mehr Zeit geht für Debugging verloren und desto weniger bleibt fürs Schreiben von Code
  • Zusätzlich zu diesen klassischen Feedback-Mechanismen bietet Clojure interaktive Entwicklung (interactive development)
    • Zentral ist die REPL-basierte Entwicklung (Read-Eval-Print Loop)
    • Entwickler starten vor dem Schreiben des Codes die Clojure-Runtime und verbinden sie mit dem Editor
    • Einzelne Code-Fragmente können nacheinander ausgeführt werden und liefern sofortiges Feedback
    • Dank der konsistenten Syntaxstruktur von Lisp lassen sich kleine Code-Stücke ebenso frei ausführen wie größere Code-Blöcke
  • Anders als bei einer gewöhnlichen REPL kann Clojure Code-Fragmente ausführen, während es mit einem laufenden System verbunden ist
    • Das liefert eine deutlich stärkere Feedback-Schleife als das klassische Muster „Code schreiben → ausführen → debuggen“
    • Programme lassen sich in Echtzeit ändern und debuggen
    • Es ist nicht nur eine einfache REPL, sondern ein leistungsfähiges Interaktionswerkzeug, das auch in Produktionsumgebungen einsetzbar ist

Eine Kultur, die Stabilität ernst nimmt

  • Wer sich für Clojure entscheidet, erhält nicht nur leistungsfähige Technik, sondern wird Teil einer Community mit eigener Philosophie und klaren Prinzipien
    • Wer nur die Technik übernimmt und nicht mit der Community in Kontakt tritt, wird den eigentlichen Wert von Clojure nur schwer erfahren
  • Die Clojure-Community betrachtet Stabilität und Abwärtskompatibilität als zentrale Werte
  • Die Kernsprache wird kontinuierlich verbessert und erweitert, fast ohne jemals Breaking Changes einzuführen
  • Auch das Open-Source-Ökosystem von Clojure folgt derselben Philosophie und minimiert unnötige Veränderungen (churn)
    • Das steht im Kontrast zu den meisten anderen modernen Ökosystemen von Programmiersprachen
    • Die Verschwendung von Ressourcen durch unnötige Änderungen summiert sich weltweit auf Milliardenbeträge
  • In Clojure ist es sehr natürlich, auf die neuesten Versionen von Sprache und Bibliotheken zu aktualisieren
    • Man profitiert von Bugfixes, Sicherheitsupdates und Performance-Verbesserungen, ohne die Codebasis neu schreiben zu müssen
    • Während in anderen Sprachen bestehender Code bei neuen Versionen leicht brechen kann, bleibt er in Clojure meist unverändert nutzbar
  • Manche Entwickler fragen sich vielleicht: „Wie kann man sich ohne Veränderung weiterentwickeln?“
    • Stabilität ist jedoch nicht dasselbe wie Stagnation
    • Clojure entwickelt sich weiter, indem es neue Funktionen ergänzt und bestehende verbessert, ohne Vorhandenes zu brechen
    • Entwickler können dadurch fortlaufend bessere Werkzeuge nutzen, ohne unnötige Codeanpassungen vornehmen zu müssen

Informationssysteme und Wissensrepräsentation

  • In der Entwicklung von Web- und Business-Anwendungen stehen das Sammeln, Zugreifen und Verarbeiten von Informationen im Mittelpunkt
  • Viele Mainstream-Sprachen zeigen bei der Darstellung und Manipulation von Informationen jedoch ineffiziente Entwurfsentscheidungen
    • Sie erzwingen low-level Datenstrukturen und verursachen dadurch unnötige Komplexität
    • Statische Typsysteme sind oft zu starr, was flexible Datenmanipulation erschwert
  • Clojure bietet von Haus aus funktionale Datenstrukturen und starke Möglichkeiten zur Datenmanipulation
    • Als dynamisch typisierte Sprache folgt es der „Open World Assumption“
      • Das maximiert Erweiterbarkeit und Flexibilität von Daten
    • Es ist stark von RDF (ein Framework zur Datenmodellierung für das Semantic Web) beeinflusst
      • Daraus ergibt sich unter anderem eine starke Synergie mit Graph-Datenbanken wie Datomic
      • Durch Keywords mit Namespace lassen sich kontextunabhängige Bedeutungen ausdrücken
  • Die Map-Struktur von Clojure mit Namespace-Keywords kann Bedeutungen präziser ausdrücken als einfaches JSON
  • Daten lassen sich leicht erweitern, und zugleich ist eine intuitive Darstellung ohne Namenskonflikte möglich

Kleine kombinierbare Funktionen und unveränderliche Daten

  • In Clojure ist es üblich, vor allem mit reinen Funktionen (pure functions) und unveränderlichen Daten (immutable data) zu programmieren
  • Man kann zwar auch imperativen Code im Stil von Java, Ruby oder C schreiben, aber idiomatisches Clojure sieht ganz anders aus
    • Reine Funktionen geben Ergebnisse allein auf Basis ihrer Eingaben zurück und verändern keinen externen Zustand
    • Unveränderliche Daten haben eine Bedeutung auf Basis von Werten (values) statt von Referenzen oder Objektidentität
  • Weil kein externer Zustand hineinspielt, ist der Code vorhersagbarer
  • Änderungen an globalen Variablen oder unerwartete Side Effects treten nicht auf
  • Da keine Gefahr besteht, dass Daten verändert werden, lassen sich Parallelisierung und Nebenläufigkeit leichter beherrschen

Nebenläufigkeit

  • Moderne Computer basieren auf Multicore-Prozessoren, und Nebenläufigkeit ist daher unverzichtbar
    • Da das Moore’sche Gesetz an Grenzen stößt, ist die Nutzung von Parallelität der Schlüssel zu mehr Performance
    • Wer jedoch mit mutable state arbeitet, muss Synchronisationsprobleme und komplexe Zeitsteuerung lösen
  • Clojure betont Immutability und adressiert damit Nebenläufigkeitsprobleme grundlegend
    • Wer veränderlichen Speicher manipuliert, schafft Abhängigkeiten von Timing und Reihenfolge
    • Reine Datentransformationen im Stil data-in, data-out lassen sich immer sicher parallel ausführen
  • Clojure nutzt die vorhandenen Nebenläufigkeitsfunktionen der JVM (java.util.concurrent), stellt aber höher abstrahierte Werkzeuge bereit
    • Atoms: unterstützen atomare Operationen mit CAS (Compare-and-Set) und automatischen Wiederholungsversuchen
    • Refs: bieten Software Transactional Memory (STM)
    • Agents: wenden Updates asynchron an
    • Futures: bieten ein fork-and-join Interface auf Basis von Thread-Pools
  • Es gibt außerdem die Bibliothek core.async
    • Sie unterstützt das CSP-Muster (Communicating Sequential Processes) ähnlich wie die Goroutines von Go
    • Vergleichbar ist das mit dem Actor-Modell von Erlang/Elixir oder Akka in Scala
  • Auch low-level Techniken der Nebenläufigkeitssteuerung können genutzt werden, ohne die höherstufigen Abstraktionen von Clojure zu verwenden
    • Unterstützt werden unter anderem synchronisierte Queues, atomare Referenzen, Locks, Semaphore, verschiedene Thread-Pools und manuelle Thread-Verwaltung
  • Wenn nötig, ist feingranulare Nebenläufigkeitssteuerung möglich, aber in den meisten Fällen ist der Einsatz abstrahierter Werkzeuge sicherer und effizienter

Lokales Schlussfolgern (Local Reasoning)

  • Die Komplexität von Code, die man auf einmal erfassen kann, ist begrenzt
  • Wenn Programmzustand und Veränderungen an vielen Stellen auftreten, werden Verständnis und Wartung schwierig
  • Warum Clojure lokales Schlussfolgern erleichtert
    • Code rund um reine Funktionen
      • Das Verhalten einer Funktion lässt sich allein aus ihren Eingaben vollständig verstehen
      • Externer Zustand muss nicht berücksichtigt werden
    • Unterschied zu objektorientierten Sprachen
      • Clojure minimiert Polymorphismus, sodass leichter nachzuvollziehen ist, wo Code ausgeführt wird
      • In der objektorientierten Programmierung (OOP) „passiert alles irgendwo anders“,
        während man in Clojure nur Funktionen verfolgen muss, die in Namespaces definiert sind
  • Dank der konsistenten Syntaxstruktur von Clojure ist Refactoring einfach
    • Durch unveränderliche Daten und reine Funktionen gibt es bei Änderungen weniger unerwartete Side Effects
    • Das notwendige Minimum an imperativem Code wird getrennt gehalten, sodass sich imperativer und funktionaler Code harmonisch kombinieren lassen

Einfaches Testen

  • Bei Clojure-Code auf Basis reiner Funktionen kann man einfach Eingaben einsetzen und Ausgaben prüfen
    • Komplexe Zustandsinitialisierung, Mock-Objekte oder Timing-Kontrolle sind für Tests nicht nötig
    • Dadurch steigt die Zuverlässigkeit von Tests und Flakiness tritt seltener auf
  • Unterstützt werden auch fortgeschrittene Testverfahren wie Property Based Testing (Generative Testing)
    • Dabei werden zufällige Eingaben erzeugt, um nach Verletzungen bestimmter Eigenschaften oder Invarianten zu suchen
    • Unterstützt wird auch die Shrinking-Technik, die minimale Fehlerfälle findet
  • Das Konzept stammt aus Haskell und wurde in vielen Sprachen als QuickCheck-basierte Test-Frameworks umgesetzt
  • In Clojure wirkt das besonders gut durch die Synergie mit unveränderlichen Datenstrukturen und REPL-basierter Entwicklung

Vorteile bei der Einstellung von Clojure-Entwicklern

  • Allgemein gibt es im Vergleich zu verbreiteten Sprachen wie JavaScript oder Python relativ wenige Clojure-Entwickler
  • Aber weil es auch nicht viele Unternehmen gibt, die Clojure einsetzen, bleibt das Verhältnis von Angebot und Nachfrage insgesamt ausgeglichen
    • In der Praxis kommt es oft zu passenden Matches zwischen Unternehmen, die Clojure-Entwickler suchen, und Entwicklern selbst
  • Clojure-Entwickler verfügen häufig über starke Problemlösungskompetenz
    • Viele lernen nicht einfach nur populäre Technologien, sondern interessieren sich für neue Denkweisen und Ideen
    • Unternehmen, die Clojure einsetzen, berichten in der Praxis oft: Es gibt zwar weniger Bewerber, dafür aber viele sehr qualifizierte Entwickler
    • Beispiel: Nubank hat in Brasilien Hunderte Entwickler direkt in Clojure geschult und damit einen erfolgreichen Fall geschaffen
  • Clojure-Entwickler zu finden ist schwierig, aber wenn man passende Talente findet, ist die Chance auf hervorragende Entwickler hoch
    • Statt nur nach Entwicklern mit direkter Sprach-Erfahrung zu suchen, kann es auch sinnvoll sein, lernfähige Entwickler gezielt auszubilden
    • Die Clojure-Community zieht von sich aus Entwickler an, die Probleme gründlich durchdenken und lösen wollen

Trade-offs und Steuerung des Abstraktionsniveaus

  • Clojure ist eine High-Level-Sprache, die auf knappen und ausdrucksstarken Code abzielt
    • Durch funktionale, unveränderliche Datenstrukturen und leistungsfähige APIs zur Datenmanipulation wird unnötige Komplexität reduziert
  • Die Datenstrukturen von Clojure verwenden intern Baumstrukturen (Hash Array Mapped Trie), um Unveränderlichkeit zu garantieren
    • Bei Updates kommt es zu Path Copying, was den GC (Garbage Collector) stärker belasten kann
    • Bei der Interoperabilität mit Java können außerdem Runtime-Reflection sowie Boxing/Unboxing auftreten
    • In typischen Anwendungen sind diese Kosten fast vernachlässigbar, während der Gewinn an Entwicklungsproduktivität groß ist
  • Für Bereiche mit hohem Performance-Bedarf wie Echtzeit-Grafik-Engines, Signalverarbeitung oder numerische Berechnungen sind low-level Optimierungen möglich
    • In Clojure lassen sich mit Type Hints Reflection vermeiden und Primitive-Operationen optimieren
    • Mit zusammenhängenden Arrays primitiver Typen kann der CPU-Cache effizient genutzt werden
    • Auch GPU-beschleunigte Bibliotheken können verwendet werden
  • Die meisten High-Level-Sprachen müssen bei Performance-kritischen Fällen auf native Erweiterungen in C/Rust zurückgreifen,
    Clojure kann jedoch mithilfe der JVM-Optimierung (JIT-Kompilierung) die meisten Performance-Probleme lösen
    • Mit Profiling und etwas Optimierung lässt sich etwa die Performance von Event-Loops stark steigern

Metaprogrammierung und datengetriebene APIs

  • Als Sprache aus der Lisp-Familie kann Clojure Code wie Daten behandeln
    • Ähnlich wie JSON, aber in einer besser lesbaren Struktur, lassen sich Programme darstellen
    • Mit dem Datenformat EDN (Extensible Data Notation) bietet Clojure eine JSON-ähnliche Darstellungsform für Daten
  • Clojure bietet mit Makros die Möglichkeit, Code selbst umzuwandeln
    • In der Clojure-Community gibt es jedoch eine Kultur, den Einsatz von Makros vorsichtig zu begrenzen
    • Makros sind schwer zu debuggen und können mit statischen Analysewerkzeugen schlechter zusammenspielen
  • Stattdessen wird datengetriebenes API-Design (data-driven) bevorzugt
    • Beispiele: HTTP-Routing, HTML/CSS-Erzeugung, Datenvalidierung
    • Statt bestimmte Funktionen direkt aufzurufen, wird das Verhalten über Datenstrukturen wie Maps und Vektoren beschrieben
    • Datenbasierte APIs lassen sich dynamisch manipulieren und Benutzerkonfigurationen leicht speichern und ändern
  • Dank datengetriebener APIs lässt sich ein System zur Laufzeit dynamisch rekonfigurieren
    • So werden hochflexible Simulationssysteme, dynamische Konfigurationsverwaltung und Metaprogrammierung leicht umsetzbar

Java-Interop und Nutzung des Ökosystems

  • Die Entwicklung moderner Anwendungen ist ein Prozess der Kombination zahlloser Open-Source-Bibliotheken und APIs
    • Da Clojure auf der JVM läuft, können Millionen von Java-Paketen aus Maven Central genutzt werden
    • Auch ohne Reflection lassen sich Java-Bibliotheken einfach aufrufen
  • Im Vergleich zu Java ist Clojure deutlich knapper, und experimentelles Programmieren mit der REPL fällt leicht
    • APIs lassen sich viel schneller erkunden und kombinieren als in Java
    • Mit ClojureScript kann auf dieselbe Weise auch das JavaScript- und NPM-Ökosystem genutzt werden

Eine ideenzentrierte Kultur

  • Mit jeder Sprache lassen sich erfolgreiche Projekte umsetzen, und umgekehrt kann man mit falscher Herangehensweise in jeder Sprache scheitern
    • Gute Werkzeuge machen noch keine guten Entwickler
  • Clojure hat eine hohe Einstiegshürde, und der Lernprozess verlangt Versuch und Irrtum, eröffnet dafür aber tiefes Nachdenken
  • In manchen Clojure-Projekten lebt Code im Stil von Java oder Python weiter, sodass die Sprache nicht richtig genutzt wird
    • Teams jedoch, die die Philosophie und Ideen von Clojure annehmen, entwickeln bessere Fähigkeiten im Softwaredesign
  • Die Clojure-Community stellt bestehende Entwicklungsweisen fortlaufend infrage und sucht nach besseren Wegen
    • Vorträge von Rich Hickey (dem Schöpfer von Clojure) sind nicht bloß technische Einführungen, sondern Untersuchungen grundlegender Prinzipien von Softwaredesign
    • Auf Clojure-Konferenzen stehen Ideen, Paper-Analysen und Erfahrungsaustausch stärker im Mittelpunkt als bloße Bibliotheksvorstellungen

Fazit

  • Clojure ist nicht nur eine Programmiersprache, sondern eine Community von Menschen, die über bessere Wege der Softwareentwicklung nachdenken
  • In dieser Community gehört es zu den zentralen Werten, eigene Grenzen zu erweitern und neue Möglichkeiten zu erkunden
  • Entwickler, die in einer solchen Kultur wachsen, werden nicht nur gut in Clojure, sondern zu Softwareingenieuren mit stärkerer Problemlösungskompetenz

3 Kommentare

 
roryk 2025-02-26

Ich benutze Clojure persönlich, und ich kann dem Inhalt des Artikels sehr gut zustimmen.
Beruflich habe ich hauptsächlich Python und Java(Type)Script verwendet, aber wenn man die Pflege auch nur ein wenig vernachlässigt, wird der Code leicht zu Legacy-Code, weil man mit den Veränderungen der Sprache selbst und der Bibliotheken nicht Schritt hält. Bei Clojure war ich dagegen sehr zufrieden damit, dass einmal geschriebener Code auch ein Jahr später noch leicht direkt angepasst und weiterentwickelt werden kann.
Seitdem nutze ich Clojure privat bevorzugt, solange ich nicht durch die Einschränkungen einer bestimmten Bibliothek dazu gezwungen bin, etwas anderes zu verwenden.

 
plumpmath 2025-02-25

Jank Jank~!

 
GN⁺ 2025-02-25
Hacker-News-Kommentare
  • Auf die Frage, welche Art des Programmierens mir Spaß gemacht hat, wäre die Antwort: Datenverarbeitungspipelines in der Shell zu bauen und in den letzten 5 Jahren Clojure und ClojureScript zu schreiben

    • Derzeit schreiben 4 Personen gemeinsam Clojure und verfügen zusammen über mehr als 30 Jahre Clojure-Erfahrung
    • In Prag wurde ein auf Clojure setzendes Beratungsunternehmen gegründet, und man ist der Meinung, dass es für verschiedene Systeme eine passende Wahl sein kann
  • Ich nutze Clojure seit 12 Jahren, davor habe ich mehr als 12 Jahre Java verwendet

    • Mit Clojure und Java wurden großartige Apps und Bibliotheken entwickelt
    • Clojure wird als die „am wenigsten schlechte Programmiersprache“ beschrieben, mit einem starken und stabilen Kern
    • Wenn man die Werkzeuge beherrscht, steigen Arbeitstempo und Präzision
    • Der REPL-orientierte Workflow von Clojure ist ein wichtiger Teil des Wertversprechens
  • Ich liebe es, Clojure zu schreiben, und habe erkannt, dass ich diese tiefe Zuneigung zu Clojure im Vergleich zu anderen Sprachen gar nicht erklären muss

    • Persönliche Probleme mit anderen Sprachen interessieren mich nicht, und meine Liebe zu Clojure hat theoretische, praktische, emotionale und finanzielle Gründe
    • Clojure ist nicht perfekt, aber unter den allgemeinen Programmiersprachen die befriedigendste
  • Ein Mitgründer verfolgt das Ziel, mit einer möglichst kleinen Firma möglichst viel Produkt zu schaffen

    • Nach 5 Jahren Bootstrapping wurde ein ARR von über 1 Million Dollar erreicht, wobei Clojure eine große Rolle gespielt hat
    • Clojure hat auch zum Glück als Programmierer beigetragen, und künftig soll das Kernproduktteam für Clojure wachsen
  • Mit Clojure wurde 10 Jahre lang ein SaaS-Geschäft betrieben, und ohne Clojure wäre das nicht möglich gewesen

    • Die Stabilität der Sprache ist äußerst nützlich, während man in anderen Ökosystemen Software oft regelmäßig neu schreiben muss
    • Statt oberflächlicher Kritik wird empfohlen, aufschlussreiche Beiträge von Leuten zu lesen, die die Sprache tatsächlich verwendet haben
  • Nutzern von Clojure wird <a href="https://www.flow-storm.org/">Flow Storm</a> empfohlen

    • In Kombination mit einer guten Testsuite lassen sich verschiedene Szenarien auslösen
  • Von Rich Hickey wurde viel gelernt, und es gab große Begeisterung für Clojure und FP

    • Mit dem Wachstum des Geschäfts wurde dynamische Typisierung jedoch zur Belastung, und nach dem Weggang wichtiger Leute war es schwer, Clojure-Entwickler zu finden
    • Der Großteil des Stacks besteht inzwischen aus Python und Go
  • Es wurde angemerkt, dass die Dokumentation auf ClojureDocs veraltet sei, und man wollte eine Funktion hinzufügen, mit der sich Antworten hochvoten lassen

    • Dank des Google-Algorithmus wird ClojureDocs hauptsächlich verwendet
  • Der Abschnitt über die Stabilität von Clojure war überraschend, denn bei jedem jährlichen Versuch fühlte es sich an, als hätte sich alles verändert

    • Es wird gefragt, ob „Hello World“ noch immer langsam startet; Clojure zu lesen macht weiterhin Spaß, aber es zu schreiben war immer mit Hürden verbunden
  • Nach einem Einstieg mit Common Lisp ging es weiter zu Go und Rust, doch in letzter Zeit wird Clojure wieder angeschaut

    • Dank REPL und interaktivem Programmieren lassen sich Dinge schnell erledigen
    • Die JVM ist eine hervorragende Runtime