Stattdessen müssen wir unsere (in diesem Beispiel sicherlich primitive) Anwendungslogik zerreißen und dem zustandslosen HTTP-Protokoll und ereignisgesteuerten Ablauf des Browsers unterwerfen:
- zeige ein Eingabeformular mit dem Prompt "Erste Zahl" an. - reagiere auf die Eingabe der ersten Zahl und zeige jetzt ein Eingabeformular mit dem Prompt "Zweite Zahl" an, merke dir dabei die erste Zahl, z.B. als versteckte Eingabe in dem zweiten Formular - reagiere auf die Eingabe der zweiten Zahl und zeige jetzt eine Seite mit dem Text "Ergebnis" und dem Ergebnis der Addition der zweiten Zahl mit der ebenfalls versteckt übertragenen ersten Zahl an.
Wir müssen die Formulare also jeweils so gestalten, dass sie den zukünftigen Programmablauf mit berücksichtigen. Diese Zukunft eines Programms bezeichnet man als continuation und Sprachen wie z.B. Scheme kennen sie als direktes Sprachkonzept.
Könnten wir continuations in Java benutzen, könnten wir vielleicht obiges Programm direkt implementieren. Dies (wenigstens zu simulieren) soll das Ziel dieses Teils des Tutorials sein.
Java hat keine Continuations, auch keine Coroutinen (die kleinen Schwestern von Continuations), aber Threads, die näherungsweise benutzt werden können, indem wir sie explizit anhalten und später weiterlaufen lassen.
Auf die URL /start hin wollen wir eine neue Continuation erzeugen, die dann das obiges Programm ablaufen lässt. Die Continuation soll unter einer einzigartigen ID abgelegt werden, damit wir sie bei jeder weiteren Anfrage, die mit /continue eingeleitet werden soll, wiederfinden.
public class ContinuationWeblet extends Weblet { private final Map<String, Continuation> continuations = new HashMap<String, Continuation>();
public boolean matches(WebRequest request) { return request.urlStartsWith("/start") || request.urlStartsWith("/continue"); }
public void handle(WebRequest request, WebResponse response) throws IOException { if (request.getUrl().startsWith("/start")) { newContinuation().start(request, response); } else if (request.getUrl().startsWith("/continue")) { getContinuation(request).resume(request, response); } else { response.sendError("404 Not found", null); } }
private Continuation newContinuation() { synchronized (continuations) { String id; do { id = newId(); } while (continuations.containsKey(id)); Continuation cont = new Continuation(id); continuations.put(id, cont); return cont; } }
private String newId() { ... } ... }
Als ID nehmen wir am besten eine ausreichend lange Folge zufälliger Zeichen. Die konkrete Implementation sei dem Leser überlassen.
Dies ist der Aufbau einer Continuation:
public class Continuation extends Thread { private final String id; private WebRequest request; private WebResponse response;
Ich bin mir nicht sicher, ob eine Thread-Unterklasse die beste Idee ist, aber irgendwie muss ich den Thread schließlich starten, laufenlassen und dabei unterbrechen können. Dazu später mehr. WebRequest und WebResponse werden jeweils übergeben und in Exemplarvariablen gespeichert, damit sie in der run-Methode zur Verfügung stehen. (Seufz: Alles muss man in Java selbst machen - auch closures...)
Zunächst möchte ich den Code zeigen, der eine Continuation wiederfindet:
public class ContinuationWeblet... ... private Continuation getContinuation(WebRequest request) { Continuation cont; synchronized(continuations) { cont = continuations.get(request.getParameter("id")); } if (cont == null) { cont = NullContinuation; } return cont; }
private static final Continuation NullContinuation = new Continuation() { public void resume(WebRequest request, WebResponse response) { init(request, response); merge(ERROR_TEMPLATE); }
};
private static final String ERROR_TEMPLATE = ... }
Wir benutzen hier das Null-Object-Pattern, das statt null ein spezielles Objekt benutzt, um so eine Fallunterscheidung oder NullPointerException in der eigentlichen Methode zu vermeiden und die Lesbarkeit zu erhöhen.
Der Fehlertext sagt sowas wie "hey, diese continuation gibt es nicht, klicke auf /start um neu zu beginnen".
Wie man sieht, habe ich die merge-Methode in die Continuation-Klasse verschoben. Wahrscheinlich sollte auch der Fehlertext dort stehen.
Die eigentliche Magie geschieht in der Continuation-Klasse, dort wo ich die "..." eingefügt habe. Beginnen wir mit der run()-Methode:
class Continuation... public void run() { webPrint("Ergebnis", webRead("Erste Zahl") + webRead("Zweite Zahl")); }
Die Methode yieldControl() hält den Thread (und damit die Ausführung der Continuation) an und lässt die zwischenzeitlich wartende start() bzw. resume()-Methode weiterlaufen, auf dass die erstellte WebResponse abgeschickt werden kann:
public class Continuation... ... private final Condition yield = new Condition(); private final Condition wait = new Condition(); ... public void start... ... start(); yield.await(); }
public void resume... ... wait.signal(); yield.await(); }
Was ist eine Condition? Ein Synchronisationspunkt zwischen zwei Threads. Wahrscheinlich gibt es da auch eine fertige Klasse im JDK 1.5, aber so war es für mich einfacher:
static class Condition { private boolean b;
synchronized void signal() { if (!b) { b = true; notify(); } }
Die Methode signal() setzt ein Flag. Mit await() kann ich in einem anderen Thread auf dieses Flag warten. Danach wird das flag zurückgesetzt, damit die Condition ein zweites Mal benutzt werden kann.
Schauen wir uns mit diesem Wissen nochmal die Continuation-Methoden an:
- Continuation#start() wird aus dem Thread des Handlers aufgerufen und startet Continuation#run() als Continuation-Thread. Dieser läuft sofort oder später los, wichtig ist nur, dass der Handler-Thread auf ein "yield"-Signal wartet. Das wird die Continuation auslösen, wenn die WebResponse fertig ist und vorerst unterbrochen werden soll. - Continuation#run() läuft parallel zum wartenden Handler-Thread los und führt das erste webRead() aus, welches yieldControl() aufruft. - Continuation#yieldControl() sagt dem wartenden Handler-Thread, dass er weitermachen kann und wartet selbst auf ein Signal, dass ein neuer Handler-Thread schicken wird, wenn /continue aufgerufen wird. - Während der Continuation-Thread also auf "wait" (besser wäre ja continue, aber das ist ein Schlüsselwort in Java) wartet, wird Continuation#resume() aus einem Handler-Thread aufgerufen, was dem Continuation-Thread bescheid gibt, dass es weiter gehen kann (mit neu untergeschobenem WebRequest und neuem WebResponse) und wartet genau wie start() darauf, dass die Continuation wieder die Kontrolle abgibt.
Wird das Ende von run() erreicht, stoppt leider die Continuation nicht, sie hängt ewig - Künstlerpech.
Ich rekapituliere nochmal: Wenn wir im Browser auf /start zugreifen, wird eine neue Continuation erzeugt und gestartet, während der Handler-Prozess wartet. Die Continuation wird stückweise ausgeführt, jedes Mal neu getriggert durch einen Request mit der URL /continue. Verschiedene URLs sind unnötig. Die Continuation "weiss", wo es weiter geht.
Neben dem offensichtlichen Problem, welches sich dieses Rahmenwerk mit vielen anderen einfachen Web-Rahmenwerken teilt - der Back-Button ist tötlich für den Ablauf - hat das Rahmenwerk somit auch ein Problem mit Reloads.
Doch gehen wir trotzdem noch einen Schritt weiter.
Eine WebComponent sei ein Objekt, das sich selbst auf einer WebPage darstellen kann:
public abstract class WebComponent { public abstract void renderOn(WebPage page); }
public class WebPage { ... public void render(String title, WebComponent component) { print(
> Stefan Matthias Aust <nob...@3plus4.de> writes: >> Oder anders gefragt, warum kann der Code nicht so aussehen? >> webPrint("Ergebnis", >> webRead("Erste Zahl") + webRead("Zweite Zahl"));
> Ich kenne mich mit Web-Rahmen nicht aus.
Sowas hält unsereins doch nicht ab, oder? :)
> Deswegen mußte ich mir jetzt schnell etwas ausdenken. > Also hier mein primitiver Rahmen. [...]
Du baust, wenn ich deinen Kode richtig entziffere, einen Zustandsautomaten (ich deutsche das für dich extra mal ein) und implementierst damit das, was die Continuation ausmacht, explizit selbst. Genau das soll ja nicht passieren, d.h.
soll unverändert so bleiben und gesucht ist einzig eine geschickte Implementierung für webPrint und webRead.
> Mit continuations kenne ich mich auch nicht aus, aber diese > entsprechen vermutlich dem, was bei mir in den > Maschinenvariablen Machine#env und Machine#pc steht.
Das ist korrekt. Eine weitere wichtige Eigenschaft ist, dass du mehrfach die Ausführung einer Maschine an der selben Stelle fortsetzen kannst, aber ich denke, das funktioniert mit deiner Implementierung.
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
Spannender ist die Funktion yieldControl. Sie erzeugt und speichert eine neue Continuation, die dann später beim nächsten Request fortgesetzt wird. Danach beendet sich die Funktion, indem eine zuvor gespeicherte exit-Continutation fortgesetzt wird. Wie req und res speichere ich beide Continuations in globalen Variablen:
var exit var cont function yieldControl() { cont = new Continuation() exit() }
Hier wird die exit-Continuation angelegt:
function doit() { exit = new Continuation() if (cont != null) { cont() } else { run() } }
Damit ich das jetzt aus Java heraus aufrufen kann, braucht es noch ein kleines Servlet (ich werde nicht müde, hinzuweisen, dass die Java-Seite tatsächlich mehr Zeilen hat als der JavaScript-Code):
class ContinuationServlet extends HttpServlet { private ScriptEngine engine; private Map<String, Object> continuations; private int nextId;
void init(...) { engine = new ScriptManager().getEngineByName("JavaScript"); engine.eval(/* hier den obigen JavaScript-Code lesen */); continuations = new HashMap<String, Object>(); }
void doGet(....) { String id = /* id from URL extrahieren */
Wenn man HttpSessions benutzen würde, müsste das noch einfacher gehen, ich fand aber bei meinem Test auf die Schnelle nicht, wie ich das in Jetty, das ich direkt ohne web.xml starte, realisiere.
Ich bin mir nicht sicher, ob die Engine wohl thread-safe ist. Außerdem räume ich meine Continuations nie auf, man will wahrscheinlich nicht alle für eine unbegrenzte Reise in die Vergangenheit aufbewahren, sondern nur die letzten 10 oder nur für X Minuten oder so.
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
> Angenommen, wir wollen eine Webanwendung schreiben, die nacheinander > zwei Zahlen einliest und die Summe anzeigt. Wie würde der Code dazu > aussehen?
> Oder anders gefragt, warum kann der Code nicht so aussehen?
Weil man dafür Threads braucht, und die sind ja teuer und das skaliert nicht und es fördert einen prozeduralen Stil anstatt einem objektorientierten Vorgehen und überhaupt geht das ja auch mit einer State Machine.
Zumindest sind das die Argumente die auf meinen Vorschlag in der Wicket-user Mailingliste kamen. Ich schätze man wollte sich Arbeit vom Hals halten, was ich gut nachvollziehen kann :)
Deine Ausführungen fand ich sehr gut. Ich hoffe ich habe tatsächlich mal die Zeit eine Continuations-Implementation für Wicket zu machen. Der grösste Aufwand wird wohl sein das vernünftig und ohne Änderungen am Kern umzusetzen, nicht die Continuations-Engine selbst. Trotzdem ist dein Artikel da schon mal eine grosse Hilfe.
Ich habe das kurz überflogen und muss leider sagen, in der Diskussion weiss keiner, worum es eigentlich bei IMHO wirklich Continuations geht. Das Argument mit den Threads trifft nur auf Java zu, was keine continuations oder co-routinen kennt. Sprachen wie Erlang oder Io (allgemein alles, was die Idee von Actor, nämliche aktive Objekte, aufgreift), die auf viele Threads oder Prozesse optimiert sind, haben keine Probleme mit 10.000 oder 100.000 (quasi-)parallelen Prozessen.
> PS: Ich fand es sehr interessant zu sehen dass du jetzt doch HTML in > Java Strings schreibst ;)
Ist halt am einfachsten hier für meine Beispiele :)
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
> r...@zedat.fu-berlin.de (Stefan Ram) writes: >> GET server?machine=AB65E5C0-CE48-11DA-A6BC-00A0CC554CB0&value=2
> Interessant wird es dann, wenn man versucht auch noch »das > Richtige« zu tun, wenn der Benutzer mit der »Zurück«-Taste > seines Klienten zurückgeht und ein altes Formular noch einmal > abschickt oder so etwas. Will er mit »Zurück« zur Bestellseite > alle bisherigen Bestellungen ungeschehen machen (»undo«) oder > nur noch eine weiter Bestellung hinzufügen?
Das ist in der Tat ein Problem, weil continuations "zu gut" sind.
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
> Braucht man nicht ... siehe mein JavaScript-Beispiel.
> > und die sind ja teuer und das skaliert >> nicht und es fördert einen prozeduralen Stil anstatt einem >> objektorientierten Vorgehen
> Sehe ich nicht so. Es fördert im Gegenteil funktionale Programmierung > und hält Code zusammen, der zusammen gehört. > > und überhaupt geht das ja auch mit einer State Machine.
> Nur viel umständlicher. Vergleiche Stefan Rams Beispiel...
Ganz meine Meinung. Die Gegenargumente die ich aufgezählt habe stammen nicht von mir, sondern von den Wicket-committern.
> Ich habe das kurz überflogen und muss leider sagen, in der Diskussion > weiss keiner, worum es eigentlich bei IMHO wirklich Continuations geht.
Das kommt mir auch so vor. Aber ich kann die Leute ja nicht zur Erkenntnis zwingen, das führt nur zu flame wars. Muss ich halt selber schreiben.
> Das Argument mit den Threads trifft nur auf Java zu, was keine > continuations oder co-routinen kennt. Sprachen wie Erlang oder Io > (allgemein alles, was die Idee von Actor, nämliche aktive Objekte, > aufgreift), die auf viele Threads oder Prozesse optimiert sind, haben > keine Probleme mit 10.000 oder 100.000 (quasi-)parallelen Prozessen.
Ich will aber Continuations in Wicket, also Java.
Aber selbst wenn Continuations nur mit Threads möglich sind ist mir das egal, denn bei einer Anwendung mit wenigen dutzend Usern ist der Nutzen bei komplexen Abläufen mit viel Interaktion hoch.
> Es ist natürlich verdienstvoll, daß Du es überhaupt > versucht hast, aber Du sahst ja auch, daß Java Dir diese > Implementation nicht gerade einfach gemacht hat.
In der Tat.
> Da fragte ich mich, ob solch ein expliziter Interpretierer > dann nicht das »kleinere Übel« sei. Seine einzelnen Operationen > können dann schließlich wiederum in Java geschrieben werden, > also Gebrauch von allen Möglichkeiten der Sprache Java machen.
Das würde ich sagen. Allerdings nicht unbedingt in der Form, die du präsentiert hast, sondern z.B. in Form eines JavaScript-Interpreters.
> Zudem können die Programm für die simulierte Maschine auch zur > Laufzeit manipuliert werden, während Java-Code ja erst einmal > statisch ist.
Das dynamische manipulieren empfände ich als Vorteil, den mein Ziel ist eigentlich die Entwicklungsumgebung im Web, wie sie Seaside bietet.
Mein nächster Versuch ist daher, einen Interpreter zu bauen, der Continuations kann, weil ich das glaube ich immer noch nicht 100%ig verstanden habe :(
> Aber selbst wenn Continuations nur mit Threads möglich sind ist mir das > egal, denn bei einer Anwendung mit wenigen dutzend Usern ist der Nutzen > bei komplexen Abläufen mit viel Interaktion hoch.
Leider ist es IMHO noch nicht einmal mit Threads möglich, weil du Threads klonen können müstest, um den Back-Button richtig zu unterstützen. Es muss möglich sein, eine Continuation mehr als einmal fortzusetzen.
Wenn du partu bei Java bleiben willst, wäre vielleicht - wenn es dann zumindest eine häßliche Lösung gäbe - ein spzieller post prozessor in Form eines ClassLoaders ein Weg, den Code automatisch zu tranformieren.
Ich meine RIFE geht so vor, allerdings meine ich ebenfalls (ohne das genauer angeschaut zu haben), dass sie dort keine echten Continuations haben.
Wenn man wenigstens in Java resume/retry-bare Exceptions hätte...
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
Stefan Matthias Aust wrote: > Das dynamische manipulieren empfände ich als Vorteil, den mein Ziel ist > eigentlich die Entwicklungsumgebung im Web, wie sie Seaside bietet.
Ich habe mir eben mal das Video auf http://seaside.st/ angesehen (habe zwar nichts vom Portugiesisch verstanden, aber die Bildchen waren ausreichend) und ich sehe nicht, warum es ein Vorteil ist, den Klassenbrowser als HTML zu haben und dort ohne Syntax-Highlighting o.ä. Code eintippen zu können. Das kann doch das Smalltalk-System selbst viel besser?
Das Framework selbst sieht so auf den ersten Blick recht gut programmierbar aus, ähnlich wie UCW (muß ich wirklich mal ausprobieren), mit Continuations. Scheint auch recht einfach zu sein, zu Aktionen eigene Webseiten zu entwerfen.
>> Aber selbst wenn Continuations nur mit Threads möglich sind ist mir >> das egal, denn bei einer Anwendung mit wenigen dutzend Usern ist der >> Nutzen bei komplexen Abläufen mit viel Interaktion hoch.
> Leider ist es IMHO noch nicht einmal mit Threads möglich, weil du > Threads klonen können müstest, um den Back-Button richtig zu > unterstützen. Es muss möglich sein, eine Continuation mehr als einmal > fortzusetzen.
Mal ganz blöde gefragt: Würde es nicht reichen sich einfach jeden Zustand zu merken? Also bei HTTP jeden Request zwischen zu speichern und dann bei allen Aktionen eine eindeutige ID vorranzustellen?
Du willst bei "diesen Continuation" -- hab den Begriff eigentlich immer nur hier in der NG von dir gelesen, vermutlich fehlt mir da eine schlüssige Definition -- also einfach nur zwischen Zwei Funktionen wie "webRead() + webRead()" eine weitere Response haben, oder? *selber noch ein wenig drüber Nachdenk*
> Mal ganz blöde gefragt: Würde es nicht reichen sich einfach jeden > Zustand zu merken?
Das funktioniert IMHO in Java nicht, weil ich mir nicht den Zustand des Systemstacks merken kann, was ich müsste, um das Programm genau an dieser Stelle (mehrfach) fortsetzen zu können.
Der Trick bei diesem simplen Beispiel mit dem "+" ist ja, dass das Programm quasi in der "+"-Operation anhält, und dort mehrfach mit verschiedenen zweiten Operanden - wenn man im Browser nach dem Ergebnis immer wieder den Back-Buttond drückt - addiert wird.
> Du willst bei "diesen Continuation" -- hab den Begriff eigentlich immer > nur hier in der NG von dir gelesen, vermutlich fehlt mir da eine > schlüssige Definition
Definition: Eine Continuation ist eine (virtuelle) Funktion, die den Rest der Ausführung eines Programms repräsentiert. Sie repräsentiert damit einen Momentaufnahme der aktuellen Ausführungsumgebung, häufig eines Stacks.
Mir hat der Artikel "Teach yourself Scheme in Fixnum Days" und dort der Abschnitt über "Amb" geholfen, Continuations zu verstehen.
> -- also einfach nur zwischen Zwei Funktionen wie > "webRead() + webRead()" eine weitere Response haben, oder?
Ja.
PLT-Scheme benutzt da vielleicht die besseren Namen. Dort gibt es eine "send/suspend"-Funktion (der Name enthält ein "/", da ist nichts besonderes dabei), die eine Funktion übergeben bekomt, die wiederum eine continue-URL übergeben bekommt und eine web-response erzeugt und wo send/suspend dann selbst einen web-request liefert. Per Konvention startet eine PLT-Scheme-Webanwendung mit einer Funktion "start", die den initialen Request bekommt und eine web-response liefert soll.
Leider ist es auch in Scala nicht möglich, sendAndSuspend zu implementieren...
Was man machen müsste, wäre einen Snapshot vom aktuellen Ausführungsstack und Zeiger auf die nächste auszuführende Anweisung derart anzulegen, sodass man später das Programm ab diesem Zeitpunkt weiter laufen lassen kann.
Dann muss man das Programm hier abbrechen, indem man einen zuvor gesetzten Snapshot fortsetzt, der die erstellte Response jetzt an den Browser übergibt und dann die Kontrolle bis zum nächsten Request komplett abgibt.
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
> Ich habe mir eben mal das Video auf http://seaside.st/ angesehen (habe zwar > nichts vom Portugiesisch verstanden, aber die Bildchen waren ausreichend) > und ich sehe nicht, warum es ein Vorteil ist, den Klassenbrowser als HTML > zu haben und dort ohne Syntax-Highlighting o.ä. Code eintippen zu können.
Syntaxhighlighting hast du sonst auch nicht... Ein Vorteil ist, dass du das System remote installieren und administrieren kannst. Die Werkzeuge könnte man IMHO noch deutlich verbessern... sogar remote.
> Das kann doch das Smalltalk-System selbst viel besser?
Ein bisschen, aber dort hat man sich Jahre lang auf dem Stand der späten 80er Jahre ausgeruht und inzwischen bieten Eclipse und Co ähnliche oder bessere Möglichkeiten.
Entscheidend ist ... grummel, jetzt scheint die brasiliansche Site down zu sein... jedoch, dass man während der Entwicklung direkt im Browser Zugriff auf all diese netten Werkzeuge hat, angefangen von der Möglichkeit, mal die Komponenten und das HTML selbst zu sehen, über den Profiler bis hin zum Inspector für das laufende Programm.
> Das Framework selbst sieht so auf den ersten Blick recht gut programmierbar > aus, ähnlich wie UCW (muß ich wirklich mal ausprobieren), mit > Continuations. Scheint auch recht einfach zu sein, zu Aktionen eigene > Webseiten zu entwerfen.
Ja, die benutzen das send/suspend/dispatch-Pattern, was auch PLT-Scheme benutzt. Ich weiss nicht, wer zuerst war. 2002 gibt es Paper zum "Continue-Server" und 2003, wie man ihn mit obigem Pattern neu gebaut hat. Ich glaube, Seaside ist 2002 entstanden. Da gibt es jedenfalls einen Talk auf einer Ruby-Konferenz zu Seaside.
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
Stefan Matthias Aust wrote: > Entscheidend ist ... grummel, jetzt scheint die brasiliansche Site down > zu sein... jedoch, dass man während der Entwicklung direkt im Browser > Zugriff auf all diese netten Werkzeuge hat, angefangen von der > Möglichkeit, mal die Komponenten und das HTML selbst zu sehen, über den > Profiler bis hin zum Inspector für das laufende Programm.
Vielleicht hast du Recht. Eine typische Lisp-Lösung sähe z.B. so aus: Auf dem Server läuft ein Lisp-Prozess, der einen Webserver beinhaltet und eine REPL-Kommandozeile, die z.B. per http://www.cliki.net/detachtty gestartet wurde und auf die man per SSH mit http://www.cliki.net/SLIME von seinem lokalen Emacs drauf zugreift. Emacs habe ich selbst noch nicht verwendet, aber man könnte dort auch die Tastaturbelegung so umdefinieren, daß die mit "normalen" Windows Menschen verträglich ist. Man kann dann jeweils Quelltext öffnen, editieren und per SLIME ins laufende System kompilieren und debuggen. Soweit ist das ungefähr gleich, wie wenn man in Squeak editiert und es im Browser anzeigt.
Ich sehe aber den Nachteil des Medienbruchs, wenn man es nur in der IDE editieren könnte: Squeak (und Emacs) bieten eine andere Sichtweise auf das System, als man es im Browser sieht, also z.B. muß man bei einer Webseite erst den dazugehörigen Quelltext suchen, wobei bei Seaside das mit einem Klick auf den Quelltextbutton der gerade dargestellten Seite möglich ist (denke ich). Ist also in der praktischen Anwendung wahrscheinlich gar nicht so schlecht.
Fazit: man baut Zope nach, also eine Webanwendung, mit der man Webanwendungen bauen kann :-)
In Java sähe das dann z.B. so aus, daß man z.B. einen JavaScript-Interpreter einsetzt und per Webinterface die Scripts und dazugehörigen Templates anlegen und ändern kann. Ideal wäre dann noch ein Editor als Browser-Plugin, der eine Verbindung zum Java-Server aufnimmt und beim editieren von Quelltext z.B. auto-completion bereitstellt. Oder ganz visionär: Man baut in Eclipse einen Webeditor, der in integrierten Textfeldern die Editor-Funktionen von Eclipse bereitstellt. Da bekäme man dann viel von der Projektverwaltung usw. für die Webanwendung zum webanwendungsbauen geschenkt.
> Vielleicht hast du Recht. Eine typische Lisp-Lösung sähe z.B. so aus: Auf > dem Server läuft ein Lisp-Prozess, der einen Webserver beinhaltet und eine > REPL-Kommandozeile, die z.B. per http://www.cliki.net/detachtty gestartet > wurde und auf die man per SSH mit http://www.cliki.net/SLIME von seinem > lokalen Emacs drauf zugreift.
Tja, in PHP oder auch (Ruby on) Rails sähe es so aus, dass man einfach den Quelltext ändert und das System bei jedem Request ja komplett und immer alles neu lädt und quasi neu startet und dadurch ebenfalls die Illusion der direkten Änderbarkeit bietet. Nervig dabei ist aber, dass man nicht direkt eine Methode debuggen und ändern kann *während* die Anwendung läuft. PHP und Rails gleichen das dadurch aus, dass wirklich alles, was persistent sein soll, in die Datenbank wandert. Ist aber IMHO unschön.
> Ich sehe aber den Nachteil des Medienbruchs, wenn man es nur in der IDE > editieren könnte: Squeak (und Emacs) bieten eine andere Sichtweise auf das > System, als man es im Browser sieht
Muss nicht so sein.
Ich würde mich jetzt sogar hinstellen und behaupten, du kannst einen Webmacs in JavaScript bauen, jedenfalls eine Näherung, sodass der Emacs-Anfänger keinen großen Unterschied sieht. Scheme-Interpreter in JavaScript gibt es jedenfalls :)
Nein, die Herausforderung wäre schnelles Syntaxhighlighting. Das normale <textarea> ist ja ungefähr so komfortabel wie notepad.
Man müsste also stattdessen das benutzen, was Mozilla Midas nennt und Microsoft mit dem contenteditable-Attribut realisiert. Dann mit einem Listener jede Änderung des Dokuments erfahren, mit regulären Ausdrücken Regionen finden, dann dort span-Elemente mit der entsprechenden CSS-Klasse einfügen und alte span-Elemente entfernen. Und das alles mit umständlichen DOM-Befehlen.
Das scheint aber tatsächlich zu gehen (falls die das so machen): http://demo.aboutedit.com. Wow, ich bin beeindruckt.
> Fazit: man baut Zope nach, also eine Webanwendung, mit der man > Webanwendungen bauen kann :-)
Jein. Zope ist ja eher ein CMS. Man baut eigentlich eine IDE.
> In Java sähe das dann z.B. so aus, daß man z.B. einen > JavaScript-Interpreter einsetzt und per Webinterface die Scripts und > dazugehörigen Templates anlegen und ändern kann.
Z.B. Helma macht das so - Helma ist quasi sowas wie Zope für Java. Nur hat Helma AFAIK keine eingebaute IDE, diese Idee hatten die Autoren nicht oder sie war ihnen nicht so wichtig.
> Ideal wäre dann noch ein
> Editor als Browser-Plugin, der eine Verbindung zum Java-Server aufnimmt und > beim editieren von Quelltext z.B. auto-completion bereitstellt.
Ich bin überzeugt, sowas kann man bauen.
> visionär: Man baut in Eclipse einen Webeditor, der in integrierten > Textfeldern die Editor-Funktionen von Eclipse bereitstellt. Da bekäme man > dann viel von der Projektverwaltung usw. für die Webanwendung zum > webanwendungsbauen geschenkt.
Ah, du meist wie bei Seaside, was ja auch nur eine Hülle um die "echten" Entwicklungstools darstellt und diese nicht nochmal neu baut? Durchaus nett... aber vielleicht doch recht aufwendig :)
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
Stefan Matthias Aust wrote: > Ah, du meist wie bei Seaside, was ja auch nur eine Hülle um die "echten" > Entwicklungstools darstellt und diese nicht nochmal neu baut? Durchaus > nett... aber vielleicht doch recht aufwendig :)
ja, so ähnlich: Statt die IDE in HTML nachzubauen dachte ich daran, HTML in der IDE nachzubauen: Also daß Eclipse die Webanwendung ausführt und die HTML-Seiten anzeigen kann, deren Templates und zugehörige Aktionen verwaltet und bei Bedarf direkt einen Quelltexteditor zum passenden Kontext aufruft. Das dürfte eigentlich nicht so aufwendig sein und u.U. sogar einfacher, als der umgekehrte Weg mit einem reinen HTML-Browser.
> Statt die IDE in HTML nachzubauen dachte ich daran, HTML in > der IDE nachzubauen: Also daß Eclipse die Webanwendung ausführt und die > HTML-Seiten anzeigen kann, deren Templates und zugehörige Aktionen > verwaltet und bei Bedarf direkt einen Quelltexteditor zum passenden Kontext > aufruft. Das dürfte eigentlich nicht so aufwendig sein und u.U. sogar > einfacher, als der umgekehrte Weg mit einem reinen HTML-Browser.
Wenn ich dich richtig verstehe ist das eine ziemlich ähnliche Idee wie die von Wolfgang Schmidetzki, Eclipse als CMS Autorenoberfläche zu benutzen:
> ja, so ähnlich: Statt die IDE in HTML nachzubauen dachte ich daran, HTML in > der IDE nachzubauen: Also daß Eclipse die Webanwendung ausführt und die > HTML-Seiten anzeigen kann, deren Templates und zugehörige Aktionen > verwaltet und bei Bedarf direkt einen Quelltexteditor zum passenden Kontext > aufruft.
Verstehe. Aber läuft das nicht entweder auf einen eingebetteten IE (mit dem Nachteil, das man nicht auf einem richtigen Browser wie Firefox oder Safari entwickeln kann) oder gar den Nachbau eines Webbrowsers hinaus?
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
> Nach dem, was ich gestern über die Fortsetzungen bei den > Aktoren von Hewitt gelesen habe, ist es zunächst einmal eine > Art von Funktion. Dabei wird jedem Aktor eine Fortsetzung > übergeben, an die er seine Ergebnisse schicken kann oder soll.
So verstehe ich das auch.
> Die Aktoren kehren aber -- im Gegensatz zu Methoden nie zum > Aufrufer zurück.
Das ist jetzt Actor-spezifisch aber nicht typisch für continuations im Sinne von Scheme. Dort bezeichnet eine continuation eben die Zukunft eines Programms und kann - muss aber nicht - aufgerufen werden, um den aktuellen Kontext durch obige eingefrorene Zukunft ersetzt werden - wenn ich das mal so informell ausdrücken darf.
> Ich brauchte einen Moment, bis ich die letzte
> Zeile im folgenden Aktor zur Berechnung der Fakultät in einer > an Scheme angelehnten Notation verstand:
> ( define factorial > ( lambda( n c ) > ( if( = n 0 ) > ( c 1 ) > ( factorial( - n 1 )( lambda( f )( c( * f n )))))))
Vielleicht wird's einfacher, wenn du die Leerzeichen so setzen würdest, wie es bei Scheme üblich ist - aber was rede ich, das machst du bei Java ja auch nicht... :)
Zu obigem Beispiel zu bemerken ist übrigens noch, dass die Funktion endrekursiv ist, was die "normale" Fakultätsfunktion nicht ist. Scheme - das Endrekursionen erkennen und optimieren muss - würde obigen Code mit konstantem Speicherbedarf auswerten.
Eine call/cc-Variante der Fakultätsfunktion wäre:
(define (fac/cc n) (let* ((r 1) (k (call/cc (lambda (k) k)))) (set! r (* r n)) (set! n (- n 1)) (if (= n 0) r (k k))))
> In Java hätten wir nun ja fast Fortsetzungen, wenn Java eine > spezielle Endrekursionsimplementation böte.
Man hat sie sogar auch ohne, aber es ist sehr ineffizient, weil sich ein Callstack aufbaut, der niemals wieder abgebaut wird.
> Allerdings könnte man vielleicht etwas tricksen, indem man im > tiefsten Keller, wenn alle Ziele, die mit einem Pfad von > Fortsetzungen erreicht werden sollten, auch erreicht wurden, > wirft, um den Keller auf einen Schlag leerzuräumen.
Ich glaube - ohne es wirklich durchdacht zu haben - das man sich dadurch die IMHO wichtige Eigenschaft, eine continuations mehrfach zu durchlaufen verbaut. Exceptions sind ja auch nur eine Spezialform von continuations, aber eben welche, die nur einmal funktionieren.
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
> Das wird aber doch nicht möglich sein, denn zu einer > Fortsetzung gehören doch noch mehr Informationen als sie > in einem Throwable aufbewahrt werden.
Nein, im Gegensatz zu Smalltalk, wo diese Kontextinformationen wirklich als Objekte zur Verfügung stehen und auch meist manipuliert werden können (was Seaside überhaupt ermöglicht hat, continuations zu implementieren) ist es in Java nur ein informativ und nicht mehr an die VM gebunden.
Das hat aber auch gute Gründe, denn man verbaut sich ansonsten einiges an Optimierungen. Da es in Smalltalk sehr selten ist, dass man wirklich explizit auf den Context zugreift, so erzählte mir der Autor der VisualWorks-VM mal, haben die dort quasi zwei Betriebsmodi. Den normalen, wo optimiert wird und die Context-Objekte eigentlich gar nicht existieren und den, der es so macht, wie Smalltalk-80 spezifiziert ist und Änderungen am Kontext erlaubt.
-- Stefan Matthias Aust // Ergo bibamus, ne sitiamus, vas repleamus!
Stefan Matthias Aust wrote: > Verstehe. Aber läuft das nicht entweder auf einen eingebetteten IE (mit > dem Nachteil, das man nicht auf einem richtigen Browser wie Firefox oder > Safari entwickeln kann) oder gar den Nachbau eines Webbrowsers hinaus?
Der Webbrowser könnte als ActiveX Komponente angezeigt und die Links von der Entwicklungsumgebung abgefangen und ggf. direkt zur Anzeige von Quelltext im Quelltexteditor umgeletiet werden. Das könnte ein schönes integriertes System werden.
Unter Mac und Linux könnte man den Java-HTML-Renderer verwenden (gab's doch irgendein Swing-Control für?). Bis das Projekt fertig ist, hat Sun das vielleicht soweit hinbekommen, daß aktuelle Webseiten vernünftig dargestellt werden :-)
> So etwas (auf dem Klienten) ist beispielsweise mit »narrative > JavaScript« möglich.
Warum gräbst du alte Threads aus, um denen durchaus interessante Links hinzuzufügen, wenn du doch gleichzeitigt alle deine Postings nicht archivieren lässt sodass niemand wirklich davon etwas hat?