[linux-l] typdynamische Sprachfeatures

Volker Grabsch vog at notjusthosting.com
Sa Okt 1 22:57:50 CEST 2005


> > Introspektion kann nervende Tipp-Orgien ersparen. Gut eingesetzt
> > ermöglicht es eine starke Vereinfachung des Programm-Codes. Ein
> > typisches "sanftes" Beispiel sind Dispatcher:
> >
> > 	class Test:
> > 		def call(self,action):
> > 			getattr(self,"call_"+action)()
> >
> > 		def call_meth1(self):
> > 			print "meth1"
> >
> > 		def call_meth2(self):
> > 			print "meth2"
> >
> >
> > 	t = Test()
> > 	t.call("meth1")
> > 	t.call("meth2")
> > 	t.call("meth3") # wirft Exception
> 
> Welchen Vorteil hast Du davon?
> 	t = Test()
> 	t.call_meth1()
> 	t.call_meth2()
> tut doch genau das selbe, und
> 	t.call_meth3()
> führt zu einem *statischen* Fehler. Und mehr getippt habe ich jetzt auch 
> nicht.

Du redest gegen mich, ohne mir zu widersprechen. Ich habe längst
eingeräumt, dass der Beispielcode in dieser Form sinnlos ist. Es
ging nur darum, den Dispatcher irgendwie zu demonstrieren. Von mir
aus nehmen wir eben folgenden Code:

	t = Test()

	# User kann nun z.B. "meth1" eintippen
	name = raw_input("Was soll getan werden? ")

	t.call(name)


.. besser?


> > Natürlich macht das keinen Sinn, wenn man die Dynamik nicht braucht,
> > aber falls man z.B. ein Menü-System hat, wo "action" zur Laufzeit
> > eingelesen wird, dann ist dies eine sehr bequeme Möglichkeit.
> 
> Einem Menu-System kann ich genausogut sagen, welche Funktion/Methode an 
> das jeweilige Oberflächenelement gebunden ist.

Natürlich meinte ich ein textbasiertes Menüsystem.

Ein anderer ganz klassischer Anwendungsfall sind auch Remote-Aufrufe,
z.B. wenn ein String wie "meth1" per CGI-Parameter kommt.

> Sowas in Strings zu 
> codieren ist nicht nur ineffizient sondern extrem leichtsinnig. 
> Ineffizient ist es, weil man einen Dispatcher braucht, leichtsinnig ist 
> es, weil keine (statische) Kontrolle über das Zusammenpassen von 
> Oberfläche und Modell da ist.

Willst du mich verarschen??

Es ging mir doch gerade darum, einen Dispatcher zu schreiben.
Es ging nicht darum, wann ein Dispatcher sinnvoll ist und wann
nicht, sondern dass die dynamischen Features von Python es einem
besonders leicht machen, z.B. Dispatcher zu schreiben. Du wolltest
ein Beispiel für eine sinnvolle Anwendung dieser Dynamik haben, und
ich habe dir welche genannt, u.a. Dispatcher und Decorator.

Ich fühle mich verarscht, dass du nun auf einmal von Thema
ablenkst, und suggerierst, es ginge gar nicht darum, einen
Dispatcher zu schreiben! Das ist Blödsinn, denn genau darum
ging es von Anfang an in meinem Beispiel.

So kann man nicht diskutieren. Du kannst nicht einfach mein
Beispiel heruntermachen, indem du es aus dem Kontext reißt,
und in einen Anwendungsfall einbettest, in dem es keinen
Sinn mehr ergibt.

> > Ein "medium" Beispiel wäre das Decorator-Muster. Statt einen
> > langweiligen Decorator zusammenzutippen, und stets mit dem Interface
> > synchron zu halten, obwohl sie Methoden doch nur weitergeleitet
> > werden, was jeweils eine einzige Zeile Code bedeutet. In Python packt
> > man ein sehr dynamisches Objekt dazwischen, das jeden Methodenaufruf
> > abfängt (kann man machen), und ihn selbst verwaltet, indem es einfach
> > von "dekorierten" Objekt die gleichnamige Methode mit den
> > entsprechenden Parametern aufruft. Letzteres entspricht übrigens genau
> > dem, was im GoF-Buch beim Decorator-Muster für Smalltalk empfohlen
> > wird.
> 
> Was ist ein Decorator?

Siehe GoF-Patterns. Dort ist es wesentlich besser erklärt als ich es
hier könnte. Auf vielen WWW-Seiten wird das ebenfalls erklärt.

Kurzfassung: Erweitere ein Objekt dynamisch um Methoden. Python erlaubt
das direkt. In C++ oder Java regelst du das über ein neues Objekt, das
eine Instanz auf das "Original"-Objekt hält, das gleiche Interface hat,
und alle Methoden auf die Original-Instanz weiterleitet. Außerdem
implementiert dieses neue Objekt zusätzlich noch die gewünschten
Methoden.

Ganz kurze Kurzfassung: RTFM.  ;-)

Decorator sind in C++/Java nur über Umwege möglich, während es in
Ruby/Python ein Kinderspiel ist. In Ruby gibt es sogar Basisklassen
(genauer: Module, die man von Klassen aus "includen" kann), die einem
aus der eigenen Klasse automatisch einen Decorator für eine andere
Klasse bauen.

> > Das Beispiel ist insofern schlecht, als dass auch hier eigentlich
> > zur Compilezeit alles feststeht. Jedoch kann man SQLObject auch sagen,
> > dass es zur Laufzeit die Spalten der SQL-Tabelle hernimmt und die
> > entsprechende Klasse erzeugt.
> 
> Was willst Du damit sagen? Was meinst Du mit 'krass'? Brauche ich denn 
> auch nur eine Codezeile mehr, wenn ich das mit C++ hinschreiben will?

Nein, sondern in C++ ist das überhaupt nicht direkt möglich.

> > Bitte keine weiteren Detail-Fragen zu SQLObject, das ist auf deren
> > Webseite wunderbar alles erklärt. Mit geht es nur darum: Natürlich ist
> > die Introspektion ein "Hack", und sollte sehr sparsam eingesetzt
> > werden.
> 
> Mir ist absolut nicht klar, warum ich sie überhaupt einsetzen *will*.

Ich persönlich brauchte dieses Feature auch nicht. Aber andere
Applikationen brauchen es vielleicht.

> > Dass diese Nachrichten manchmal erst zur Laufzeit feststehen, und
> > somit im Konstrukt "p.name = ..." der Begriff "name" mehr als String
> > statt als Funktionsname angesehen werden kann, ist Absicht! Genauso
> > wird das in Python gemacht: Alle Variablen-Namen, Methoden-Namen, etc.
> > sind letztlich Einträge in Dictionaries (Hashtabellen), auf die man
> > zur Laufzeit Einfluss nehmen kann.
> 
> Und wieder codierst Du Methoden in Strings. Das brauchst Du allenfalls, 
> wenn der Programmfluss die Plattform verlässt (ala RPC). Das erschlägst 
> Du aber nicht mit einer Programmiersprache, sondern Du benötigst tiefer 
> liegende Konzepte (CORBA, COM, JavaBeans sind Beispiele dafür).

Mag sein. Aber ein einfaches RPC-System ist in Ruby und auch in Python
sehr schnell erledigt.

> > Der Dispatcher oder der Decorator sind gute Beispiele. In C++ oder
> > Java muss man dafür extra Boilerplate-Code produzieren, d.h. sehr
> > ähnlichen Code an mehreren Stellen hinschreiben und synchron halten.
> 
> Ich würde gerne ein Beispiel sehen. Was ist Boilerplate-Code? 'Sehr 
> ähnlichen Code an mehreren Stellen hinschreiben und synchron halten' 
> pflege ich mit Templates zu vermeiden.

Siehe GoF ... da stehen die Beispiele ausführlich erklärt drin, und zwar
viel besser, als ich es hier auf anhieb könnte. Auch andere Muster wie
z.B. Proxy, die bei Java-RMI und Konsorten extrem intensiv angewendet
werden, sind in Ruby und Python mit viel weniger Aufwandt verbunden.

> > Im Vergleich zu C++ oder Java werden also vorallem zahlreiche
> > OO-Muster, und das Schreiben von "object-relational wrappers"
> > erleichtert. Man kann die Probleme innerhalb der Sprache angehen, und
> > Code-Duplikation verhindern.
> 
> Was ist ein object-relational wrapper? Die Probleme innerhalb der Sprache 
> angehen und Code-Duplikation verhindern kann ich in C++ auch.

Ja, aber nur via Codegeneratoren, und durch ein Einführen einer
Metasprache. Python hingegen ist gleich die Metasprache.

> > In sicherheitskritischen Projekten ist ersteres das geringere Übel,
> > aber möglichst wenig Beschäftigungstherapie beim Programmieren zu
> > haben, halte ich für wichtiger, insbesondere für meine Projekte.
> 
> Schon mal was von 'code reuse' gehört? (SCNR)

Eine sinnlose Beleidigung. Bitte gehe konkret auf das ein, was ich
gesagt habe. Das kann ja wohl nicht wahr sein! Diese Diskussion habe ich
angezettelt, gerade *weil* ich mir um Softwaredesign sehr viele Gedanken
mache, und *weil* ich stets dabei bin, verschiedene Konzepte zu
vergleichen.

Und wenn ich gewisse Muster (wie z.B. Decorator) einmal beschreibe,
und fortan direkt verwenden kann, bzw. diese Muster schon Teil der
Standard-API sind (das ist in Ruby der Fall), dann *ist* das doch
Code Reuse! Aber wenn ich (wie in Java) diese Muster jedes Mal von
neuem durchochsen muss, für jede Klasse bei der ich das brauche,
dann ist eben *kein* Code Reuse, sondern Boilerplate. Dummes
herumgetippe auf der Tastatur, das ich gerne wegabstrahiert hätte.

Und zwar nicht erst durch eine extra Meta-Sprache + Java-Codegenerator.
Sondern *innerhalb* der Sprache.

> > > Python ist nicht mächtiger als C++, bezogen auf die zu lösenden
> > > Probleme.
> >
> > Bezogen auf strukturelle Probleme eröffnet Python ganz andere
> > Möglichkeiten als C++, seinen Code zu organisieren.
> 
> Schon möglich, ich glaube aber eher, dass Du die Möglichekeiten zur 
> Code-Organisation von C++ unterschätzt. Allerdings sehe ich nicht ganz 
> ein, warum man bewährte Konzepte (hier: statische Typprüfung) verlassen 
> soll.

Templates sind ein guter Anfang, falls du das meinst.

Dass man statische Typen dafür verlassen sollte, habe ich nie gesagt!
Im Gegenteil! Das fand ich ja bei den Sprachen, die ich bisher
kennengelernt habe, so schade: dass man immer auf eins von beidem
verzichten muss.

> Versteh' mich nicht falsch: bezogen auf Java gebe ich Dir gerne Recht - 
> eine grässliche Sprache, wenn man Größeres vorhat. Ich wehre mich auch 
> nicht gegen die nützlichen Vereinfachungen, die Python bietet. Aber 
> solche 'dynamischen' Sprachfeatures wie oben angedeutet halte ich für 
> Fehl am Platz. Der Python-Interpreter sollte die Möglichkeit besitzen, 
> sie zuverlässig abzuschalten.

Das ist korrekt. Aber trotz dieser Mängel überwiegen in meinen
Anwendungen die Vorteile von Python (und ähnlichen Sprachen)!

Was an Zeit verloren geht, um Trivialitäten hinzuschreiben, ist für
mich kritischer als der Verzicht auf statische Typen. Obwohl ich
natürlich viel lieber beides hätte - keine Frage. Ist natürlich projekt-
abhängig. YMMV

> > Bezogen auf das reine Lösen von Problemen, egal wie umständlich, ist
> > Python natürlich genauso mächtig wie C++ oder Assembler. In Python
> > legt man den Schwerpunkt darauf, übersichtlichen kompakten Code
> > zu produzieren.
> 
> In C++ auch. Nur in Java offensichtlich nicht ;)

Dein Decorator in C++ würde mich mal interessieren. Oder von mir aus
auch ein Dispatcher (am besten analog zu meinem obigen Beispiel-Code.
Dann nehm ich dein Beispiel her und markiere dir genau die Stellen,
dir mich nerven, also den Boilerplate-Code.

Bei einem einzigen Dispatcher im Code ist es ja noch nicht so schlimm,
aber wenn man dauernd solchen Code schreiben muss, und zig Code-Fetzen
synchron halten muss, bloß damit der Compiler die Umsetzung "string
-> Methode" hinkriegt, die sowieso nicht typsicher ist, dann steht
mir in dem Moment das Typensystem im Weg, statt mir zu helfen.

> > > Laut einer Erhebung von 1997 in den USA enthalten ausgelieferte
> > > Softwareprojekte durchschnittlich (!) 7 Fehler pro 100 Codezeilen...
> >
> > Mein Code nicht.  ;-)
> 
> Du bist also ein Überflieger :)

Nein, natürlich nicht. Aber ich hab z.B. vorgestern nen Haufen Code
in Python geschrieben, und das sind knapp 100 Zeilen Code gewesen.
Aber da kam es besonders auf die API an und die Herangehensweise;
inhaltlich haben die Methoden kaum was gemacht.

Als ich meine Testsuite drübergejagt habe, fiel gerade ein einziger
Fehler auf. Und der fiel mir schon in dem Moment auf, wo ich einen
bestimmten Testcase geschrieben hatte.  :-)

Würde ich natürlich größeren Code schreiben, oder hätte ich das
ganze in C++ gemacht, wären garantiert mehr Bugs drin gewesen.
Zumindest bei mir, aus meiner Erfahrung.

> > Wenn ich weniger schreiben muss (und ich meine, *wesentlich* weniger),
> > um ihm klar zu machen, was ich will, und wenn ich nicht haufenweise
> > Banalitäten aufschreiben muss, die der Compiler auch selbst
> > herausfinden kann, dann erspart das ne Menge Zeit und Nerven, und ich
> > kann mich auf das Wesentliche konzentrieren. Etwas mehr Debugging
> > nehme ich dafür gerne in Kauf. Vorallem die Strukturierung von
> > größerem Programm-Code und das Refactoring, profitieren sehr davon,
> > wenn der Code knapp und leicht durchschaubar gehalten werden kann.
> 
> Vorsicht. 'Etwas mehr Debgging' bedeutet exponentiellen Aufwand bei der 
> Erstellung der Testfälle. Das wird im professionellen Umfeld nicht 
> akzeptiert.

Bisher war, aufgrund der Einfachheit die Python mit sich bringt, meine
"Grund-Fehlerrate" so niedrig, dass man Code bisher sogar viel weniger
Debugging-Zeit gefressen hat, als entsprechender C++ oder Java-Code.
Wohlbemerkt, bei mir. YMMV

> Die Tendenz geht in eine andere Richtung. Schwerpunkt: code reuse, das 
> Wiederverwenden von Softwarebausteinen (sehr zu empfehlen: Szyperski: 
> Component Software). Die wichtigste Voraussetzung ist dabei eine 
> sorgfältige und *verbindliche* Gestaltung der Schnittstellen 
> (Typfestigkeit zur Compilezeit!). Zwar werden Softwarekomponenten zur 
> Laufzeit zusammengesteckt (wie shared libraries oder, und da sind wir 
> plötzlich wieder on-topic, Kernelmodule), und die Schnittstellen können 
> zur Laufzeit 'erfragt' werden. Die Vereinbarung von Typen erfolgt aber 
> immer zur Compilezeit.

Ich steuere auf ein Projekt zu, wo eben diese Verbindlichkeiten nicht
gegeben sind, aber aus gutem Grund.

Es wird dennoch eine typfeste API geben, aber die wird in Wesentlichen
nur ein Workaround für ein dynamisches Typsystem sein, d.h. jedes
Objekt wird u.A. eine Methode "actions()" haben, die alle zulässigen
Aktionen ("Methoden", wenn man so will) zurückliefert, dann ein
"discover(...)", wo man zu einer bestimmten Methode die Parameter
abfragen kann, und eine Methode "call(...)", wo man die Methode mit
den entsprechenden Paramtern aufrufen kann.

Nun gut, könnte man sagen, bisherige Protokolle erlauben ähnliche Dinge
ja auch. Aber meine actions() und dicover()-Methoden liefern nicht
nur ne Methoden- bzw. Parameter-Liste zurück, sondern auch gleich ne
abstrakte GUI-Beschreibung. Und die Typen der Parameter ... bzw. es
braucht ja nur einen zu geben, wenn man "Struct"-Typen zulässt ..
ja, die Typen sind ebenfalls sehr dynamisch. Will eine Methode nen
Integer haben, aber der "Client" (z.B. die GUI) kennt nur Strings,
dann wird automatisch konvertiert. Und diese Konvertierungen sind
ebenfalls frei definierbar, genau wie die Typen. 

Mehr dazu in dem anderen Thread zu dem Thema.

> > Ich sehe, du arbeitest an der Uni, auf der ich studiere. Woran
> > arbeitest du denn? Welche Sprachen / Werzeuge benutzt du dafür?
> 
> Signalverarbeitung/Bildverarbeitung auf DSPs, FPGAs, ASICs, MPSs. 

Cool!

> Ich hatte gestern aber meinen letzten Arbeitstag (Zeitvertrag zu Ende :(, 
> meine Lehrveranstaltungen hast Du also verpasst)

Das ist ja schade.  :-(


Viele Grüße,

	Volker

-- 
Volker Grabsch
---<<(())>>---
Administrator
NotJustHosting GbR



Mehr Informationen über die Mailingliste linux-l