Interaktivität ist bekanntlich eines der wesentlichen Merkmale von Programmierung in Lisp. Sicherlich ist das einer der Gründe dafür, daß sich das Entwickeln von GUI-Anwendungen in Lisp immer etwas unnatürlich anfühlt. Wenn man nicht gerade ein besonders lispiges Framework wie den Common Lisp Interface Manager oder ein smalltalkesques wie Morphic zur Verfügung hat — und im Falle von Clojure hat man zu meinem großen Bedauern keines von beiden — dann ist die einzige Möglichkeit, die noch bleibt, ein traditionelles GUI-Toolkit zu verwenden, das sich halbwegs interaktiv programmieren läßt.
Nun ist das mit der Interaktivität so eine Sache. Die meisten Programmiersprachen lassen sich in dieser Weise gar nicht nutzen, sondern setzen einen umständlichen Editieren-Kompilieren-Starten-Zyklus voraus. Das gilt insbesondere für die populären Sprachen mit C-artiger Syntax. So erstaunt es auch nicht, daß verbreitete Toolkits wie Gtk+ und Qt den Programmierer zwingen, Dinge zu tun, die in einem interaktiven Kontext völlig inakzeptabel wären. Eines davon ist folgendes Codemuster (hier beispielhaft für Qt-Jambi in Java, anderswo sieht es ähnlich aus):
gdk_threads_enter();
// GUI-Code hier.
gdk_threads_leave();
Code, der auf das Benutzerinterface zugreifen will, beispielsweise um es zu verändern, muß in einem Kontext stehen wie der Kommentar „GUI-Code hier“ oben im Codebeispiel. Der Grund dafür ist, daß die verbreiteten Toolkits stets nur einem einzelnen, bestimmten Thread den Zugriff auf das Interface erlauben.
Für interaktive Programmierung ist die Erzwingung dieses Codemusters katastrophal. Es ist nicht möglich, in der Clojure-REPL mal schnell einen Button zu erzeugen und ihn auf das bestehende Interface zu kleben, um auszuprobieren, wie das aussieht. Schließlich will wirklich niemand ernsthaft Dinge wie (try (threads-enter) (.show button) (finally (threads-leave)))
anstelle von (.show button)
eintippen wollen — jedenfalls definitiv nicht an einer Befehlszeile!
Genausowenig kann man schnell in einem geöffneten Editorfenster C-M-x drücken, um eine Label-Beschriftung zu aktualisieren, die man gerade angepaßt hat.
Vielleicht kann jemand, der interaktives Programmieren nicht gewohnt ist, nicht gleich verstehen, warum das alles ein riesiges Problem ist. Nichtsdestotrotz ist es eine Tatsache, daß sich in der Welt von Common Lisp trotz der Verfügbarkeit von Gtk+- und wxWidgets-Anbindungen immer noch Tk als Quasistandard für GUI-Programmierung hält. Ich bezweifle, daß es Zufall ist, daß gerade dasjenige Toolkit am meisten Zuspruch erhält, das das Problem des abgetrennten GUI-Threads nicht aufweist.
Lassen wir nun aber Tk einmal außen vor. Abgesehen davon, daß es, wie es scheint, keine akzeptable Java-Anbindung dafür gibt, mutet es doch zumindest unter X11-basierten Systemen etwas prähistorisch an. Was gibt es für moderne Clojure-Programmierer für Alternativen?
Da wäre freilich einmal JFC/Swing. Genügend Leute klagen über das barocke API. Doch: Entgegen jeder rationalen Erwartung, kennt Swing das Problem des GUI-Threads nicht! Es eignet sich somit ganz hervorragend zum interaktiven Zusammenbasteln von GUIs.
Nun, vielleicht nicht ganz hervorragend. Wie gesagt, ist das API barock und etwas unhandlich zum interaktiven Programmieren. Hinzu kommt, daß es keine realistische Möglichkeit gibt, zur Laufzeit ein mit einem GUI-Designer entworfenes Interface zu laden. Matisse zum Beispiel, der für Swing-Verhältnisse exzellente GUI-Designer in Netbeans, erzeugt Java-Code. Um den zur Laufzeit neu zu laden, müßte man erst die alte Instanz irgendwie aus dem Speicher kriegen, und dann— nein. Viel zu aufwendig.
Vielleicht ist Qt die Lösung? Der Qt Designer erzeugt XML-Beschreibungen des entworfenen Interfaces, die man zur Laufzeit laden kann. Doch — oh Schreck! — wie muß man GUI-Code bei Qt aufrufen?
QCoreApplication.invokeAndWait(new Runnable() {
public void run() {
// GUI-Code hier.
}
}
Nein, nein und nochmal nein! So wird das nichts mit der interaktiven Programmierung. Es sei denn, jemand würde SLIME hacken und alle interaktiven Befehle automatisch in diesen Boilerplate einpacken, bevor sie ausgeführt werden...
Gesagt, getan. Zwar schreibe ich im Moment keine GUI-Anwendungen, aber mit diesen kleinen SLIME-Hacks bewaffnet, werde ich beim nächsten Mal Qt in Erwägung ziehen, wenn ich Bedarf an Desktopgraphik habe.
Comments
Mhh, du könntest doch "einfach" eine Repl in den GUI-Thread einbauen, oder im Event-Dispatch-Loop bei jedem Durchlauf einen Liste/Queue (z.B. java.util.concurrent.LinkedBlockinQueue) von Funktionen abarbeiten. Mit (with-qt (show my-button)) wird dann die (show my-button) Funktion auf den Queue gepackt und beim nächsten Loopdurchlauf ausgeführt. So habe ich auch die JBox2D Physikengine von der Clojure-REPL aus benutzbar gemacht (Jbox2d ist von Haus aus nicht threadsafe).
Mit Clojure programmiere ich grad GUIs mit dem Apache Pivot Toolkit, und es ist recht interaktiv, zur Laufzeit lässt sich fast alles gestalten und die API ist, wenn auch nicht funktional, so doch sehr aufgeräumt. Sämtliche Listener lassen sich zum Beispiel durch ein Makro generieren.
Natürlich ist es, da es auf Java2D aufsetzt langsamer/ressourcenhungriger im vergleich zu Qt.
Hallo!
Genau: So etwas wie ein with-qt-Makro geht natürlich immer. Nur: Einerseits ist es etwas nervig, Befehlseingaben immer in eine with-qt-Form einpacken zu müssen (die Sache mit der GUI-REPL würde hiergegen wohl helfen), und andererseits ist interaktives Programmieren mit SLIME viel mehr als nur die REPL.
Zum Beispiel: Man schreibt eine Funktionsdefinition oder ändert eine bestehende, drückt C-M-x oder C-c C-c, und schon ist die neue Definition geladen. Dasselbe geht auch für die Definition einer globalen Variablen oder beliebiger anderer Codestücke.
Wenn man das alles jetzt extra für die interaktive Nutzung in ein with-qt-Makro einbetten muß, dann stört das unter Umständen noch mehr als direkt an der REPL. Mein SLIME-Hack nimmt einem genau das ab.
Das Pivot-Toolkit war mir noch gar nicht bekannt. Es sieht in der Tat nach einer sehr interessanten Alternative aus. Web-nahe Ansätze eignen sich meist für interaktive Entwicklung deutlich besser als Desktop-Toolkit-basierte. Um so besser, wenn man sie auf dem Desktop auch einsetzen kann. Danke für den Tip! :)
Very nicce!
Submit a comment
Note: This website uses a JavaScript-based spam prevention system. Please enable JavaScript in your browser to post comments. Comment format is plain text. Use blank lines to separate paragraphs.