[linux-l] Ocml vs. Java

Volker Grabsch vog at notjusthosting.com
So Sep 25 19:57:27 CEST 2005


On Sat, Sep 24, 2005 at 02:45:40AM +0200, Oliver Bandel wrote:
> Es gibt nun mal Leute, die mögen lieber fertige Texte lesen, und andere
> sind eher dialogisch geneigt.
> Du scheinst derjenige der RTFM-Fraktion zu sein, ich bin eher dialogischer
> Typ. Das kann dann manchmal nervn - beide Seiten. ;-)

Nee, da liegst du IMHO voll daneben. Ich bin definitiv ein Dialog-Typ.
Das merkt man am Studium (gemeinschaftlich komme ich, auch wenn's um
reines Lernen geht, wesentlich besser voran als allein), und das merkt
man an unserem Gespräch. Schau doch nur, wie viel ich bereits
geschrieben habe, bis ich das erste Mal "RTFM" sagte! Und dieses RTFM
bezog sich auf einzelne Fragen, niemals auf gesamte Mails. Oder anders
ausgedrückt: Du musstest dir sehr viel Mühe geben, mir ein "RTFM" zu
entlocken. :-)

> > Ja, natürlich! Aber in Python sind Iteratoren darüberhinaus noch Objekte
> > mit einer "next"-Methode.
> 
> Naja, das ist in dem Stream-Modul auch so.
> Es gibt next und iter.

Das ist wahr. Das entspricht tatsächlich ungefähr dem, was ich in Python
beschrieb.


> [...]
> > Funktionale Konzepte sind mir sehr wohl vertraut ... *gerade* als
> > Python-Programmierer.
> 
> Aha. Naja, daß Python so viele funktionale Konzepte kennt, war mir neu.
> Ich dachte, Ruby sei die einzige dr Scripting-Sprachen, die da brillieren
> kann.

Nö. Was die "Featureliste" angeht, sind sich Ruby und Python sogar recht
änhlich. Auch, was die Ziele angeht, und die Sprachen, von denen sie
Elemente übernommen haben. Dennoch gibt es natürlich an allen Ecken und
Enden auch Unterschiede, auch Unterschiede konzeptioneller Art.

Zumindest alles, was wir bisher hatten, könnte ich genauso in Ruby
veranstalten.

> > ein ausgefeites
> > dynamisches Typ- und Klassen-System auf der anderen Seite.
> 
> *räusper* (jedenfalls bzgl. Typsystems ;-))

Das typsystem in Python ist sehr weit entwickelt. Unter den dynamisch
typisierten Sprachen ist Python IMHO wirklich ein Highlight.

Wenn du also Ocaml mit einer dynamisch typisierten Sprache zwecks
Sauberkeit / etc.  Vergleiche anstellen willst, vergleiche es bitte
nicht mit Perl, sondern mit Python.


> > In Python
> > kann man die verschiedenen Herangehensweisen wunderbar vergleichen
> > und gegenüberstellen.
> > 
> > 
> > Natürlich kann ich in Python sowas hier schreiben:
> > 
> > 	def mein_iterator(f,start):
> > 		f(start + 1)
> > 		f(start * 2)
> > 		f(start / 3)
> > 
> > 	def innere_funktion(param):
> > 		print param
> > 
> > 	mein_iterator(innere_funktion,10)
> > 
> > 
> > liefert:
> > 
> > 	11
> > 	20
> > 	3
> 
> Sehr ungewöhnliche Syntax... aus meiner Sicht jedenfalls.
> 
> Ah, das ist noch nicht mal ein akkumuliertes Egbnsis?
> Das hatte ich erst gedacht, als ichz es eben in Ocaml
> kodieren wollte...

Nee, sorry, du hast am "falschen Ende" optimiert. Deine Vereinfachung
ist aber korrekt, und in Python übrigens haargenauso möglich:

> ======
> # let liste = apply [
>    (fun x -> x + 1);
>    (fun x -> x * 2);
>   (fun x -> x / 3) ]
>   10 ;;
> ======


In Python:

	def fun1(x): return x+1
	def fun2(x): return x*2
	def fun3(x): return x/3

	[f(10) for f in (fun1,fun2,fun3)]

bzw. auch in einer Zeile mittels "lambda".



> ======
> # let stream = Stream.of_list liste;;
> val stream : int Stream.t = <abstr>
> # open Stream (* damit wir statt "Stream.next" auch "next" schreiben können *);;
> # next stream;;
> - : int = 11
> # next stream;;
> - : int = 20
> # next stream;;
> - : int = 3
> # next stream;;
> Exception: Stream.Failure.
> # 
> ======
> 
> Na, das ist doch zumindest Deinem sehr ähnlich?
> Und auch nicht " all zu funktional", so daß Du keine großen
> Syntax-Bauchschmerzen bekommst? ;-)

Interessant. BTW, ich bekomme von funktionaler Syntax keine
Bauchschmerzen. Ich meinte nur, dass man, gerade bei einfachen
Ausdrücken, die funktionalen Paradigmen nicht zu weit treiben
sollte, weil for/if - Konstrukte IMHO besser lesbar sind. Ist
sicher auch Geschmackssache.

> oder besser ein anderer Name und auch anders geschrieben:
> ====
> let create_iterator flist start = Stream.of_list ( apply flist start)
> ====

Ja klar, das ist natürlich auch möglich. Du packst einen Iterator um
deine Liste. Dann verwendest du die Liste wie einen Iterator. In Python
wiegesagt macht man das eher umgekehrt: Man will einen Iterator wie eine
Liste behandeln können. Kommt aber insgesamt aufs Gleiche raus, da hast
du recht.


Nur mal des Interesses halber: Wie würdest du folgendes Problem in Ocaml
angehen? Du willst eine Funktion "list_file". Sie bekommt als Parameter
eine Datei (genauer: irgendeinen Stream, den man zeilenweise durchlaufen
kann), und liefert einen Iterator zurück. Diesen Iterator soll man
"aufheben" können, d.h. der Iterator soll zwischendurch auch wirklich
als einzelnes Objekt (bzw. Funktion) dastehen. Wenn man ihn später
durchläuft, iteriert er alle Zeilen der Datei, entfernt das Newline und
"trailing spaces", und setzt ein "XXX: " davor. Einzige Bedingung: Keine
Listen! Das heißt, die Datei wird wirklich erst dann iteriert, wenn die
zurückgegebene Iterator aufgerufen wird. Wir können nämlich auch sehr
große Dateien haben, die auf keinen Fall zwischendurch komplett in den
RAM geladen werden dürfen.

Das heißt, wir nehmen eine Datei "test.txt":

Zeile1      
Zeile2      
Letzte Zeile

Und folgenden Python-Code:

	f = open("test.txt")
	iter = list_file(f)

	for x in iter: print x

	close(f)


Dies sollte zurückliefern:

XXX: Zeile1
XXX: Zeile2
XXX: Letzte Zeile

In Python würde list_file so aussehen:

	def list_file(f):
	    return ("XXX: "+line.rstrip() for line in f)


andere Variante:

	def list_file(f):

	    for line in f:
	        yield "XXX: "+line.rstrip()



> Mir fällt zwar noch keine sinnvolle Anwendung auf Anhieb ein, aber diese
> etwas umständliche Sache geht auch:
> 
> ================================
> # let streamer = create_iterator [ (String.length) ] "iouough";;
> val streamer : int Stream.t = <abstr>
> # next streamer;;
> - : int = 7
> # next streamer;;
> Exception: Stream.Failure.
> # 
> ================================
> 
> Geht sowas in Python auch?

Ja, klar. Siehe oben.


> Oder sind Iteratoren zwingend festgelegt auf Integers?

Nein, keineswegs! In Python können Iteratoren selbstverständlich alles
mögliche durchlaufen. Auch Objekte von unterschiedlichem Typ.

> > Beim funktionalen Ansatz hingegen muss man mit den Iteratoren wie mit
> > verschachtelten Funktionen umgehen, und sie ständig auf diese Weise
> > im Hirn halten.
> 
> Wie findets Du den Ansatz da oben?

Wiegesagt, du hast das falsche optimiert. Dein Ansatz geht in Python
genauso. War einfach ein blödes Beispiel von mir.


> Btw: Die Sache mit den Verschachtelungen sehe ich noch ein bischen
> anders. Man kann sich auch Funktionen als composites erstellen.
> In Haskell gibt es dafür sogar einen eigenen Operator (".").
> Den gibt's in OCaml leider nicht. Aber dann baut man sich eben eine
> entsprechende Kompositions-Funktion zusammen.
> Muß man nur einmal machen und hat dann seine Ruhe. ;-)

Das erinnert mich an etwas: In Python gibt es sehr viele Sprache-Features,
die gar nicht im Compiler gelöst werden, sondern direkt in Python, dank
einiger mächtiger Meta-Features. Zum Beispiel kann man mit "Metaclasses"
in Python ne Menge anstellen. Sie sind für Anfänger schwer zu verstehen,
dennoch vom Konzept her so einfach wie nur denkbar, wenn man sich
ansieht, was man damit realisieren kann. Einzelheiten würden jetzt aber
wirklich zu weit führen.


> Naja...  OCaml ist eben keine purely functional language.
> Sie bietet eben mehrer Möglichkeiten an; und genau deswegen
> hat sie bei mir bessere Karten als Haskell.
> 
> Haskell ist zwar edler, aber eben auch dogmatisch.

Mag sein, aber dort gibt's Listcomprehensions, da sind sie von den
Dogmen etwas abgegangen zugunsten von "Usability". Finde ich gut, sowas.

> [...]
> > Nun die funktionale Variante mit map/filter:
> > 
> > 	map(ausgabe,filter(kleiner10,map(durch2,filter(gerade,liste))))
> Naja, ob man das so wirklich machenwollen würde?
> 
> Zumindest in Haskell kann man aber Funktionskomposition
> mit dem "."-Oprator machen.
> Dann notiert man das wie eine Pipe(line).
> Von rückwärts allerdings, aber immerhin ohne Klamerungs-Arien.

Cool. Nette Sache.

> > Und das sage ich nicht, weil ich keine
> > funktionalen Konzepte kenne oder nicht intensiv damit gearbeitet hätte,
> > sondern einfach als pragmatischer Python-Programmierer, der beide
> > Varianten zur Auswahl hat und schaut, was besser lesbar ist.
> 
> haskell kennt auch List-Comprehensions. Und?
> Das ist deswegen nicht nicht-funktional.

Nein, aber es weicht IMHO vom funktionalen Dogma ab. Eigentlich könnte
man ja alles über zwei Funktionen "map" und "filter" machen, wozu also
Listcomprehensions? Weil sie sehr gut lesbar sind. :-)

Sorry, falls ich das als Python-Erfindung dargestellt haben sollte. Kaum
etwas von Python wurde in Python zuallererst gemacht. Aber es ist eine
gute Zusammenstellung, und kleine, aber sehr gut gewählte Auswahl von
Features anderer Sprachen.


> > Funktionale Elemente werden damit nicht komplett aus Python verbannt.
> 
> Du redest nur über die Syntax der Sprachen, aber was hat das mit
> funktional/nicht-funktional zu tun?
> doch erst mal nixhts ausser, daß Spracheigenschaften und Syntax vielleicht
> oftmals zusammen auftreten.

Ich wollte damit nur sagen, dass Python von diesem Dogma abgewichen ist.
"Funktionale Elemente" war als Bezeichnung wohl ziemlich schlecht.


> > Ich finde sie besser lesbar.
> 
> Naja, in manchen Fällen sind die auch sinnvoller, weil sie
> das problem besser darzustellen vermögen.
> Aber in den meisten Fällen habe ich gemerkt, daß die
> funktionale Programmierung am besten passt.
> Nicht für alles, aber sehr vieles.

In meinen Applikationen bin ich mit OO Programmiererung gut gefahren.
Aber wird haben offenbar auch recht unterschiedliche Typen von
Programmen, die wir schreiben. Wenn ich z.B. für's Wikidok-Projekt etwas
schreibe, werden wohl viel weniger Klassen drin vorkommen.  :-)

> > Huch, sorry, hab ich mich falsch ausgedrückt. Ich meinte natürlich nicht
> > alle funktionalen Elemente, sondern nur map und filter, weil sie gegen
> > naheliegendere Konstrukte ersetzt wurden, sowie den lambda-Operator,
> 
> Map und Filter sind wunderbare Funktionen.
> 
> Wenn diese im Kontext der Sprache Python nur auf so seltsame Weise eingesetzt
> werden, oder wenn Du sie nur auf so seltsame Weise einsetzt, dann ist das
> schon seltsam.

Ich setze sie ja nicht so ein.  :-)

> Wenn ich eine Liste von Values habe und nach einem Kriterium
> selektieren will, nach behalten und ignorieren (Aschenputtel-prinzip),
> dann ist doch filter eine prima Sache :)

Selbst wenn die Funktion also solche schon vorhanden ist
("filter_func"), würde das vielleicht anders aussehen. Da wäre da
hier eher schlechter lesbar

	(x for x in liste if filter_func(x))

... als das hier:

	filter(liste,filter_func)

Dennoch liefert ersteres einen Iterator zurück, während zweiteres
Speicher verschwendet und eine Liste aufbaut. Aber egal, in Python
gibt's sicher auch einen "filter", der einen Iterator und keine Liste
erzeugt.

> Wenn ich aber mit map/filter ALLES, was ich irgendwie programmieren kann,
> darstellen will, dann wird das natürlich nicht funktionieren.

Doch, das schon. Aber es ist unlesbar. :-)
Ein herrliches Beispiel dafür, was bei zu viel Dogmatismus passieren
kann.


> > Wenn OO mir dir Arbeit erleichtert und meinen Code besser lesbar macht,
> > dann nehme ich das. Dabei greife ich gelegentlich auf funktionale
> > Elemente zurück, wenn ich z.B. einfach nur ne Funktion und nicht ein
> > ganzes Objekt übergeben will.
> 
> Wenn es auch mit Modulen geht: keine Speicher-/Laufzeit-Ressourcen,
> die man verballert. Alles wird schön brav kompiliert und zur Laufzeit
> muß nix extra generiert werden.

Das ist natürlich ein genereller Vorteil von statisch typisierten
Sprachen: 

Wenn du Objekte hast, also Daten-Anteile, auf die du verschiedene
Operationen anwenden willst, dann haben Funktionen, die diese Daten als
Parameter bekommen, natürlich erstmal mehr Performance als Klassen.
Aber sobald du Polymorphie brauchst, hast du sowieso einen gewissen
Overhead, den du in diesem Fall aber gerne in Kauf nimmst, weil er
mikrig ist im Vergleich zur Alternative. Ein kleines dummes Beispiel:

	class Kreis:
	    def __init__(self,radius):
	        self.radius = radius

	    def draw(self):
	        print "Kreis mit Radius", self.radius


	class Rechteck:
	    def __init__(self,breite,hoehe):
	        self.breie = breite
	        self.hoehe = hoehe

	    def draw(self):
	        print "Rechteck der Form", self.breite, "x", self.hoehe


	liste = [Kreis(1),Rechteck(2,3),Kreis(4)]

	for obj in liste:
	    obj.draw()


Es kommt heraus:

	Kreis mit Radius 1
	Rechteck der Form 2 x 3
	Kreis mit Radius 4


Von mir aus gib Rechteck und Kreis noch eine gemeinsame Basisklasse,
welche die Methode "draw()" vorschreibt. In Python ist das nicht
notwendig, hauptsache die Objekte haben die Methode. Klassenhierarchien
und Vererbung gibt's in Python natürlich trotzdem.

Der "OO-Overhead" ist minimal im Vergleich zur Alternative, nämlich
eine Monster-Draw-Funktion zu haben. Und in dem Moment, wo "obj.draw()"
aufgerufen wird, kann der Compiler unmöglich wissen, welche Funktion
das ist.

Es ist ein einfaches Beispiel für Polymorphie. Der "herkömmliche Weg"
(riesige Draw-Funktion mit Fallunterscheidung für alle möglichen
Objekt-Typen) ist viel unübersichtlicher (genauso solche Konstrukte
will man ja mit Polymorphie verhindern), und hat übrigens zur Laufzeit
i.d.R. auch eine schlechtere Performance.

Es kommt also immer darauf an, ob du wirklich Klassen brauchst oder
nicht.

> [...]
> > Zum Beispiel ist das meiste, was du hier gemacht hast, in OO genauso
> > möglich, wenn man den Begriff "Modul" gegen "Klasse" und den Begriff
> > "Funktion" gegen "Methode" austauscht. Wieso du da so einen himmelweiten
> > Unterschied reindichtest, weiß ich nicht. In Python stehen mir alle
> > Möglichkeiten offen, und ich nutze die, die mir helfen.
> 
> OO: Laufzeit-Overhead.

Nicht unbedingt. Wenn man es missbraucht, dann schon. Aber wenn man es
wirklich mit Klassen zu tun hat, sollte man sie in der Programmiersprache
auch als Klassen programmieren können. Denn dann ist OO gerade *kein*
Overhead, sondern sehr effizient. Hat man natürlich nur-Daten-Objekte
oder nur-Funktionen-Objekte, sieht das etwas anders aus. Deiner
Pauschalisierung muss ich also entschieden widersprechen.

> > Also, was mich angeht, würde ich natürlich Ocaml lernen wollen, also
> > wenn ich z.B. dein Labanscale-Projekt unterstützen sollte, würde ich
> > natürlich deine Wahl (Ocaml) voll und ganz akzeptieren.
> 
> Aha, würde Dich das Teil also interessieren? :)
> hmhhh...

Natürlich! So sind wir doch ins Gespräch gekommen. Mich hat Ocaml
interessiert, also habe ich ein paar Fragen gestellt. Und mich
interessiert dein Laban-Projekt, deshalb habe ich mitgedacht, Fragen
gestellt, und Vorschläge gebracht.

> ... mit mehreren Leuten an was herum zu schrauben macht auch mehr Spaß,
> als das nur alleine zu machen.

Das ist auch meine Erfahrung.

> Vielleicht wäre das ja doch was, was man mal als Projekt anpeilen kann. :)

Anpeilen?!  Ich habe schon versucht, mich aktiv an der Planung deines
Laban-Projektes zu beteiligen, nur du hast all diese Versuche bisher
nicht wirklich wahrgenommen.



Viele Grüße,

	Volker

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



Mehr Informationen über die Mailingliste linux-l