[linux-l] Ocml vs. Java

Oliver Bandel oliver at first.in-berlin.de
Do Sep 22 22:32:15 CEST 2005


Moin,


also auf Sticheleien steh ich heute nicht so.
Dafür bin ich heute zu platt. Muß wohl mal wieder ein
Bierchen trinken, dann komm ich viell. besser drauf. ;-)

Denke jedenfalls, daß wir oft aneinander vorbei reden.


On Thu, Sep 22, 2005 at 05:07:06PM +0200, Volker Grabsch wrote:
> On Wed, Sep 21, 2005 at 11:49:05PM +0200, Oliver Bandel wrote:
> > OK, dann habe ich mich vielleicht ungünstig/mißverständlich ausgedrückt.
> > 
> > Eine Sprache nennt man funktional, wenn man Funktionen GENAUSO wie
> > andere Values behandeln kann. Tricksereien gelten nicht.
> 
> Naja, man muss halt zwischen Programmier-Stil und Sprachfeature
> unterscheiden. In Java *kann* man funktional programmieren, aber Java
> ist keine funktionale Sprache. Genauso kann man selbst in C und Assebler
> objektorientiert programmieren, obwohl es keine OO-Sprachen sind.

Naja, meine ich ja so.

 
[...]
> > [...]
> > > > Beispiel: gegeben sind zwei Listen und zwei Funktionen.
> > > > func_1 addiert zwei Integers,
> > > > func_2 multipliziert zwei Integers.
> > > > 
> > > > Ermittle Summe und Produkte der jew. korrespondierenden
> > > > Elemente der beiden Listen:
> > > 
> > > Statt diesen "prozedural vs. funktional" - Standard-Rant zu
> > > unterstützen, werde ich einfach mal einen Python-Rant dagegen
> > > starten. Python lässt sich nicht so einfach in die Schublade
> > > "prozedural" stecken, aber auch nicht "funktional". Es übernimmt
> > > aus beiden (und vielen anderen) Konzepten die guten Sachen.
> > 
> > Soweit ich weiß, hat Ruby auch ne menge funktionale Features an Board.
> > Wird aber anscheinend noch kaum benutzt?
> 
> Doch, doch, genau wie in Python wird das auch in Ruby an den Stellen
> eingesetzt, wo es sinnvoll ist. Aber die meisten funktionalen Konzepte

Ich meinte nicht, ob diese features eingesetzt werden, sondern
ob Ruby selbst eingesetzt wird.
IMHO nur sehr selten.


> werden durch Closures und gute Iteratoren-Unterstützung komplett platt
> gemacht, und durch einfachere und mächtigere Konstrukte ersetzt.
> Wie z.B. List Comprehensions in Python, über die du gerade eben was
> hättest lernen können, wenn du "zugehört" hättest:
> 
> > > Zuerst die Listen:
> > > 
> > > >>> liste1 = [2,5,66,2,3,45,2,1]
> > > >>> liste2 = [4,2,4,55,6,7,8,10]
> > > 
> > > >>> zip(liste1,liste2)
> > > [(2, 4), (5, 2), (66, 4), (2, 55), (3, 6), (45, 7), (2, 8), (1, 10)]
> > > 
> > > >>> for a,b in zip(liste1,liste2):
> > > >>>     print a+b,
> > > 6 7 70 57 9 52 10 11
> > 
> > OCaml's List.map gibt aber nicht nur aus, sondern kreiert eine neue Liste.
> 
> Diesen Rant hättest du dir komplett sparen können, wenn du einfach mal
> die nächste Zeile abgewartet hättest. Du hast sie nichtmal zitiert, also
> nochmal:

Nix Rant. Nur der Hinweis, das da was anderes passiert.
List.iter wäre das entsprechende Pendant.
(Oder einfach nur "iter", wenn man das Modul List mit "open" geöffnet hat
(das Interface komplett zugänglich gemacht hat).


[...]
> Und im Gegensatz zu funktionalen Sprachen funktioniert das auch

Was meinst Du hier mit Iteratoren?

Gib doch mal ein Beispiel.

Ohne beispiele kommen wir nicht weiter, weil wir sonst aneinander vorbei reden.
Das haben wir bereits mehrfach getan.


> hervorragend mit Iteratoren, nicht nur mit Listen. Und darin liegt dann
> nochmals eine enorme Stärke. Dass man mit Iteratoren fast genauso
> leicht wie mit Listen arbeiten kann, dafür sorgen übrigens Iterator-
> Generatoren, welche über Closures (d.h. einen "yield"-Ausdruck) verfügen
> (auch sehr ähnlich zu Ruby, haben sie sich - glaubich - von Icon
> abgeschaut).

Beipielcode + Beispielergebnis, bitte.



> 
> > Für Ausgaben (weil anderer Typ) müsste man List.iter (bzw. in dem Beispiel List.iter2)
> > nehmen.
> > 
> > Daß das ergebnis da oben schon steht liegt daran, daß ich das im Toplevel
> > eingetippert habe.
> > 
> > List.iter2 (fun a b -> print_endline (string_of_int(a + b))) liste1 liste2
> 
> Also da fände ich eine Schleife aber sehr viel übersichtlicher. Hier
> fängt man an, IMHO, die funktionalen Konzepte zu missbrauchen, um
> schlecht lesbaren Code zu produzieren.

Nein.

Die vielen Funktionen waren wegen unterschiedlicher Tyapen notwendig.

(Keine casts!)


Besser lesbar vielleicht so:

List.iter2 (fun a b -> Printf.printf "%d\n" (a + b)) liste1 liste2

Oder:

let my_sum_printer a b = Printf.printf "%d\n" (a + b)

List.iter my_sum_printer liste1 liste2





> 
> Man fängt nämlich an, Funktionen zu verweden an Stellen, wo eigentlich
> gar keine nötig sind.

Blödsinn.
Du hast nicht verstanden, wie mächtig funktionale Programmierung ist
und weil Du bei alt hergebrachtem bleiben magst (Schleifen),
meinst Du das wäre schlechte Programmierung, es mit List.iter zu erledigen.

Vielleicht wäre es besser, wenn Du bei OO bleibst und bei imperativem Code.
Den Sinn funktionaler Programmierung scheinst Du nicht zu begreifen.

Oder findest Du while/for loops einfach sexy?






> Egal, ob man sie nun inline definiert oder nicht,
> ist das unnötig schwer lesbar, IMHO.  In Python:
> 
> for a,b in zip(liste1,liste2):  print a+b
> 
> Aber wiegesagt, dass ist nur ein Standard-Rant von Python-Entwicklern.
> :-)

jaja.

Wie wäre es mal mit mehr Konstruktivem?


> > > Dasselbe geht übrigens nicht nur für Listen, sondern für alle
> > > Interatoren. Die zip-Funktion nimmt dann zwei Iteratoren und gibt
> > > einen neuen Iterator zurück. Und es gibt "Generator-Expressions"
> > > (seit Python 2.4), die im Prinzip genauso aussehen wie List-
> > > comprehensions, aber eben Iteratoren zurückgeben. Das hat enorme
> > > Vorteile, wenn es um große Datenmengen geht, weil zwischendurch
> > > keine einzige Liste erzeugt wird.
> > 
> > Hmhh, klingt so, als ob die bei Python noch nach sinnvoller Implementierung
> > suchen und suchen.
> 
> Nein, sie haben sie gefunden! Sie sind deswegen gerade dabei,
> funktionalen Overhead wie lamda, map, filter etc. zu verbannen und
> langfristig abzuschaffen, weil eben Listcomprehensions und Generator
> Expression einfacher und mächtiger sind.

Warum hat man dann bei Haskell diese Sachen noch nicht verbannt?
Du meinst wirklich, die sind nicht notwendig?

Tse.




> 
> Generator Expressions sind, wenn man so will, die naheliegende
> Verallgemeinerung von Listcomprehensions. Damit haben sie viele
> unnötig umständliche funktionale Überbleibsel mit einem Schlag
> erledigt.


Beispielcode?


[...]
> > > Ist in Python übrigens auch möglich. Aber man wendet das dort nur
> > > an, wenn's wirklich sinnvoll ist und zu übersichtlichem Code führt.
> > > Es wird nicht so überbetont.
> > 
> > Naja, FP führt IMHO zu sehr übersichtlichem Code.
> 
> Im Vergleich zu Java vielleicht. Aber bei Python ist das wirklich nur
> eine Frage deines konkreten Problems. Aber wiegesagt, man müsste
> jemanden haben, der mit beiden intensiv gearbeitet hat, um das
> einschätzen zu können.

Codeaussehen ist auch Geschmacksfrage. Aber die Sache mit der
Typprüfung ist fundamentalk im Finden von Fehlern.
Da kann ich mit Python nix anfangen.




> 
> > > In Python wird das konsistent gelöst. Dort sind Module, Klassen,
> > > Funktionen etc. alles im Wesentlichen Namespaces. Diese Denkart
> > > und Herangehensweise erschlägt nen haufen Probleme auf einmal.
> > 
> > Und? Gibt es da Funktoren?
> > Kannst Du ein Modul und ein weiteres Modul nehmen und eine
> > Funktion darauf ansetzen um ein neues Modul zu erzeugen?
> 
> Nö, ist aber auch nicht wirklich nötig. Aber mit Klassen und Funktionen
> kann man das, und dann braucht man das nicht mehr ganz oben auf Modul-
> Ebene.

Wozu Klassen?
Warum OO, wenn es auch ohne geht? :-)

Wir drehen uns im Kreise.

So macht das keinen Sinn.



[...]
> > Unter "Intent" steht dort auf S. 151:
> > 
> >    "Decouple an abstraction from it's implementation so that the two
> >     can vary independently."
> > 
> > Also, wenn ich das jetzt richtig verstehe (kannst ja korriegieren, wenn
> > es nicht stimmt), geht es darum, Interface und Implementierung trennen zu können.
> > Hmhhh. Wenn ich IMplemetierung und Interface voneinander trennen sollte,
> > habe ich mehrere Module mit unterschiedlicher Implementierung und
> > oräge dort allen eine einzige Signatur auf.
> > Dann kann man den Code verändern, aber das Interface bleibt gleich.
> 
> Keine Ahnung, was das soll. Du hast eben nur triviales Zeug von dir
> gegeben, und offenbar das Kapitel gar nicht gelesen.

OK: überflogen hab' ich's :)


> Nichtmal die
> Kurzbeschreibung hast du verinnerlicht:
> 
> 	"... can vary /independently/."

ja.

> 
> Es geht darum, was du machst, wenn es ein Interface gibt, viele
> Implementierungen, und dass einerseits neue Implementierungen
> hinzukommen (bzw. sich ändern), aber andererseits auch das Interface
> mit der Zeit immer mehr können muss.

ja.

Ich finde sowas immer am Besten an nem Beispiel diskutiert.


> 
> Sicher kannst du jetzt dem Interface neue Klassen/Funktionen hinzufügen,
> und das in jeder einzelnen Implementierung entsprechen hinzu coden.
> Genau das soll aber verhindert werden. Die GoF-Lösung, also das
> Brückenmuster, will ich hier jetzt nicht weiter ausführen, aber wenn
> man seinen Code umwirft und diesen Emüfehlungen folgt, hat man pro
> neuem Interface nur *einmal* was zu tun,

Ja, so habe ich das auch verstanden.


> und pro neuer Implementierung
> ebenfalls.

Ja.


> Der Aufwandt für beides bleibt also stets konstant, statt
> immer größer zu werden, je mehr das Projekt wächst.

Ja, so dachte ich es mir auch.

Konkret ausgetüftelt mit OCaml habe ich es mir noch nicht.

Vielleicht gehe ich die Design Patterns mal durch und schaue,
wie ich die in OCaml -- aber ohne OO, sonst wäre es ja genau das
selbe, was da im Buch steht -- kodiere.

Vielleicht sind manche der Patterns auch weiterhin als OO-Patterns überlebensfähig. ;-)

Habe ja nicht gesagt, alle Patterns werden erschlagen. Abr vieles kann man
mit FPL eleganter/einfacher lösen. Man muß da selbstverständlich die ganze Denke
umbauen und nicht nur bei einem Pattern die altbekannten Denkmuster ansetzen.

Also, mal kieken, vielleicht ist das ne nette Wochenendbeschäftigung mit diesen
Patterns auf non-OO-eise umgesetzt. :)


> 
> 
> Übrigens stehe ich gerade auch an so einem Problem, aber es ist gerade
> so vertrakt, dass ich das Brückenmuster nicht anwenden kann. Jedenfalls
> nicht so, wie es sich die GoF gedacht haben.

Tja, vielleicht mal andre Programmierparadigmen nutzen? ;-)


> Aus Effizienz-Gründen ist
> die "Brücke" bei mir etwas komplexer, eine Art Meta-Typ-System.

Sowas baust Du selbst?
Das gibt's doch alles schon. :->


> Blöd
> zu erklären, aber wenn ich's fertig habe, ist es auf jeden Fall sehr
> leicht anzuwenden.

Erklären ist meist schwer - ein Beispiel sollte her!




> 
> 
> > GGf. hat man ein Modul, das die anderen zusmmenfasst und dann
> > separat geändert wird. Man ruft von dort dann die jewiligen Funktionen
> > der Module auf, die man braucht.
> > 
> > Ist also schnell erledigt.
> 
> Was du beschreibst, ist der Austausch der Implementierungen. Schau mal
> unter "Abstract Factory", das beschreibt ungefähr das, was du gerade
> machst: Du hast mehrere Implemntierungen (z.B. Module) mit gleichem
> Interface, und willst verhindern, dass dein Programm sich direkt auf
> eine Implementierung bezieht. Das erreichst du durch eine
> Zwischenschicht. Bei dir ist es ein weiteres Modul, bei den GoF ist es
> eine weitere Klasse, eine "Factory"-Klasse.

Habe das alles im Schnellschuß gestern Abend abgelassen.
Vielleicht geht es sogar noch simpler.

Aber wenn ich bei "Abstract Factory" schaue, scheint das was anderes zu sein,
als Du hier meinst.

 "Provide an Interface for creating families of related or dependent
  objects without specifying their concrete classes."

Wenn ich das jetzt richtig zuordne (wie gesagt, selten OO-Zeugs in OCaml genutzt)
ist es das, was OCaml als "class type" bereits spracheigen mitliefert.


Schade, daß es - zumindest bei der BeLUG - kine interessanten SW-Projekte
gibt.
Wäre doch auch mal was feines, zusammen was leckeres zu programmieren. :)

Aber wenn es schon beim festlegen auf die Sprache solche Probleme gibt.

BTW: Werde diese Diskussion mal zum Anlaß nehmen,
     neben Postscript-Ausgabe auch LaTeX-Ausgabe des labscalgen zu generieren.
     Dann habe ich gleich mal ne nette Idee, das Modulsystem mal konkret
     zu nutzen.




> 
> Dies ist übrigens ein Muster, das in Smalltalk, Python etc. nicht mehr
> nötig ist, weil dort Module und Klassen ebenfalls wie Variablen (bzw.
> Objekte erster Stufe) behandelt und zugewiesen werden können. So merke
> ich mir in Python quasi in einer "Variablen", welche Implementierung
> ich gerade verwenden will.

Könnte man in OCaml machen, indem man den ode eines bestimmten Moduls auswählt.
Auch per Variable. So what?


So, ich kürze das mal ab hier, hole mir jetzt ein Entspannungs-Bierchen
und setze mich nachher nochmal an den labscalgen, bereite mal was vor bzgl.
untzerschiedlicher Ausgabe-Formate.

Ciao,
   Oliver



Mehr Informationen über die Mailingliste linux-l