axelerator.de

Speed coding Minesweeper in a statically typed functional language

5 Minuten
#elm
19 September 2024

Elm kann fürs Prototyping genauso produktiv sein wie dynamische Programmiersprachen! Ich habe mir selbst die Herausforderung gestellt, einen browserbasierten Minesweeper-Klon in weniger als 2,5 Stunden und mit weniger als 100 Codezeilen zu programmieren und zu deployen.


Die Herausforderung: Statisch typisiertes Prototyping

Screenshot von Radan Skorićs persönlicher Website Lies den vollständigen Artikel “Exercise: Minesweeper in 100 lines of clean Ruby” auf Radan Skorićs persönlicher Website.

Kürzlich bin ich auf einen Blogbeitrag mit dem Titel “Exercise: Minesweeper in 100 lines of clean Ruby” gestoßen.

Als Entwickler, der seit 2006 mit Ruby arbeitet, habe ich Ruby’s lesbare Syntax und umfangreiches Ökosystem immer geschätzt. Die Fähigkeit, mit Ruby schnell Prototypen zu erstellen, war ein Eckpfeiler meiner Karriere und diente sowohl Startups als auch Anwendungen mit Millionen von Nutzern. Aber es gab auch Schattenseiten, insbesondere mit Ruby on Rails. Die Grundannahme das alle Objekte immer verändert werden können und der Mangel an ordentlicher statischer Analyse führten oft dazu, dass komplexere Anwendungen schwer zu warten und zu erweitern sind.

Beim Lesen des Artikels kam ich auf eine Idee. Kann ich das Gleiche in Elm erreich? Einer statisch typisierten, streng funktionalen Sprache, die oft als langsamer fürs Prototyping wahrgenommen wird?

Ich stellte mir selbst eine Herausforderung:

  1. Minesweeper in Elm nachbauen
  2. Unter 100 Codezeilen bleiben
  3. In weniger als 2,5 Stunden fertig werden
  4. Live Deployment inklusive

Und um den Einsatz zu erhöhen, beschloss ich, den gesamten Prozess auf Twitch zu streamen.

Ich habe die Aufnahme auch auf YouTube hochgeladen, mit detaillierten Kapitel-Sprungpunkten in der Beschreibung.

Vorurteile abbauen

Bevor wir uns die Ergebnisse ansehen, möchte ich erst den Elefanten im Raum ansprechen. Funktionale und statisch typisierte Sprachen leiden oft unter Vorurteilen:

  • Sie gelten als langsam bei der Entwicklung
  • Prototyping wird als umständlich empfunden
  • Die Notwendigkeit, alles im Voraus zu definieren, wird als Hindernis gesehen

Diese Annahmen könnten nicht weiter von der Wahrheit entfernt sein, und ich war dabei, das zu beweisen.

Der Elm-Vorteil

Aus dem Elm-Guide:
Wenn ein Wert jemals auf ungültige Weise verwendet werden kann, teilt dir der Compiler das mit einer freundlichen Fehlermeldung mit. Das nennt man Typinferenz. Der Compiler ermittelt, welche Art von Werten in alle deine Funktionen hinein- und herausfließen.

Eine der herausragenden Eigenschaften von Elm ist die Typinferenz. Dies ermöglichte es mir, schnell zu arbeiten, ohne explizite Typsignaturen schreiben zu müssen, was einer großen Kritikpunkte der Befürworter dynamischer Sprachen ist. Während der gesamten Sitzung lieferte der Compiler hilfreiche Fehlermeldungen basierend auf abgeleiteten Typen und leitete meinen Entwicklungsprozess.

Der Entwicklungsprozess

Das Aufsetzen der Elm-App von Grund auf und die Konfiguration des kontinuierlichen Deployments mit GitHub Actions dauerte weniger als 15 Minuten. Von dort aus erweiterte ich schrittweise die Beispiel-App aus dem offiziellen Elm-Guide und verwandelte sie in einen voll funktionsfähigen Minesweeper-Klon.

Eine der Stärken von Elm wurde früh deutlich: der Browser als Zielplattform. Im Gegensatz zur Ruby-Version, die auf Kommandozeileneingaben angewiesen war, bot unser Elm-Minesweeper eine klickbare Bedienelemente, was es erheblich einfacher machte, den Spielverlauf zu überprüfen.

Ich bin ein großer Fan von Elms Ausdrucksstärke, möchte hier aber nicht zu sehr auf Implementierungsdetails eingehen. Hier ist nur ein kleiner Ausschnitt, der eine der vielen Aspekte demonstriert, die zu lesbarem Code beitragen.

Dieser Code berechnet die Anzahl der umgebenden Bomben. Beachte, wie der |> Operator es uns ermöglicht, pipeline-artige Operationen klar zu beschreiben, ein Markenzeichen der funktionalen Programmierung. Der |> Operator übergibt im Grunde die Ausgabe einer Zeile als letztes Argument an den Funktionsaufruf in der folgenden Zeile.

numBombs =
  [(-1, -1), (0, -1), (1, -1) , (-1, 0),(1, 0) , (-1, 1), (0, 1), (1, 1)]
  |> List.map (\(x,y) -> (tx + x, ty + y))
  |> List.filter (inBounds width height)
  |> List.filter isBomb
  |> List.length

Herausforderungen und Lösungen

Das größte Hindernis kam nach etwa zwei Stunden bei der Implementierung der Rekursion zum Aufdecken leerer Felder. Dies wird normalerweise rekursiv gelöst, also habe ich natürlich eine Endlosschleife erstellt. Das fehlende Kriterium war, nicht auf Feldern zu rekursieren, die bereits aufgedeckt wurden. Elm ist sehr ausdrucksstark, und als Ergebnis passt die gesamte Funktion zum Reagieren auf einen Feldklick in 20 Zeilen. Es brauchte also nur etwas stoisches Anstarren und Nachdenken, um die fehlende Bedingung zu identifizieren.

Reflexionen und Implikationen

Ich war überrascht, wie sehr der Elm-Compiler mich unterstützte, selbst ohne benutzerdefinierte Typen und Annotationen. Diese Erfahrung bestärkte meine Überzeugung, dass in statisch typisierte Sprachen wie Elm Prototyping nicht nur möglich ist - sie sind tatsächlich besonders gut dafür geeignet!

Die Herausforderung, unter 100 Zeilen zu bleiben, zwang mich dazu, Funktionssignaturen vollständig wegzulassen. Dennoch bot Elms Typinferenz robuste Fehlerprüfung und Anleitung. Dies beweist, dass die Vorstellung äberholt ist, dass in statisch typisierte Sprachen umfangreiche Typdefinitionen im Voraus erforderlich sind.

Fazit

Dieses Experiment beweist, dass statisch typisierte Sprachen wie Elm für Prototyping genauso agil sein können wie ihre dynamischen Gegenstücke. Die Fähigkeit, ein browserbasiertes Spiel in weniger als 2,5 Stunden zu erstellen und zu deployen, mit den zusätzlichen Vorteilen der Typsicherheit, ist ein überzeugendes Argument Alternativen zu traditionellen Skriptsprachen in Erwägung zu ziehen.

Ob du ein Entwickler bist, der sonst lieber in seiner Komfortzone bleibt, oder jemand, der im Tumult der sich ständig ändernden JavaScript-Frameworks gefangen ist, ich ermutige dich, Elm auszuprobieren. Du wirst überrascht sein, wie schnell du Ideen in funktionierende, deploybare Anwendungen umsetzen kannst.

Das Ziel ist nicht unbedingt, deine Lieblingstools zu ersetzen, sondern dein Toolkit zu erweitern. Elm bietet eine einzigartige Mischung aus Geschwindigkeit, Sicherheit und Einfachheit, die die Art und Weise, wie du dein nächstes Projekt angehst, verändern wird.

Was denkst du? Hast du ähnliche Erfahrungen mit statisch typisierten Sprachen gemacht? Ich würde gerne deine Perspektive in den Kommentaren unten hören!

Ressourcen für deine Elm-Reise

Wenn das dein Interesse an Elm geweckt hat, hier sind ein paar Ressourcen, mit denen du loslegen kannst: