Grad scheint Microsoft wieder mal “the best thing since sliced bread” erfunden, wie es scheint. Der Reactive Framework (Rx) zaubert jedenfalls einiges breites Grinsen auf Gesichter bei Microsoft, hier der Erfinder des Ganzen, Erik Meijer, in einem Interview über Rx.
(Achtung: Dieses Interview ist auf sehr hohem Abstraktionsniveau! Eine Beschreibung von Rx für Praktiker sucht man hier vergebens.)
Den schätze ich eigentlich sehr, nicht nur für sein früheres “brainchild” Linq. In der letzten Zeit zaubert Erik Meijer aber zumindest mir kein beseeltes Grinsen mehr auf Gesicht, sondern Stirnrunzeln. Da war zuerst das ganz unglückliche Project Volta, welches fundamentalen Erkenntnissen zu verteilten Anwendungen zuwiderlief und jetzt zurecht offline ist. Und jetzt kommt der Reactive Framework.
Was ist der Rx? Das kann man hier und hier recht gut lesen. Ich möchte das jedoch lieber nicht selbst wiedergeben. Denn für mich besteht das Problem darin, dass ich neues Vokabular für etwas Altes benutzen müsste. Meine Kritik am Rx ist: es ist eben keine tolle neue Erfindung seit geschnitten Brot, sondern etwas lange Bekanntes in aufgehübschtes Gewand kleidet.
Mit Rx ist Microsoft aus meiner Sicht nur über Complex Event Processing (CEP) gestolpert. Wenn Erik Meijer die Rx-Interfaces als komplementär zu IEnumerable/IEnumerator ansieht und damit Linq-Queries auf Strömen von Events visioniert, dann ist das nichts anderes als eben CEP: statt auf fixen Daten Queries zu fahren, liest man Ströme von Daten ein und verarbeitet sie mit Queries. Das ist nicht neu, das ist kein Hexenwerk.
Wer wirklich einmal ausprobieren will, was man mit einer Abfragesprache auf Event-Strömen tun kann, der kann mit NEsper spielen: http://esper.codehaus.org/. Das ist (zusammen mit seinem Java-Bruder Esper) ein enterprise ready Open Source CEP Framework. Hier ein kurzes Beispiel für die Ermittlung des Durchschnittspreises “vorbeirauschender” Bestellungen (OrderEvent) innerhalb von jeweils 30-Sekunden-Fenstern.
select avg(price) from org.myapp.event.OrderEvent.win:time(30 sec)
Das ist grundsätzlich nichts anderes tut Rx (s. dazu das Beispiel zur Generierung von Mouse Drag Events hier).
Oder nehmen wir einfach nur Datenflüsse (data flows). Wo ist denn der Unterschied zwischen Rx, das nun wundersamerweise z.B. Mausereignisse als abfragbare Ereignisflüsse sieht? Oder als Flüsse, auf denen man Ereignisbehandlungsroutinen notieren kann. Tut mir leid, da ist nichts neues für mich dabei. Wenn ich im etablierten Paradigma data flow denke, dann ist das etwas uraltes und kann mit heutigen Mitteln bewältigt werden.
Hier ein einfacher Event-Strom mit “Event-Handler”:
new[] {1, 2, 3}.Subscribe(Console.WriteLine);
Der “Event-Handler” braucht nur das altbekannte IEnumerable:
static class EnumExt
{
public static void Subscribe<T>(this IEnumerable<T> eventStream,
Action<T> handleEvent)
{
foreach (T e in eventStream)
handleEvent(e);
}
}
Aber damit nicht genug! Man kann auch IEnumerable-Ströme zusammenstecken zu “Pipes and Filters”. Das ist dann ein Flow. Warum nicht aus einer Zahlenliste die ungeraden herausziehen und negieren?
Zahlen –> UngeradeZahl | ZahlNegieren –> Liste der negierten ungerade Zahlen
Die Formulierung in Code ist denkbar einfach:
new[] {1, 2, 3, 4, 5, 6, 7}
.Where(n => n%2 != 0)
.Transform(n => -n)
.Subscribe(Console.WriteLine);
Und wenn noch ein Schritt dazukommen soll – z.B. die Quadratur der ungeraden Zahlen –, dann wird der einfach eingefügt:
new[] {1, 2, 3, 4, 5, 6, 7}
.Where(n => n%2 != 0)
.Transform(n => n*n)
.Transform(n => -n)
.Subscribe(Console.WriteLine);
Die Transformationsmethode ist straightforward eine Extension Method wie oben Subscribe():
public static IEnumerable<TOut> Transform<TIn, TOut>(
this IEnumerable<TIn> eventStream,
Func<TIn, TOut> processEvent)
{
foreach (TIn e in eventStream)
yield return processEvent(e);
}
Wer will, kann sowas auch asynchron machen. Das habe ich schon in früheren Blogposts beschrieben:
http://ralfw.blogspot.com/2009/07/ccr-flows-asynchrone-prozesse-mit-der.html
http://ralfw.blogspot.com/2009/07/verstandnisvorteil-fur-flows.html
Flows habe ich aber natürlich nicht erfunden. Die sind alt. Hier eine Quelle, die das Paradigma schon in den 1970ern propagiert hat: http://jpaulmorrison.com/fbp/. Mainstream ist es aber nicht geworden, wie wir sehen. Schade. Denn in die Schritte in einem Flow steigern die Evolvierbarkeit von Software, finde ich.
Jetzt noch die Umsetzung von Mausbewegungen in einen Strom, den man abfragen kann:
public partial class Form1 : Form
{
private readonly EventStream mouseMovements = new EventStream();
public Form1()
{
InitializeComponent();
this.MouseMove += this.mouseMovements;
}
…
Eine Klasse EventStream sorgt dafür, die Mausbewegungen (oder auch andere Events) in ein IEnumerable zu transformieren. Das kann man dann z.B. so durchlaufen:
this.mouseMovements.Events<MouseEventArgs>()
.Where(args => args.X < 100 && args.Y < 100)
.Subscribe(args => Console.WriteLine("{0}, {1}", args.X, args.Y));
Wie macht EventStream das? Die Windows Message Loop darf ja nicht aufgehalten werden. Es geht nur asynchron. Also rufe ich die Concurrency Coordination Runtime (CCR) zur Hilfe:
class EventStream
{
public class Event
{
public object Sender;
public EventArgs Args;
}
private Port<Event> events = new Port<Event>();
private void EventListener(object sender, EventArgs args)
{
this.events.Post(new Event{Sender = sender, Args=args});
}
public IEnumerable<TEvent> Events<TEvent>() where TEvent : EventArgs
{
Event e;
while (this.events.Test(out e))
yield return (TEvent)e.Args;
}
public static implicit operator MouseEventHandler(EventStream stream)
{
return stream.EventListener;
}
…
}
Mit der impliziten Konvertierung kann ich den EventStream einfach an einen Eventhandler für die Mausbewegungen binden, ohne zu verraten, wer intern die Verarbeitung übernimmt.
Der Eventhandler schiebt dann jeden Event in einen CCR Port. Das ist sogar Thread-sicher. Und wenn ich die aufgelaufenen Events prozessieren will, dann hole ich sie mir über Events() als IEnumerable wieder heraus.
Alternativ könnte ich natürlich auch einen Eventhandler registrieren, der life auf neue Events reagiert und sie z.B. in einen asynchronen Flow einspeist. Dann könnte ich in einer Kette von asynchronen Schritten filtern, transformieren, reagieren. Auch da wären Where(), Transform() und Subscribe() wie oben in den synchronen Beispielen möglich.
Wo also ist das entscheidend neue beim Rx? Beats me. Ich kann nur die grübelnd die Stirn runzeln.
Ok, der Rx mag hier und da etwas einfacher machen, weil er schon Abstraktionen bietet, die ich vielleicht erst bauen muss/müsste. Das wäre ne nette Sache – aber deshalb hat Erik Meijer nun nicht gleich “the best thing since sliced bread” erfunden. Er steht nur in einer langen Tradition. Die zu erwähnen und weniger die Flügel zu schlagen, um nicht zuviel Hype-Staub aufzuwirbeln, das fänd ich angemessen.
PS: Oder übersehe ich hier die eigentliche Erfindung? Geht der brilliante Innovationshub an mir vorbei? Ich bitte um Erhellung.
5 Kommentare:
Ralf, Du hast in der Tat nichts übersehen oder mistverstanden. Alles was Du hier gerade so hervorragend auseinandergepflückt und mit Hilfe von Esper demonstriert hast, ist mir ebenfalls beim Betrachten der Rx Videos aufgefallen; ich konnte mich leider nur zu einem (leicht hämischen) tweet erbarmen: http://twitter.com/asynchronaut/status/4500448611
Sicher ist es sinnvoll, daß man über event streams Filter laufen lassen kann. Blöderweise ist die Idee aber auch schon >20 Jahre alt und wurde schon ~1986 bei Gelernter propagiert, der ja Tuplespaces erfunden hat. Die dürften Dir bekannt sein :)
CEP engines sind ebenfalls im Markt etabliert, von daher ist es in der Tat eher ein architektureller Rückschritt, soetwas in die Sprache zu integrieren. Hauptsache es wurden wieder wieder eine PhD Arbeit und Research Papers geschrieben und ganz wichtig von Dualitäten in funktionalen Sprachen gefaselt, die auch recht an den Haaren herbeigezogen wirken - (eingebildete?) mathematische Eleganz und konzeptionelle Symmetrie hin oder her.
Was mich am meisten an den Rx Videos bzw. vielen Microsoft-Entwicklungen ärgert, ist oftmals der Mangel an Horizont bzw. die Unwilligkeit anzuerkennen, daß vieles schon mal erfunden wurde - nur nicht bei Microsoft. Ich weiß nicht woran das liegt: akademische Hybris, Alter, oder einfacher nur der US-typische Hurra-Euphorismus wenn es darum geht, in der eigenen Sandkiste etwas neues zu erfinden - schließlich ist man ja bei den Besten Der Besten..
@Holger: Wirklich Neues ist halt selten. Wir sind in einer Zeit und in einer Branche, da Fortschritte allemal (wenn nicht schon früher) vor allem klein sind und dadurch entstehen, dass man irgendwas synthetisiert, d.h. Existierendes zusammenführt und ein kleinwenig mehr Summe des Ganzen erhält.
IEnumerable war nur der Ausdruck eines Iterators in .NET und damit alt. SQL ist alt. Und Linq hat beides zusammengeführt. Das Mehr der Summe waren dabei Extension Methods, würd ich sagen.
Jetzt Rx. Eventströme sind alt. Sie mit einer Abfragesprache zu analyiseren und gar neue Events daraus zu generieren, ist alt. Und Rx führt das bei .NET irgendwie zusammen und erzeugt ein kleinmehr mehr als die Summe. Was das Mehr konkret ist, verstehe ich aber noch nicht. Etwas Einfachheit? Hm...
Was mir aufstößt ist dabei weniger ein "Mangel an Horizont", wie du es nennst. Ich kann nämlich nicht glauben, dass der Horizont von Erik Meijer so eng ist. Sondern eher der "Hurra-Euphorimus", d.h. eine gewisse Naivität und Unbekümmertheit.
Der Westen wird immer noch von Redmond aus gewonnen, auch wenn dahinter eigentlich nur der Pazifik liegt ;-)
Erik Meijer, der im Planwagen fährt und kindlich erstaunt ist über die Landschaften, durch die er da rollt, und dabei übersieht, dass dort schon Menschen leben. Es gibt schon Kultur in den unbewohnt geglaubten Gegenden.
Hm... wenn ich diese Metapher so schreibe, dann ist der Horizont vielleicht doch nicht so weit? Egal. Denn am Ende find ich es mit oder ohne weiten Horizont für Microsoft grundsätzlich bedenkenswert laut zu verkünden, die Welt sei nun durch dies oder jenes gerettet. Selbst wenn sie es denn wirklich wäre!
Bescheidenheit, Vorsicht, Zurückhaltung: das sind Tugenden, die ich zumindest im Fall wie bei Rx vermisse.
Irgendwie verhält sich Microsoft auch ambivalent: auf der einen Seite CTPs bis zum Abwinken, also Zurückhaltung und ewige Unsicherheit, ob denn etwas schon "richtig" sei. Auf der anderen Seite dann aber solch ein Fanfarenstoß.
Mit will auch nicht eingehen, warum man mit Rx ohne öffentliche Annäherung in Form von CTPs plötzlich hinterm Vorhang vorspringt es es gleich in die Produktion gibt. Denn anderes, was älter und ausgereift ist - z.B. CCR -, das findet und findet keinen Weg zu den Massen. Sehr schade.
-Ralf
Hi Ralf,
ich kann deine Kritik gut verstehen im Sinne deiner Argumentationsführung. Ich habe mir letzte Woche das erste der C9 lecture Videos angesehen. Was ich aber im Moment nicht verstehe, ist die Kritik von Holger des fehlenden Horizonts, bzw. deine er wolle das als "die Erfindung" verkaufen. Aber vielleicht fehlen mir auch ein paar Informationsquellen, auf die du dich beziehst.
In dem ersten Video macht er quasi nichts anderes als auf die Grundlagen in der FP aus der Geschichte heraus einzugehen und zu sagen, das alles schon mal da war und nichts neues erfunden wird. Er macht eigentlich genau das, nämlich zu erwähnen das es schon mal da war.
Hast du dieses erste Video auch angesehen? Zu finden unter http://channel9.msdn.com/shows/Going+Deep/Lecture-Series-Erik-Meijer-Functional-Programming-Fundamentals-Chapter-1/
Deine Lösung über die CCR finde ich übrigens sehr elegant - muss mir die CCR auch mal anschauen, hab die bisher ignoriert. Gefällt mir persönlich gut, dein Ansatz.
Die Kritik an die Marktreife sehe ich auch und warum z.B. CCR da nicht weiter kommt. Was macht in diesem Zusammenhang der AppSpace?
Rainer
@Rainer: Ich lese "have created a profound and beautiful .NET library that will take managed event based programming to new levels."
und ich lese
"Meijer and software design engineer Wes Dyer have designed what Meijer described as a major breakthrough in asynchronous programming: 'Of all the work I've done in my career so far, this is the most exciting,' Meijer told session attendees. 'I know it's a bold statement, but I really believe that the problem of asynchronous programming events has been solved.'"
Das ist nicht bescheiden, das zeigt nicht, das man auf Existierendem aufbaut. Es klingt für mich wie "Wir haben es grad ex nihilo erfunden!"
Wenn ich damit der einen oder anderen Person unrecht tue, dann ist das bedauerlich. Am Ende zählt aber nicht die persönliche Bescheidenheit eines Einzelnen, sondern die Gesamtbotschaft von Microsoft als Unternehmen. Und die ist nicht bescheiden, nicht zurückhaltend, nicht differenziert, wie ich finde. Sie ist euphorisch, sie erzeugt einen Hype.
Aber es gibt natürlich auch andere, positive Beispiele von Microsoft.
-Ralf
@Ralf: Danke für deine Ausführungen, da stimme ich dir zu.
Wurde bei Microsoft nicht schon immer viel Hype gemacht?
Vielleicht ist dieser Hype eine gute Möglichkeit den bescheidenen Softwareentwickler, der Anwendungsentwicklung betreibt darauf aufmerksam zumachen. Ihm neue Ideen zu geben. Glaubst du er würde sich in Rahmen seiner Entwicklungszeit mit solchen Themen beschäftigen? Vielleicht kommt auch nicht jeder auf die Idee und sieht erst druch den neuen, andersartigen Input was er tun kann.
Aber darum geht es dir ja gar nicht, dich stört die Haltung zu dem Thema, also die Art und Weise, wie das ganze präsentiert wird. Das es das tollste neueste "MS'sche" ist.
Ja, da hast du wohl recht.
Kommentar veröffentlichen
Hinweis: Nur ein Mitglied dieses Blogs kann Kommentare posten.