[linux-l] Ocml vs. Java

Oliver Bandel oliver at first.in-berlin.de
Mi Sep 21 23:49:05 CEST 2005


On Wed, Sep 21, 2005 at 01:40:26PM +0200, Volker Grabsch wrote:
> On Tue, Sep 20, 2005 at 06:23:57PM +0200, Oliver Bandel wrote:
> > Na, das wage ich doch sehr zu bezweifeln, daß Java funktionale
> > Programmierung ermöglicht.
> 
> Das ist grundsätzlich falsch. In Java ist auch dies alles möglich, und
> wird auch eingesetzt.
> 
> > Kannst Du in Java Funktionen als Argumente und als Rückgabewerte haben?
> > Also Funktionen eine Funktion als Übergabeparameter geben?
> 
> Wenn du Objekte übergeben kannst und Polymorphie hast, kannst du
> letztlich auch Funktionen übergeben. Bei den GUI-Libraries (Event-Handling
> durch "Listener"-Objekte) wird das z.B. getan.
> 
> Also bitte kein Schubladendenken! Sowas ist peinlich: "Sprache-A ist in
> Kategorie-A, und unterstützt deshalb auf einen Fall Feature-B, das man
> von Sprachen der Kategorie-B kennt."
> 
> Aber das ist in Java unnötig umständlich, wie vieles andere auch, da
> stimme ich vollkommen zu.

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.
Sonst könnte man auch behaupten, es wäre in C möglich... immerhin kann
man da Pointer auf Funktionen erzeugen und diese übergeben.
Naja,a gut, wenn man das so machen will... aber dadurch ist C noch keine
funktionale Sprache.
Man kann in C auch OO-Programmierung machen. Aber dadurch wird C noch keine
OO-Sprache.

OK, da habe ich mich also unklar ausgedrückt.

Also Im Sinne von Ivan und Dir kann ich also sagen:
Klar, in ANSI-C kann man OO-Programmierung machen.

Ja, und nun?

Man kann aber in nicht-funktionalen Sprachen die Funktionen eben nicht
behandeln, wie andere Values (first class citizens).

Und darauf kommt es an!






[...]
> > 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?


> 
> Seit kurzem gibt es in Python die sog. "generator expressions".
> Diese lehnen sich syntaktisch an For-Schleifen und If-Anweisungen
> an, besitzen aber die gleiche Mächtigkeit wie map- und filter-
> Operationen, die man aus funktionalen Sprachen kennt. Generator-
> Expressions sind eigentlich sogar mächtiger, und bleiben dennoch
> übersichtlicher und leichter verständlich, wenn man sie verschachtelt.
> 
> 
> > # let func_1 a b = a * b;;
> > # let func_2 a b = a + b;;
> > # let liste = [2;5;66;2;3;45;2;1];;
> > # let liste_2 = [4;2;4;55;6;7;8;10];;
> 
> > # List.map2 func_1 liste liste_2;;
> > - : int list = [8; 10; 264; 110; 18; 315; 16; 10]
> 
> > # List.map2 func_2 liste liste_2;;
> > - : int list = [6; 7; 70; 57; 9; 52; 10; 11]
> 
> 
> Zuerst die Listen:
> 
> >>> liste1 = [2,5,66,2,3,45,2,1]
> >>> liste2 = [4,2,4,55,6,7,8,10]
> 
> Python hat zwar auch ein "map2"-Konstrukt, aber ich möchte auf simplere
> Konstrukte zurückgehen, bei dem ich keine unnötige Funktion definieren
> muss (aber natürlich könnte, wenn ich's brauche). Zunächst werden beide
> Listen zu einer Liste von Paaren vereinigt ("zip"):
> 
> >>> zip(liste1,liste2)
> [(2, 4), (5, 2), (66, 4), (2, 55), (3, 6), (45, 7), (2, 8), (1, 10)]
> 
> Diese könnte man in einer Schleife durchlaufen und die Summen ausgeben:
> 
> >>> 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.
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

(wenn ich beide Listen onsistent benenne; in meinem Bsp. hatte ich liste und liste_2 genommen)

Die Klammer mit dem (fun a v -> ...)
ist übrigens eine anonyme Funktionsdefinition, damit ich nicht extra die Ausgabe-Funktion
definieren muß.

Ansonsten wäre es:

let my_sum_printer a b = print_endline (string_of_int (a+b))

List.iter2 my_sum_printer liste1 liste2


(Wenn man will, kann man ja auch Printf.printf nehmen und dann auf
gewohnte Weise statt mit print_endline(string_of_int(...)) werkeln.




> 
> 
> Oder ich wende eine sehr ähnliche Syntax an, um das alles innerhalb von
> Listen zu machen, das sind die sog. Listcomprehensions:

Das kenne ich von Haskell (etwas andere Syntax).
Da haben die Python-Leute wohl ihre Inspiration her? ;-)


> 
> 
> 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.
Bei OCaml hat die Creme de la Creme der Informatik dran gearbeitet.
Da brauche ich nicht andauernd auf neue Features warten. Tun vielleicht andere
Leute, die aber uns alle in den Sack stecken würden...

Ein neues Feature bei OCaml sind recursive Modules.

Habe ich bisher noch nicht benutzt. Aber allem Anschein nach kann mir das
den einen Fall, wo ich doch mal OO nutzen wollte, erschlagen. :)
Na, dann nehme ich natürlich lieber dieses feature statt Objeckte, denn
dann kann man schon zur Compilierzeit alles festlegen, statt zur Laufzeit
unnötigen OO-Overhead zu haben. :)


> 
> Funktionale Konzepte vereinfachen zwar einiges, aber Python ist
> nicht starr bei ihnen geblieben, daher konnten sie dank sehr guter
> Iterator-Konzepte die meisten funktionalen Aktionen nochmal ordentlich
> vereinfachen.
> 
> 
> > Man übergibt func_1 bzw. func_2 als Argument an List.map2,
> > so, wie man auch die beiden liste als Argumente übergibt.
> 
> 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.


> 
> > Funktoren:
> > Man hat ein Modul mit allgemeiner Implementierung (z.B.
> > Balanced Tree). Man will dies auf einen speziellen Datentyp
> > hin spezialisieren.
> > Dann definiert man die speziellen Funktionen in einem eigenen
> > Modul, setzt den Funktor auf die beiden Module an und hat als
> > Ergebnis ein neues Modul!
> > 
> > Sozusagen ein Modul als Parameter eines anderen zur Erstellung eines
> > weiteren.
> > 
> > Extrem geniales Feature! :)
> 
> 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?





> 
> 
> > > - Objektorientierte Programmierung + Entwurfsmustern ist sehr mächtig
> > 
> > Funktionale Programmierung macht die meisten OO-Design-Patterns platt.
> > Man muß zwar umdenken, aber hat dann das meiste im Nu schon in der Kiste
> 
> Python macht diese Patterns auch platt, aber man muss weniger "umdenken".
> Pythons objektorientierten Features sind sehr weit entwickelt.

Die von OCaml auch.
Aber der Schwerpunkt ist OO in OCaml nicht.
Wozu auch? Braucht man ja kaum.
Außer bei GUIs vielleicht, da sind Objekte ja durchaus sinnvoll.


> 
> Grundsätzlich hast du natürlich recht: Die meisten Design-Patterns
> lösen Design-Probleme, die auf mangelnde Features der Sprache basieren.
> Im berühmten Buch der GoF wird z.B. an zahlreichen Stellen gesagt: Das

GoF ??

Für was steht das?


> ist zwar in C++ nötig, aber in Smalltalk gibt es Das-und-das, daher ist
> es dort kein Problem. (z.B. Prototyp-Muster oder Dekorator-Muster)
> 
> > und kann die Design-Patterns Literatur ins Museum bringen. ;-)
> 
> Diese Arroganz ist verfrüht.

Warten wir's ab. :)


> Es gibt nämlich trotzdem etliche Design-
> Patterns, gerade im GoF-Buch, die wirklich Software-Design betreffen,
> wo man wirklich in der Planung und dessen, was man implementiert,
> umdenken soll. Zum Beispiel geht es im Brücken-Muster um Sachen, die
> man eben nicht per Sprach-Feature erschlagen kann.

Kommt auf die gewählte Sprache und ihre Features drauf an. :)

Brücken-Muster soll wahrscheinlich das sein, was in "Design Patterns"
von Gamma/Helm/Johnson/Vlissides als Bridge bezeichnet wird.

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.

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.


> Dieses Muster ist
> in allen OO-Sprachen gleichermaßen wichtig und überall ähnlich
> umzusetzen, weil es eben /kein/ Workaround für ein fehlendes Sprach-
> Feature ist, sondern mehr eine Anleitung, wie man seinen Code
> organisiert.

In diesem Sinne ist es ja durchaus sinnvoll.
Aber wenn es nun doch ein Sprachfeature gibt, wa das unterstützt,
nur aber nicht in jenen Sprachen, wo man solche Patterns i.A. nutzt?!


> Du solltest nicht herablassend über Literator reden,
> die du ofensichtlich nicht gelesen hast.

Welche Literatur meinst Du denn?
Ich habe mich über keine Literatur herablassend geäußert.
Ich meinte nur, daß man die meisten Design Patterns nicht mehr braucht,
wenn man über den OO-Tellerrand hinaus schaut.
Man muß dann zwar konzeptionell anders dran gehen, erschlägt damit aber
vieles.

Wenn man eine Sprache hat, bei der man OO in den meisten Fällen nicht
 braucht (aber nutzen kann, wenn man es braucht), dann zeigt das die Stärke der Sprache.
Wenn man sie aber haupts. Vom bisherigen OO-denken her benutzen würde,
wäre es natürlich unbedingt notwendig, OO-Ansätze zu nehmen.
Im Vorteil ist man dann mit einer Sprache, die vieles vereint.


BTW: Adaptor-Pattern ist auch sehr interessant.
Im Pather-Buch (Perl) haben die das ja auch mal vorgeführt.

Nach o.g. Buche steht da unter Intent:
  "Convert the interface of a class into another interface clients expect.
   Adapter lets classes wpork together that couldn't otherwise because of
   incompatible intefaces."

Hmhh, mach' mir jetzt nicht die Mühe, den ganzen Kram zu lesen;
schaue mir im Panther-Buch die Grafik an und würde wieder sagen, daß OCaml's
Modulsystem einem hier hilft.
Ein Interface und mehrere spezialisierte Implementierungen. Das riehct nach
Anwendung von Funktoren.

Kann man sicherlich auch alles OO-mäßig machen.
Da würde ich vermuten, man nimmt Class-Types und stülpt die über
bestimmte Implementierungen drüber (bei Adaptor oder Bridge).
Aber das OO-Zeugs habe ich ja quasi nie gebraucht.

Aber solange ich etwas zur Compilierzeit machen kann, warum soll ich es dann
zur Laufzeit machen (aslo mit Objekten)?




[...]
> Nein, stattdessen werden in den "Mustern der Geschäftsschicht"
[...]

Was für Teile?


[...]
> und wegkapseln, wenn die Sprache Java flexibler wäre (in Python kann man
> das jedenfalls). Daher ist es auch kein Wunder, dass man große Projekte
> in Java ohne Codegenerator (besser: Redundanz-Generator) gar nicht
> sauber hinkriegen kann.
[...]

Aha, also auch verglichen mit Python kein Pluspunkt für Java...


Ciao,
   Oliver



Mehr Informationen über die Mailingliste linux-l