3 Punkte von GN⁺ 23 일 전 | 1 Kommentare | Auf WhatsApp teilen
  • Eine kleine Sprache, die Syntax im Rust-Stil verwendet und auf der Go-Laufzeit läuft, also die Stärken beider Sprachen kombiniert
  • Eine Struktur, die Sicherheit und Ausdrucksstärke durch algebraische Datentypen, Pattern Matching, ein Hindley-Milner-Typsystem und standardmäßige Unveränderlichkeit stärkt
  • Gewährleistet Interoperabilität mit dem Go-Ökosystem durch direkten Import von Go-Paketen, den Pipeline-Operator, try-Blöcke und task-basierte Nebenläufigkeit
  • Verbessert Developer Experience und Code-Stabilität durch Fehlererkennung zur Compile-Zeit, klare Diagnosemeldungen und LSP-Unterstützung
  • Zentral ist, dass Lisette-Code in klar lesbaren Go-Code umgewandelt wird und sich dadurch natürlich in bestehende Go-Projekte integrieren lässt

Überblick über Lisette

  • Lisette ist eine kleine Sprache, die auf Rust-Syntax basiert und in die Go-Laufzeit kompiliert wird
  • Zu den Merkmalen gehören algebraische Datentypen, Pattern Matching, kein nil, ein Hindley-Milner-Typsystem, standardmäßige Unveränderlichkeit und Interoperabilität mit dem Go-Ökosystem
  • Die Installation ist mit dem Befehl cargo install lisette möglich; außerdem lassen sich Go-Pakete wie fmt, io und os direkt importieren und verwenden

Vertraute Syntax

  • Besitzt eine Rust-ähnliche Syntaxstruktur
    • Unterstützt Pattern Matching mit enum und match
    • Mit struct- und impl-Blöcken lassen sich Methoden definieren
  • Als ausdrucksorientierte Sprache geben if, let und Blöcke allesamt Werte zurück
  • Unterstützt Chaining und Lambdas, sodass sich die Verarbeitung von Umgebungsvariablen oder String-Manipulationen kompakt ausdrücken lässt
  • Unterstützt Interfaces und Generics; generische Funktionen können mit interface-Definitionen und T: Trait-Constraints geschrieben werden
  • Mit if let- und let else-Syntax lässt sich der Option-Typ kompakt behandeln

Sicherheit

  • Erkennt mögliche Fehler der Go-Laufzeit bereits zur Compile-Zeit

    • Wenn in einer match-Anweisung nicht alle Muster behandelt werden, tritt ein Fehler auf
    • nil kann nicht verwendet werden; fehlende Werte werden mit Option<T> dargestellt
    • Wird ein Result-Rückgabewert ignoriert, erscheint eine Warnung
    • Wird ein nicht öffentlicher Typ in einer öffentlichen API offengelegt, erscheint eine Warnung
    • Wird eine unveränderliche Variable an ein veränderliches Argument übergeben, entsteht ein Fehler
    • Fehlen Struct-Felder, führt das zu einem Compile-Fehler
    • Diagnosemeldungen liefern zusammen mit konkreten Code-Positionen auch Korrekturvorschläge
    • Dank LSP (Language Server Protocol) wird die Nutzung in wichtigen Editoren wie VSCode, Neovim und Zed unterstützt

Nutzbarkeit

  • Mit Fokus auf Interoperabilität mit Go entworfen
  • Der Pipeline-Operator (|>) drückt Function Chaining kompakt aus
  • try-Blöcke vereinfachen die Fehlerweitergabe
  • Nebenläufigkeit wird mit task und Channel umgesetzt, ähnlich wie Goroutines in Go
  • Über Serialisierungsattribute (attribute) lassen sich JSON-Feldnamen, Auslassung, String-Konvertierung und Validierungs-Tags festlegen
  • Bietet einen recover-Block zur Wiederherstellung nach panics; mit dem Result-Typ ist sichere Fehlerbehandlung möglich
  • Unterstützt die defer-Syntax, um Ressourcenbereinigung oder Transaction-Rollback sicherzustellen

Transparente Compiler-Ergebnisse

  • Lisette-Code wird in klaren und gut lesbaren Go-Code umgewandelt
    • Option- und Result-Typen werden jeweils in lisette.Option- bzw. lisette.Result-Structs umgewandelt
    • match-Syntax wird in Go-Bedingungsanweisungen umgewandelt, die die einzelnen Zweige verarbeiten
    • Der ?-Operator wird intern durch Code zur Prüfung von Result ersetzt
  • Im Beispiel nimmt die Funktion classify ein Option<int> entgegen und wird in explizite Go-Bedingungsanweisungen umgewandelt; die Funktion combine wird in Go-Code übersetzt, der Result prüft

Weitere Informationen

  • Offizielles Repository: github.com/ivov/lisette
  • Veröffentlicht unter der MIT-Lizenz; entwickelt laut Stand 2026 von Iván Ovejero

1 Kommentare

 
GN⁺ 23 일 전
Hacker-News-Kommentare
  • Ich habe mit dem Autor gesprochen und die Sprache selbst zwar nicht ausprobiert, aber Lisette wirkt interessant und wie eine klare Verbesserung gegenüber Go.
    Trotzdem glaube ich, dass sich die Grenzen von Go nicht vollständig überwinden lassen. Zum Beispiel wird das typed nil-Problem aus Gos Interface-Typen in Lisette mit Option behandelt, aber doppeltes Unwrapping (Some(Some(h))) kann sich unbeholfen anfühlen.
    Außerdem ist Gos defer-Ansatz weiterhin unpraktisch und bietet keine automatische Ressourcenfreigabe wie RAII.
    TypeScript hat JavaScript deshalb ergänzt, weil es keine Alternative gab, die im Browser laufen konnte; mit WASM ist die Lage heute anders.
    Daher stellt sich die Frage: „Warum Go wie Rust machen, wenn es bereits Rust gibt?“ Allerdings scheint Lisette genau auf diese Zwischenposition zu zielen.
    Letztlich wirkt Lisette wie eine passende Sprache für Leute, die bestehende Go-Codebasen verbessern oder weiterhin die Go-Runtime nutzen möchten.
    Was mir fehlt, ist ein Quickstart-Guide zur Frage: „Wie fange ich an, die folgende Datei statt in Go in Lisette zu schreiben?“
    Zugehöriger Blogpost: Go is still not good

    • Go ist weiterhin einzigartig, weil es eine GC-basierte Concurrency-Runtime bietet.
      In Problemfeldern mit komplexen Referenzgraphen ist GC unverzichtbar, und dank Gos User-Mode-Stack-Struktur hat es ein effizientes Speichermodell.
    • In GC-Sprachen ist die Entwicklung viel schneller. Ich habe Chatbots in Rust und Python gebaut, und trotz Rust-Erfahrung war Python deutlich schneller.
      Go eignet sich auch für solche schnell gebauten CLI-Tools — zum Beispiel: wordle-tui
    • Go hat als Sprache viele Merkwürdigkeiten, ist aber als Compiler-Target hervorragend.
      Die Syntax ist einfach, es gibt Cross-Platform-Support, eingebaute Runtime und GC, das Modell „errors as values“, Green Threads, einen schnellen AOT-Compiler und mehr.
      Gos defer ist nützlich, aber Fehlerbehandlung und Scope-Regeln wirken unbeholfen.
    • Die Formulierung aus dem Blog, Go sei „die Sprache, die NULL versehentlich zweimal erfunden hat“, fand ich einprägsam.
      TypeScript hat dieses Problem auch nicht gelöst, sondern eher verschlimmert. Deshalb habe ich selbst ein Option-Type-Paket gebaut und auf NPM veröffentlicht → fp-sdk
    • Rusts async ist ohne GC weniger bequem als in Go. Allein das ist schon ein Grund, sich für die Go-Runtime zu entscheiden.
  • Es gibt bereits mehrere Sprachen, die nach Go kompilieren — XGo, Borgo, Soppo usw.

    • Borgo und Lisette ersetzen Rückgaben vom Typ (T, error) einfach durch einen Result-Typ, aber semantisch ist das nicht völlig gleichwertig.
      Zum Beispiel bedeutet bei io.Reader.Read (n!=0, io.EOF) ein normales Ende; wenn man das einfach als Fehler behandelt, führt das zu falschem Verhalten.
    • Ich frage mich, wie Compiler-Fehler von der Zielsprache an die Quellsprache zurückgereicht werden.
  • Die Qualität der Fehlermeldungen von Lisette ist beeindruckend. Die „help“-Hinweise wirken tatsächlich nützlich.
    Allerdings könnte der nach Go übersetzte Code sehr ausführlich werden, daher mache ich mir Sorgen, dass man bei Runtime-Fehlern im Go-Code debuggen muss.
    Außerdem scheint die Richtung, aus bestehendem Go-Code heraus Lisette aufzurufen, schwierig zu sein.
    Ich frage mich, ob Lisette eine experimentelle Sprache ist oder tatsächlich auf Produktion zielt.

    • Der Entwickler hat selbst geantwortet: Mit der Option lis run --debug werden in den Go-Code //line source.lis:21:5-Kommentare eingefügt, sodass Stacktraces auf den ursprünglichen Lisette-Code gemappt werden.
      Das LSP behandelt Compile-Time-Fehler auf Basis der .lis-Dateien.
      Aktuell gibt es noch keine Möglichkeit, Lisette aus Go aufzurufen, aber Go-Pakete aus Lisette zu importieren hat Priorität.
      Anfangs war es ein Experiment, aber das Ziel ist, es zu einer produktionsreifen Sprache weiterzuentwickeln.
  • Ich frage mich, warum man die Rust-ähnliche Syntax nicht einfach vollständig übernommen hat.
    Zum Beispiel import "foo.bar" statt use foo::bar, Bar.Baz => statt Bar::Baz => usw.
    Wer Rust kennt, wird dadurch verwirrt, und wer es nicht kennt, kann Wissen nicht auf Rust übertragen.

    • Solche Syntaxunterschiede sind nebensächlich; entscheidend ist, Rusts Typsystem nach Go zu bringen.
      int und float64 folgen Gos Namenskonventionen für Typen.
    • Wenn man zwischen vielen Sprachen wechselt, sorgt Syntaxähnlichkeit eher für zusätzliche Verwirrung. Ich vergesse zum Beispiel in PHP oft, dass man statt + einen . verwenden muss.
    • Ich wollte anfangs selbst eine Rust-artige Sprache auf TypeScript-Basis bauen und habe dann festgestellt, dass Rust selbst gar nicht so schwierig ist, wie es wirkt.
    • In einer GC-Sprache ein Rust-artiges Speichermodell umzusetzen, ist sehr unnatürlich. Es ist, als hätte jedes Objekt einen eigenen Adressraum, was die Sache kompliziert macht.
    • Lisette ist eine von Rust inspirierte Sprache, versucht aber nicht, Rust zu sein. Die Hauptzielgruppe sind Go-Entwickler.
  • Die Go-Runtime ist gut, aber die Sprache selbst wirkt grob und wenig verbesserungswillig.
    Wenn man deshalb schon zu einem Transpiler greift, muss man Go vermutlich wirklich nicht mögen.

  • Ich frage mich, warum Rust und Rust-artige Sprachen Struct und Methode trennen.
    Warum kann man Methoden nicht direkt im Struct definieren?

    • In Rust beeinflussen Struct-Felder Auto-Traits, daher ist es wichtig, die Felder auf einen Blick zu sehen.
      Außerdem können impl-Blöcke andere Generic-Constraints haben als das Struct selbst, weshalb man mehrere davon definieren kann.
      Und schließlich ist Rust als Sprache darauf ausgelegt, vom Shape der Daten aus zu denken.
    • Persönlich finde ich, dass sich impl-Blöcke ähnlich anfühlen wie Methoden in Go; ich würde nicht sagen, dass das eine klar besser ist als das andere.
  • Eine Sprache mit Python-ähnlicher Syntax, die aber nach Rust oder Go kompiliert, wäre wirklich großartig.

    • Mojo ist eine Hochleistungssprache mit Python-Syntax, entwickelt vom Schöpfer von Swift.
    • Spy ist ein früher Versuch, der nach C kompiliert; Nim gehört ebenfalls in eine ähnliche, schon reifere Kategorie.
    • Nim hat eine Python-ähnliche Syntax mit statischem Typsystem und kompiliert zu verschiedenen Targets wie wasm und C.
    • Static Python Skill ist ein Versuch, Python statisch zu kompilieren.
    • Grumpy war ein von Google gebauter Python→Go-Transpiler, aber seit 9 Jahren ohne Updates (für Python 2.7).
  • Lisette wirkt wie eine Sprache, die die Balance zwischen Gos Einfachheit und Rusts Komplexität gut trifft.
    Ich frage mich, ob es einen Grund gibt, warum die Kompilierung viel langsamer als in Go sein sollte, und welche Rust-Features bewusst weggelassen wurden.
    Zum Beispiel: Borrow Checking, Datentypen, async usw.

  • Go ist leicht zu lernen, aber funktional begrenzt.
    Lisette wirkt interessant, weil es genau diese Lücken füllt.
    So wie TypeScript JavaScript erweitert hat, könnte Go mit einem ausdrucksstarken Typsystem und einem strengen Compiler eine großartige Backend-Sprache werden.
    Mein persönlicher Vorschlag wäre Unterstützung für Type-Sharing mit TypeScript-Frontends. Das ist einer der Gründe, warum TypeScript auch im Backend beliebt ist.

  • Als Entwickler für Infrastruktur-Automatisierung, der zwischen Rust-Sicherheit und Go-Einfachheit abwägt, finde ich die Idee, Rust-Bedienbarkeit auf die Go-Runtime zu setzen, äußerst attraktiv.
    Ich werde die Weiterentwicklung des Projekts weiter beobachten.