Warum Angst vor Abstraktionen mit komischen Namen?

Landei

Top Contributor
Als Ausgangspunkt soll einmal dieses Zitat dienen:

Code:
implicit def wrapToOps[T](x: T)(implicit monoid: AdditiveMonoid[T]): AdditiveMonoid[T]#Ops = new monoid.Ops(x)
<ironie>
Wow, Scala ist wahrlich ein Meilenstein für die Verständlichkeit und Nachvollziehbarkeit der Fachkonzepte aus dem Code
</ironie>

Auf den Scala-Aspekt will ich hier nicht eingehen, das wurde schon im dortigen Thread gut beantwortet. Es ist aber auffallend, dass ausgerechnet ein Beispiel ausgewählt wurde, das den "furchteinflößenden" Begriff "Monoid" verwendet.

Das es gut ist, in seinem Code Strukturen zu entdecken, die sich verallgemeinern lassen, sollte eigentlich klar sein: Jede "entdeckte" Abstraktion ist eine Chance, wiederverwendbaren Code entweder zu schreiben oder zu nutzen.

Ein Monoid ist ein sehr einfaches Konzept: Wir haben eine assoziative Operation # (also [c]a # (b # c) == (a # b) # c)[/c]) mit einem neutralen Element e (dass bei Veknüpfung mit einem anderen Element dessen Wert nicht ändert). Monoide sind z.B. die natürlichen Zahlen bezüglich der Addition und Multiplikation, aber auch die Verkettung von Strings oder Listen(*). Hat man diese Abstraktion einmal erkannt, kann man z.B. eine [c]List<T>[/c] zu einem einzelnen Wert [c]T[/c] "eindampfen" (oder "falten"), egal welche Art von Monoid über [c]T[/c] man benutzt. Eigentlich eine praktische Sache.

Also: Wieso lassen sich Programmierer so leicht von mathematisch angehauchten Namen abschrecken? Irgendwie muss man die Strukturen ja nennen, und es wäre Blödsinn, eine eigene Nomenklatur zu verwenden, wenn es schon eine allgemein akzeptierte, konsistente Benennung gibt. Wieso werden so schnell die Scheuklappen aufgesetzt, wenn ein Wort wie "Monoid", "Funktor", "Arrow" oder dreikreuzederherrseibeiuns "Monade" fällt?

(*) Mehr Beispiele: Monoid (Functional Java 3.0)
 
Zuletzt bearbeitet:

schalentier

Gesperrter Benutzer
Ich glaub es geht da weniger um das Wort "Monoid", als vielmehr um das Verstaendnis dieses Konstrukts. Ich hab mir das ne Weile angesehen, aber als Scala-Nicht-Kenner versteh ich ueberhaupt nicht was das sein soll und wozu es gut ist. Dieser Effekt, dass man einige Statements ohne tieferes Wissen ueber die Sprache auch nicht annaehernd verstehen kann, macht Scala in gewisser Weise einzigartig.

Und ich rede jetzt nicht von der Syntax oder der Formatierung (man kann auch Java so kryptisch schreiben, dass es niemand versteht), sondern von der Semantik.

Die andere Sache ist, dass der normale Entwickler wohl eher selten das Verlangen hat, einen neuen Monoiden zu programmieren. Meisten rechnet man mit Zahlen, iteriert ueber Listen und konkatiniert Strings. Mehr braucht man selten und damit fehlt auch das Verstaendnis, wieso man nun mit Scala ploetzlich mit Monoiden, Funktoren oder Faltungen rummachen muss.
 

Landei

Top Contributor
Wie gesagt soll das hier keine Scala-Diskussion werden, deshalb nur soviel: Der obige Code ist ein typisches Beispiel für das Scala-Typklassen-Pattern. Hier ein praktisches Beispiel: Algorithmically challenged: Type Class pattern example

...wieso man nun mit Scala ploetzlich mit Monoiden, Funktoren oder Faltungen rummachen muss.
Müssen muss niemand, auch in Scala nicht.

In Java werden sogar die einfachsten Abstraktionsmöglichkeiten verpasst: Wieso hat man [c]List.size()[/c], [c]String.length()[/c] und [c]array.length[/c]? Wieso implementiert [c]String[/c] nicht [c]Iterable<Character>[/c]? Wieso implementiert [c]StringBuilder[/c] nicht [c]List<Character>[/c]? Die Liste ließe sich endlos fortführen. Das Problem ist, dass man einerseits zehn verschiedene Wege (oder Methodennamen) lernen muss, um prinzipiell das Gleiche zu tun, aber andererseits die vorhandenen Methoden deshalb nicht so flexibel sind, wie sie sein könnten. Bin ich der einzige, der das doof findet?
 
B

Beni

Gast
Ich möchte nicht, dass jede Klasse 20 Interfaces implementiert und 300 Methoden hat... Bei Eiffel wurde das so gemacht (ein Fenster ist ein Rectangle, und eine Liste von Componenten, und und und). Resultat: flexibel, aber so extrem unübersichtlich dass es praktisch nicht zu gebrauchen ist.
 

Marco13

Top Contributor
NEIN. Viele dieser Punkte sind mir auch schon aufgefallen. Ein Beispiel, das in die gleiche Kategorie fällt, sind StringBuilder und StringBuffer: Für ein paar 'synchronized's eine neue Klasse?! :autsch:

Ich glaube auch, dass viele Leute sich lange über bestimmte Strukturen und Ansätze Gedanken machen. Dann kommen sie zu irgendeinem Ergebnis, und setzen es um. Aber ein ähnliches oder sogar besseres Ergebnis hätte man erreichen können, wenn man sich pragmatischer an die Vorgaben der rein mathematischen Beschreibung gehalten hätte. Mathematik ist eine Sprache für gute Beschreibungen, und anscheinend wird viel zu selten erkannt, dass die Algebra sie eigentliche formale Grundlage der Objektorientierten Programmierung ist.

Oder anders: Man muss vielleicht nicht die Definition von "Monoid" auswendig wissen (da könnte man gleich noch nach Gruppe, Ring und Körper fragen - darum geht es ja nicht) aber wer sich "Informatiker" schimpft, sollte schon wissen, dass das eine (einfache) algebraische Struktur ist - und wenn nicht, hat derjenige IMHO viel nachzuholen...

Ich fand z.B. sowas wie org.jscience.mathematics.structure (JScience 5.0-SNAPSHOT API) vom Ansatz her toll, aber erstens nicht konsequent und systematisch genug umgesetzt, und zweitens nicht so "mächtig", wie es sein könnte, wenn dieser Aspekt systematischer in der Standard-API berücksichtigt worden wäre.

Aber ich nehme an, dass dein Einwand sich nicht nur auf die existierenden, schon mathematisch definierten Strukturen an sich bezog, sondern, wie im Titel angedeutet, um allgemeine Abstraktionen - also die Definition eigener Algebren. Über ähnliche Aspekte hatte ich mir auch schon oft Gedanken gemacht. Und ich habe immer Angst, dass, wenn man mögliche Abstraktionen ausnutzt, der entstehende Code (primär von anderen) nicht mehr so gut nachvollzogen werden kann.

Man könnte sagen, dass ein weiterer Grundgedanke der OOP ja der ist, irgendwelchen Dingen Namen zu geben. Und wenn diese Namen wegen "zu viel Abstraktion" wegfallen, und jeder Typ nur noch eine lose Ansammlung von Mengen, Funktoren, Tupeln, Gruppen und Prädikaten ist, erleichtert das das Verständnis nicht unbedingt. Etwas pauschalisiert könnte man wohl sagen, dass es einen Trade-Off gibt, zwischen der Flexibilität und Allgemeingültigkeit, die die Mathematik an sich innehat, und der einfachen Verbindung zwischen Code und Objekten in der "realen" Welt.

Aber ich versuche zumindest eher in Richtung der allgemeineren Beschreibung zu gehen. Wenn jemand anderes das dann nicht mehr versteht... tja...
 

Landei

Top Contributor
size und length sind 2
Wie kommst du auf 10 :lol:

Das reicht auch schon. Ohne nachzuschlagen: Heißt es [c]File.length()[/c] oder [c]File.size()[/c]? Heißt es [c]javax.swing.text.Document.length()[/c] oder [c]Document.size()[/c]? Reingefallen, es ist [c]Document.getLength()[/c]!
 
Zuletzt bearbeitet:
S

SlaterB

Gast
und dann auch getSize() in ListModel ;)

aber das ist kein rechter Angriffspunkt, das sind zum Teil separate Libraries, unabhängig von der Sprache,
oder nach Jahren von verschiedenen Programmierer-Generationen hinzugefügt,
dass dort nicht alles verregelt ist ist eine gewisse Entspannung,

es hilft nix, komplizierte Neuerungen haben es immer schwer
 

Landei

Top Contributor
...aber das ist kein rechter Angriffspunkt, das sind zum Teil separate Libraries, unabhängig von der Sprache...

Ich finde es aber ärgerlich, wenn z.B. eine Bibliotheksklasse kein [c]Iterable[/c] implementiert, wo das sinnvoll wäre. In Haskell würde eine Bibliothek, die Instanzen für wichtige Typklassen definieren könnte, das aber nicht tut, dafür mit Sicherheit kritisiert und u.U. nicht akzeptiert werden. Diese Einstellung fehlt mir bei den imperativen Sprachen.

Ein Grund dafür ist, ist, dass durch Implementierung bekannter Interfaces o.ä. auch leichter zu sehen ist, was eine Klasse tut. Wenn ich z.B. eine add-Methode habe, ändert sie dann das Objekt, oder liefert ein neues zurück? Und nein, das kann man nicht sofort am Rückgabewert sehen (man denke z.B. an [c]StringBuilder.append[/c]). Eine Abstraktion erfüllt also auch eine wichtige Dokumentations-Funktion.
 

0x7F800000

Top Contributor
Nja, 'tschuldigung: ich wollte in dem Beispiel mit einer 0 anfangen, und alle Elemente einer Liste mit + addieren, dazu brauchte ich eben eine Struktur mit 0 und +, soll ich die jetzt "GridBagLayout" oder "Helmut" nennen, damit's vertrauter klingt? :bloed:

Ich glaub es geht da weniger um das Wort "Monoid", als vielmehr um das Verstaendnis dieses Konstrukts. Ich hab mir das ne Weile angesehen, aber als Scala-Nicht-Kenner versteh ich ueberhaupt nicht was das sein soll und wozu es gut ist.
Ich find's wirklich so lustig: wenn zB. bei Python fürchterliche Innereien in C/C++ geschrieben, und dann mit dem Python-Brei übergossen werden, dann sagen alle: "oooh hat das eine schöne Syntax", und kein Mensch beschwert sich über die fürchterlichen Innereien, und darüber, dass man mit der Sprache an sich eigentlich nichts anstellen kann, außer irgendwelche Altlasten aufzuhübschen.

Kaum entwirft jemand eine Sprache, wo sowohl die fürchterlichen Bibliothek-Innereien, als auch die benutzerfreundlichen DSL's implementiert werden können, geht das Geheule los, es sei doch "Alles so kompliziert!", und "Wie soll ich denn bloß nach Mallorca fliegen, ohne eine Flugzeugturbine zusammenbauen zu können?!". Scala vereint alles: es ist gut für die Benutzer der Bibliotheken, und es ist gut für die Designer der Bibliotheken. Wenn man sich als Benutzer versteht, dann sollte man sich aus dem Design der Bibliotheken einfach erstmal raushalten. Oder es einfach lernen: es ist nämlich ein riesen Spaß. ;)

Die andere Sache ist, dass der normale Entwickler wohl eher selten das Verlangen hat, einen neuen Monoiden zu programmieren. Meisten rechnet man mit Zahlen, iteriert ueber Listen und konkatiniert Strings. Mehr braucht man selten und damit fehlt auch das Verstaendnis, wieso man nun mit Scala ploetzlich mit Monoiden, Funktoren oder Faltungen rummachen muss.
"Normaler Entwickler" muss über alle Äpfel im virtuellen Einkaufskorb gehen, und den float-Preis der Äpfel aufaddieren, und ist dann glücklich. Okay. Ich mag auch online-Shops und konsumiere gerne Äpfel, und bin den "normalen Entwicklern" außerordentlich dankbar, wenn der Preis am Ende korrekt berechnet ist. Ich muss aber mit doubles, floats, rationalen Zahlen, konstruierbaren zahlen, Galois-Feldern, p-Adischen Zahlen, komplexen Zahlen, Cayley-Zahlen, Oktonionen, elliptischen Kurven, tropischen Ringen, algebraischen Körpererweiterungen, Ganzheitsringen, inversen Limiten von Idealen, Polynomen, Multivariaten Polynomen, Reihen, Multivariaten Reihen, Matrizen von dem ganzen Kram, allgemeinen Linearen Operatoren zwischen dem ganzen Kram, Computertomographie-Phantomen, Masse- oder Ladungsdichten, Zufallsvariablen, Stochastischen Prozessen, Maßen, sowie natürlich allen möglichen Datenstrukturen von all dem rechnen können, und ich will stets hingehen und
Code:
sum(seq)
hinschreiben können, und zwar OHNE hundert mal die [c]sum(seq)[/c]-methode neuimplementieren zu müssen. Und da fängt man schon an, sich Gedanken über die richtige Abstraktionen zu machen.

Ich möchte nicht, dass jede Klasse 20 Interfaces implementiert und 300 Methoden hat...
Warum nicht? Das ist doch Frage der Dokumentation, nicht die Frage der zu komplexen Syntax. Ist es denn besser, die 300 methoden jedes mal neu zu implementieren?

Aber ein ähnliches oder sogar besseres Ergebnis hätte man erreichen können, wenn man sich pragmatischer an die Vorgaben der rein mathematischen Beschreibung gehalten hätte. Mathematik ist eine Sprache für gute Beschreibungen, und anscheinend wird viel zu selten erkannt, dass die Algebra sie eigentliche formale Grundlage der Objektorientierten Programmierung ist.
[...]
Ich fand z.B. sowas wie org.jscience.mathematics.structure (JScience 5.0-SNAPSHOT API) vom Ansatz her toll
Was die Anwendung der mathematischen Definitionen bei der Programmierung angeht, ist Vorsicht geboten. Ich habe schon mehrmals versucht, eine Bibliothek (in Scala) aufzubauen, indem ich mir einfach nur ein dickes Algebra-Buch genommen habe, und einfach alle Strukturen nacheinander als Traits skolemisiert habe. Es ist sowas wie in dem Link angedeutet herausgekommen, allerdings nicht mit 8 Interfaces, sondern mit ~60 Traits. Es war nicht wirklich praktisch, aus folgenden Gründen:

1) In der Mathematik rechnet man oft mit Objekten herum, von den anfangs nicht ganz klar ist, welche Struktur sie eigentlich tragen: ist beispielsweise Z/(8220625327)Z ein Körper, oder lediglich ein Ring mit Nullteilern? Soll man elemente solchen Dings als "Ring" oder "Field" initialisieren, um damit rumzurechnen? Während man damit rumrechnet, führt man gerne (beweisbar gültige) Teilungen durch, auch wenn man im Ring ist, also müsste das Ring-interface eigentlich auch inverse beinhalten, aber wodurch unterscheiden sich dann Ringe von Körpern bei der Implementierung?

2) Um irgendwelche strukturierten Teilmengen zu erkennen, ist das Typsystem nicht mächtig genug. Beispielsweise bilden invertierbere quadratische Matrizen gleicher Größe eine wunderbare Gruppe, aber allgemeine matrizen kann man nicht mal vernünftig multiplizieren: krampfhafte Versuche, da irgendeine mathematisch korrekt definierte Struktur drüberzuziehen sehen nicht gut aus.

3) Hirnlose Skolemisierung der mathematischen Definitionen führt nicht zu brauchbaren schnittstellen. Beispiel Graphen: Mathematisch ist ein Graph G = (V, E) eine menge von Knoten blah blah blah... Es ist aber schwachsinnig, das ganze wie folgt zu einem interface zu machen:
Code:
interface Graph[V, E]{
  Collection[V] getNodes()
  Collection[E] getedges()
}
Denn Graphen (auch wenn sie im mathematischen Sinne endlich sind usw.) müssen überhaupt nicht in den Speicher reinpassen, und müssen all ihre Knoten nicht kennen: man denke an riesige backtracking-Bäume oder Webcrawler. Das Interface, welches für einen Wegsuch-algo sinn macht, sieht in diesem fall völlig anders aus, als die mathematisch einwandfreie, aber unbrauchbare Skolemisierung.

Man muss da also schon mit viel Gefühl rangehen, wie genau versuche ich herauszufinden. :rtfm:
 
Zuletzt bearbeitet:

schalentier

Gesperrter Benutzer
Ich muss aber mit doubles, floats, rationalen Zahlen, konstruierbaren zahlen, Galois-Feldern, p-Adischen Zahlen, komplexen Zahlen, Cayley-Zahlen, Oktonionen, elliptischen Kurven, tropischen Ringen, algebraischen Körpererweiterungen, Ganzheitsringen, inversen Limiten von Idealen, Polynomen von dem ganzen Kram, Multivariaten Polynomen von dem ganzen Kram, Reihen von den ganzen Kram, Multivariaten Reihen von dem ganzen Kram, Matrizen von dem ganzen Kram, allgemeinen Linearen Operatoren zwischen dem ganzen Kram, Computertomographie-Phantomen, Masse- oder Ladungsdichten, Zufallsvariablen, Stochastischen Prozessen, Maßen, sowie natürlich allen möglichen Datenstrukturen von all dem rechnen können, und ich will stets hingehen und
Code:
sum(seq)
hinschreiben können, und zwar OHNE hundert mal die [c]sum(seq)[/c]-methode neuimplementieren zu müssen. Und da fängt man schon an, sich Gedanken über die richtige Abstraktionen zu machen.

Auch wenn mir vieles davon nix sagt, hast du schonmal versucht dein Problem mit einer Sprache ohne statisches Typsystem umzusetzen? Deine sum(seq) Methode saehe in Ruby z.B. so aus:
Code:
def sum(initial_value, seq)
  seq.inject(initial_value) do |result, s|
    result + s
  end
end

Das klappt dann fuer alle Listen, Arrays, Enumerations, etc. mit Objekten drin, die die Methode "+" implementiert haben. Nur so ne fixe Idee...
 

Landei

Top Contributor
Geht bestimmt, ist aber für ernsthafte numerische Aufgaben zu langsam. Scala hätte als Äquivalent strukturelle Typen anzubieten ("für alle Typen, die eine Methode foo(Bar) haben, tue...", siehe Pimp my Library eSCALAtion Blog ), die aber auch nicht gerade für ihre Performance berühmt sind.
 
Zuletzt bearbeitet:

0x7F800000

Top Contributor
Auch wenn mir vieles davon nix sagt, hast du schonmal versucht dein Problem mit einer Sprache ohne statisches Typsystem umzusetzen? Deine sum(seq) Methode saehe in Ruby z.B. so aus:
Code:
def sum(initial_value, seq)
  seq.inject(initial_value) do |result, s|
    result + s
  end
end

Das klappt dann fuer alle Listen, Arrays, Enumerations, etc. mit Objekten drin, die die Methode "+" implementiert haben. Nur so ne fixe Idee...
1) Klassisches Argument gegen Duck-Typing: man merkt erst zur Laufzeit, dass irgendwas schief gelaufen ist. In diesem konkreten Fall könnte ich mir beispielsweise vorstellen, dass schon sehr bald mehrere Benutzer sich über folgendes seltsames Verhalten wundern könnten:
Code:
sum([[1,2,3],[4,5,6],[7,8,9]]) //liefert [1,2,3,[4,5,6],[7,8,9]] statt [12,15,18]
Hier will man 3 listen in einer liste komponentenweise addieren. Eine Liste könnte nun "+" implementieren, allerdings nicht das "+" einer Additiven Gruppe, sondern ein "+" zum hinzufügen der Elemente, was dazu führen könnte, dass die nachfolgenden listen als elemente angehängt werden. Die fehlermedung würde dann vielleicht hundert zeilen später auftauchen, wenn man versucht, den mittelwert solch einer liste auszurechnen o.ä. Ich bin mir ziemlich sicher, dass Python sowas abfangen sollte, bei Ruby weiß ich es nicht. Ich kenne allerdings genug jämmerlich zusammengeschusterte Skript-Sprachen, die sich ungefähr so bescheuert benehmen würden.

2) das "klassische OOP" strebte ursprünglich danach, daten und methoden zu vereinen. Das macht aber nicht immer unbedingt Sinn. Ich möchte vielleicht mit Double-daten rumrechnen, aber einmal mit der üblichen Körperstruktur, und anderes mal wie in einem tropischen ring mit Operationen (max, +) statt (+, *). Eher denkbar: man möchte mit longs rumrechnen, allerdings nicht mit üblichen Z/(2^64)Z-Ring, sondern in einem Z/pZ-Körper. Da man nur bei statisch typisierten Sprachen so etwas wie implizite paremeter übergeben kann (bei dynamisch typisierten weiß der kompiler ja nicht, was verlangt ist), müsste man die Struktur explizit übergeben. Das mag bei so einfachen methode gehen, wird aber bei komplizierteren aufgaben mir 2-3 Strukturen auf Dauer evtl. stressig.

3) Für mich eigentlich das wichtigste: dank der statischen Typisierung kann man den scala-compiler dazu zwingen, aus einfachen Formeln komplizierte Algorithmen zur compile-zeit zusammenzubauen. Beispiel: Seien A, B, C, D... matrizen
Wenn man sowas schreibt:
Code:
val C = A * B
val Z = C * D
dann stellt man beim genauen hinschauen fest, dass man anhand der ersten zeile allein nicht feststellen kann, ob die matrix C in row-major, col-major, oder im gehashten-Assemblier-Zustand vorhanden sein muss. Man muss also Auswertung von C auf später verschieben. Nun wird in der zweiten Zeile C an (sagen wir mal dichte) matrix D dranmultipliziert, jetzt ist klar: C muss in rowMajor gespeichert werden, damit die zeilen/spalten korrekt in den Cache reinpassen und nicht umkopiert werden müssen. Doch wie wertet man das aus? Haufen if-Abfragen bei jeder Multiplikation? Könnte man machen, aber scala-compiler erledigt das schon zur Kompilezeit, und zwar indem es mithilfe von impliciten casts die zweite Zeile zu sowas wie
Code:
val Z = evalInRowMajor(C)*convertToColMajor(D)
umbaut. Der Compiler wählt mir anhand harmlos aussehenden Formeln also die passenden Algorithmen. Das gibt's imho bei gar keiner anderen Sprache, nicht mal bei C/C++ oder Java.

Das fehlen dieser Fähigkeit merkt man schon bei deinem Beispiel: bei deiner version muss man den startwert mit übergeben, weil der compiler eben die Lücken mit dem passenden Struktur nicht ausfüllen kann.

4) [EDIT] Ok, Performance habe ich hier gar nicht erwähnt: klar, um etwas bei diesen ganzen Skriptsprachen schnell zu machen, muss man es in C/C++ schreiben. Wenn ich den s***** eh in C/C++ schreiben muss, wozu brauch ich dann die blöden Skriptsprachen überhaupt?
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Was die Anwendung der mathematischen Definitionen bei der Programmierung angeht, ist Vorsicht geboten. Ich habe schon mehrmals versucht, eine Bibliothek (in Scala) aufzubauen, indem ich mir einfach nur ein dickes Algebra-Buch genommen habe, und einfach alle Strukturen nacheinander als Traits skolemisiert habe. Es ist sowas wie in dem Link angedeutet herausgekommen, allerdings nicht mit 8 Interfaces, sondern mit ~60 Traits. Es war nicht wirklich praktisch, aus folgenden Gründen:

1) In der Mathematik rechnet man oft mit Objekten herum, von den anfangs nicht ganz klar ist, welche Struktur sie eigentlich tragen: ist beispielsweise Z/(8220625327)Z ein Körper, oder lediglich ein Ring mit Nullteilern? Soll man elemente solchen Dings als "Ring" oder "Field" initialisieren, um damit rumzurechnen? Während man damit rumrechnet, führt man gerne (beweisbar gültige) Teilungen durch, auch wenn man im Ring ist, also müsste das Ring-interface eigentlich auch inverse beinhalten, aber wodurch unterscheiden sich dann Ringe von Körpern bei der Implementierung?

2) Um irgendwelche strukturierten Teilmengen zu erkennen, ist das Typsystem nicht mächtig genug. Beispielsweise bilden invertierbere quadratische Matrizen gleicher Größe eine wunderbare Gruppe, aber allgemeine matrizen kann man nicht mal vernünftig multiplizieren: krampfhafte Versuche, da irgendeine mathematisch korrekt definierte Struktur drüberzuziehen sehen nicht gut aus.

3) Hirnlose Skolemisierung der mathematischen Definitionen führt nicht zu brauchbaren schnittstellen. Beispiel Graphen: Mathematisch ist ein Graph G = (V, E) eine menge von Knoten blah blah blah... Es ist aber schwachsinnig, das ganze wie folgt zu einem interface zu machen:
Code:
interface Graph[V, E]{
  Collection[V] getNodes()
  Collection[E] getedges()
}
Denn Graphen (auch wenn sie im mathematischen Sinne endlich sind usw.) müssen überhaupt nicht in den Speicher reinpassen, und müssen all ihre Knoten nicht kennen: man denke an riesige backtracking-Bäume oder Webcrawler. Das Interface, welches für einen Wegsuch-algo sinn macht, sieht in diesem fall völlig anders aus, als die mathematisch einwandfreie, aber unbrauchbare Skolemisierung.

Man muss da also schon mit viel Gefühl rangehen, wie genau versuche ich herauszufinden. :rtfm:

Ich gehe davon aus, dass es keinen Sinn machen würde, eine Programmiersprache 1:1 auf Mathematischen Strukturen auzubauen. Schon allein weil viele Konstrukte zu speziell sind, und wenig wirklich "praktische" Awendungsbereiche haben. Zugegeben, ich hatte schonmal was ähnliches in Erwägung gezogen, wie du: Hierarchie mathematischer Strukturen ? Wikipedia und los geht's :smoke: Es geht ja nicht um ein Computeralgebrasystem, das genau für solche Dinge da ist, sondern nur darum, existierende Strukturen auszunutzen. Und mit Monioden, Gruppen, Ringen und Körpern hat man schon den überwiegendsten Teil davon abgedeckt - die Herausforderung würde dann nicht darin bestehen, auch ja unbedingt noch den Quasikörper und den Integritätsbereich einzubauen, oder zu Erkennen, dass diese-und-jene Struktur mit einer zusätzlichen 'null' nun zu dieser-und-jender Struktur wird, sondern darum, diese wenigen, wichtigen Strukturen konsequent und gut (d.h. "viele Vorteile bringend") umzusetzen.

Das Beispiel des Graphen ist aber schon fast klassisch, und darüber (und einige andere Aspekte, die hier im Thread diskutiert werden - deswegen ist dieser Backlink vielleicht sogar ganz praktisch) hatte ich schon philosophiert, als ich den durch immer weiter gehende (speziell auch mathematische) Abstraktion drohenden http://www.java-forum.org/allgemeine-java-themen/116618-abstraktions-overkill.html mal angesprochen hatte. Ich fände, dass es durchaus einen Haufen Vorteile hätte, einen Graphen als
Tuple<Set<Object>, Set<Tuple<Object,Object>>> g;
zu beschreiben. Aber wenn das jemand sieht, der... (neutral formuliert) "eine andere Einstellung zu solchen Dingen" hat, und entweder das verwenden kann, oder stattdessen
Graph g;
schreiben kann, wird er sich tendenziell eher für letzteres entscheiden.

Leicht OT, weil ganz spezifisch darauf bezogen: Was wäre an dem angedeuteten Interface denn so "falsch" oder "unbrauchbar"? Abgesehen davon, dass man Set#size u.U. recht schwer implementieren kann, hat man durch die Interface an sich ja immernoch die Möglichkeit, dort vieles z.B. "lazy" zu implementieren, und sinngemäß einen Iterator beim Aufruf von "next" halt eine Webseite aufrufen zu lassen (rein theoretisch). Anders formuliert: "Ein Graph IST EIN Tupel aus einer Menge von Knoten und einer Menge von Kanten..." - wenn man etwas anderes implementiert, dann IST das - polemisch formuliert - eben kein Graph mehr....
 

0x7F800000

Top Contributor
Ich gehe davon aus, dass es keinen Sinn machen würde, eine Programmiersprache 1:1 auf Mathematischen Strukturen auzubauen.
Nein, denselben Satz von mathematischen Konstrukten, wie in der theoretischen Mathematik zu nehmen, und zu versuchen, das alles in API reinzuquetschen, würde keinen Sinn machen, weil Rechner einfach nicht so funktionieren, wie der unbegrenzte mathematische Kosmos. Rechner haben nun mal endlichen Speicher, endliche Geschwindigkeit, es gibt Haufenweise wohldefinierte aber unberechenbare Funktionen, Rechner können keine Instanzen aus nicht konstruktiven Existenzbeweisen herbeizaubern usw usw...

... unbedingt noch den Quasikörper und den Integritätsbereich einzubauen ...
Weiß nicht... Aus Integritätsbereichen kann man eigentlich ganz gut Quotientenkörper bauen, und mit Quotientenkörpern kann man tolle LGS lösen usw. Zum Beispiel kann man aus euklidischen Polynomringen den Körper der rationalen Funktionen erzeugen, und damit ganz gut rumrechnen, auch wenn's nicht das schnellste ist... Integritätsbereiche sind gar nicht so abwegig ;)

Ich fände, dass es durchaus einen Haufen Vorteile hätte, einen Graphen als
Tuple<Set<Object>, Set<Tuple<Object,Object>>> g;
zu beschreiben. Aber wenn das jemand sieht, der... (neutral formuliert) "eine andere Einstellung zu solchen Dingen" hat, und entweder das verwenden kann, oder stattdessen
Graph g;
schreiben kann, wird er sich tendenziell eher für letzteres entscheiden.
Nja, was soll daran schlecht sein:
Code:
type Graph[V] = (Set[V], Set[(V, V)])
beides muss sich ja nicht ausschließen, vorausgesetzt die Syntax macht mit ;)

Allgemein bei Interfaces muss man irgendwie darauf achten, dass man nicht ins unendlich abstrakte & völlig unbrauchbare abdriftet. Ich bemühe mich deshalb darum, bei Traits das richtige Gleichgewicht zwischen vorgeschriebenen Methoden und gebotener Funktionalität zu halten. Einfach nur Tausend Interfaces zu liefern, an die sich der Benutzer zu halten hat, ohne dass es ihm was nützt, ist zum Scheitern verurteilt, auch wenn diese ganzen Interfaces aus algebraischer Sicht sauber definiert sind.

Leicht OT, weil ganz spezifisch darauf bezogen: Was wäre an dem angedeuteten Interface denn so "falsch" oder "unbrauchbar"?
naja, um Breitensuche, Tiefensuche (in allen Varianten), kürzeste Wege-Algos, aufzählung der Zusammenhangskomponente eines Knoten, aufzählung der d-entfernten Nachbarschaft, test ob ein anderer knoten erreichbar ist usw: diese ganzen Algorithmen funktionieren bestens mit sehr lokaler Information, sie müssen eigentlich immer nur einen einzelnen Knoten sehen, und zu seinen Nachbarn springen können. Alle Knoten und Kanten zu sehen ist nicht nötig (und oft nicht möglich, auch wenn's theoretisch endlich viele sind). Und es gibt in der Praxis eben sehr viele Fälle, wo man gerne graphenalgos einsetzen würde, ohne jemals alle Knoten aufzählen zu können:
1) tankstelle in der Nähe suchen, ohne die ganze Weltkarte runterzuladen
2) kliquen im engen Freundeskreis bei facebook suchen, ohne ganzes facebook runterzuladen
3) backtrackings aller art auf fürchterlich großen Graphen (es gibt sehr viele Graphen, die zeitlich in unsere 4D-Welt gut reinpassen, aber niemals zur selben Zeit im Speicher Platz finden können)
4) Untergruppen-zugehörigkeits probleme, Rubic's cube lösen o.ä...

bei all diesen problemen macht es keinen Sinn, die Aufzählung aller Knoten zu verlangen. Natürlich gibt es Graphen, die alle ihre Knoten kennen und Algorithmen, die das brauchen. Aber das ist dann schon ein speziellerer Unter-Trait.

Auf den Punkt gebracht:

mathematische Definition abschreiben != die algorithmisch entscheidenenden Eigenschaften erkennen

Zum schreiben der Beweise benötigt man das erstere.
Zum schreiben des Codes benötigt man das letztere.
 
Zuletzt bearbeitet:

Marco13

Top Contributor
OK, zugegeben, die Frage was daran "unbrauchbar" ist, war ein bißchen rethorisch :oops: Tatsächlich braucht man die reine Aufzählung der Knoten nicht oder selten. (Auch wenn, nur weil dort "Set<V>" steht, das ja nicht heißen muss, dass die Knoten ALLE im Speicher liegen - es kann ja eine quasi-implizite Beschreibung der Knoten sein).

Aber es stimmt schon: Die ALLERmeisten Graphenalgorithmen brauchen massivst genau eine Funktion, nämlich: "Gibt mir alle Kanten, die an dem Knoten hängen" (Die Differenzierung zwischen ein- und ausgehenden Kanten, oder die darauf aufbauend in gleicher asymptotischer Zeit zu erreichender Suche nach allen Nachbarn mal außen vor gelassen). Für praktische Zwecke braucht man also IMMER noch eine Methode wie
Iterable<Edge> getEdges(Vertex v);
Sowas schreibt sich sowohl steng mathematisch als auch in einem Graph-Interface (zumindest in der naivsten Form) recht leicht hin - sei jetzt mal egal, ob mit "Edge" oder "Tuple<V,V>". Es ist aber nicht inheränter Teil der Definition eines Graphen. Es kann theoretisch auf Basis des Graphen in seiner angedeuteten, minimalistisch-abstraktesten Form leicht berechnet werden: Über alle Kanten gehen, und schauen, ob 'v' einer der Endpunkte ist - mit einer einfachen Utility-Methode. Und es müßte rein strukturell deswegen nicht Teil eines Graph-Interfaces sein, sondern wird nur aus Effizienzgründen mit dort aufgenommen. Warum? Weil der Graph eben üblicherweise sowas wie eine Map<Vertex, List<Edge>> speichert, in der diese Information leicht nachgesehen werden kann. Und das ist ... doch eigentlich nur eine Funktion, die Vertices auf Listen von Edges abbildet. Eigentlich wäre es doch schön, wenn man diese Information dann nicht als höchst-spezifische Methode in einer höchst-spezifischen Graph-Klasse beschreiben würde, sondern eben genau als
Function<V, Set<Tuple<V,V>>> edgesOfVertices;
Damit könnte man all die magischen Sachen machen, z.B. diese Funktion auf triviale Weise zu einer Funktion erweitern, die einen Knoten auf seinen Grad abbildet, oder auf die Menge seiner Nachbarknoten - alles Dinge, die oft praktisch sind, und durch so ein Konstrukt, dank der Abstraktion, "aus dem (fast) nichts heraus" von ganz alleine bekommt.

Aber das führt dann, konsequent weitergedacht, eben zum angedeuteten Overkill, wo man nur noch mit Sets, Tuples und Functions rumhantiert, und durch reines Lesen nicht mehr erkennbar ist, ob ein Programm eine rohe Breitensuche in einem Graphen durchführt oder die Bestellannahme eines Pizzaservices regelt :autsch: Wie im anderen Thread (und oben) schon angedeutet geht damit ja eine der Kernideen von OOP verloren, nämlich dass Strukturen sprechende (und vereinfacht gesagt: aus der realen Welt stammende) Namen haben. Ich denke, speziell bei sowas abstrakt-mathematischem wie Graphen ist das nicht so schlimm, und die Vorteile überwiegen deutlich, zumal um die allgemeinen Strukturen ja immernoch spezifischere Business-Strukturen drumrumgewickelt werden können... aber es ging ja ursprünglich um die allgemeine Frage, wie viel Abstraktion man einem Programmierer zumuten oder abverlangen kann ;)

Meine ersten Gehversuche in Scala waren übrigens ... der Versuch, einer Graph-Klasse :eek: weil ich dachte, dass es erstens eine tolle Übung wäre, und zweitens sowas damit ja aus den angedeuteten Gründen und in der angedeuteten Form total cool und allgemein umsetzbar sein müßte - und ich bin kläglich gescheitert :oops: ... da braucht's wohl viel, viel mehr Übung und Versiertheit, um da etwas in diesem Sinne vernünftiges machen zu können...
 
Zuletzt bearbeitet:

0x7F800000

Top Contributor
Aber es stimmt schon: Die ALLERmeisten Graphenalgorithmen brauchen massivst genau eine Funktion, nämlich: "Gibt mir alle Kanten, die an dem Knoten hängen"
so ist es.

Es ist aber nicht inheränter Teil der Definition eines Graphen.
Doch, man kann (auch gerichteten) Graphen G als eine Funktion f: V -> 2^V definieren (rechts steht die Potenzmenge), das wird dann völlig äquivalent zu der anderen Definition, nur eben wesentlich schöner für die Implementierung.

Und das ist ... doch eigentlich nur eine Funktion, die Vertices auf Listen von Edges abbildet.
jaaa...?
Eigentlich wäre es doch schön, wenn man diese Information dann nicht als höchst-spezifische Methode in einer höchst-spezifischen Graph-Klasse beschreiben würde, sondern eben genau als
Function<V, Set<Tuple<V,V>>> edgesOfVertices;
Was heißt "höchst spezifisch"? Eine solcher Graph-Trait hätte doch eben nur eine abstrakte methode (und dann vielleicht noch sowas wie einen "Startknoten" oder Menge von Startknoten, wo man irgendwelche Suchen anfangen kann) so irgendwie:
Code:
trait Graph[V]{
  // abstrakt
  def neighbors(x: V): Iterable[V]

  // das evtl überschreiben
  def sccRepresentants: Iterable[V] = new Set[V]()

  // alles andere lässt sich ja durch abstrakte methoden ausdrücken
  def degree(x: V) = neighbors(x).size

  // ... 150 weitere nützliche methoden, etwa:
  // search(start, predicate):X
  // aStar(heuristic, predicate):X
  // bfs(x, predicate)
  // ...
}

Wenn man sowas aus Funktion basteln möchte, bitte sehr:
Code:
implicit def functionToGraph[V](f: V => Iterable[V]) = new Graph[V]{
  def neighbors(x: V) = f(x)
}

Wenn man die neighbors Funktion aus dem graphen heraustrennen möchte, bitte sehr:
Code:
val nodes = List("Marco13", "Landei", "L-ectron-X")
val firstNeighbors = for(n <- nodes) yield g.neighbors(name).head

val separateFunction = (s: String) => g.neighbors(s)
Wo ist das Problem?

Damit könnte man all die magischen Sachen machen, z.B. diese Funktion auf triviale Weise zu einer Funktion erweitern, die einen Knoten auf seinen Grad abbildet, oder auf die Menge seiner Nachbarknoten - alles Dinge, die oft praktisch sind, und durch so ein Konstrukt, dank der Abstraktion, "aus dem (fast) nichts heraus" von ganz alleine bekommt.
Nja, aber einen ziemlich fetten Graph-Trait bekommt man eben auch durch implementierung von gerade mal einer einzigen Methode, und schon kann man damit Wegsuche machen und sonst was anstellen... Wozu soll man das denn als kryptische Function[Blah, Blah]-Dinger implementieren und die funktionalität irgendwo verstreuen?

Aber das führt dann, konsequent weitergedacht, eben zum angedeuteten Overkill, wo man nur noch mit Sets, Tuples und Functions rumhantiert, und durch reines Lesen nicht mehr erkennbar ist, ob ein Programm eine rohe Breitensuche in einem Graphen durchführt oder die Bestellannahme eines Pizzaservices regelt :autsch:
Warum? Man bastle eben ein recht abstraktes Graph Trait, und verwende es dann in der Pizza-Service. Alles benannt, alles klar, alles toll.

Weißt Du was, irgendwie habe ich das Gefühl, als ob du durch das Fehlen der higher-order-functions und closures in Java zu irgendwelchen merkwürdigen Workarounds abdriften würdest, als ob du geistig schon in einer funktionalen Sprache denken, aber physisch noch in Java coden würdest ;)

Vor dem Umstieg auf Scala ging es mir so: ich habe plötzlich angefangen, haufenweise Klassen wie "Tuple, Pair, Function, LazyMappedSequence, MappedFunction" usw. in java zu implementieren und sie an jeder Stelle zu benutzen, bis mir aufgefallen ist, dass ich eigentlich kein Java mehr schreibe, sondern lediglich java-syntax zur simulation einer funktionalen Sprache missbrauche :D

[EDIT] *im tagebuch rumblätter* :D
Genau habe ich damals folgende Hilfsmittel in Java implementiert:
MapFunction, MappedIterable, MultiIterable, EmptyIterator, MappedIterator, MultiIterator, Function, Identity, Inclusion, Memoization, Pair, Square, VisitorProcess, IntRange, LongRange, LongCounter...

also lauter so Sachen, die irgendwie Funktionales Programmieren imitieren sollten :oops:
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Dass es verschiedene äquivalente mathematische Beschreibungen geben kann, die bei "direkt-naiver" Umsetzung unterschiedlich gut implementieren lassen (oder auch abhängig von der Art des Graphen unterschiedlich effizient sind - z.B. bei "dünn besetzen" Graphen vs. vollständige Grapen) kommt zu dem ganzen noch dazu. Aber mit Interfaces und abstrakten Beschreibungen hält man sich für die Implementierung ja zum Glück gerade diese Türen so lange wie möglich offen.


Was heißt "höchst spezifisch"?
...
Wenn man sowas aus Funktion basteln möchte, bitte sehr:
...
Wenn man die neighbors Funktion aus dem graphen heraustrennen möchte, bitte sehr:
...
Wo ist das Problem?
Die erste Richtung geht mit Java genauso einfach. Die zweite kann (mit Java, und wohl auch mit Scala) etwas komplizierter sein, je nachdem, wie viele weitere abstrakte Methoden jemand unüberlegterweise(?) in dieses Interface mit reingepackt hat. Natürlich hat man meistens noch diese Option des "Einwickelns". Man kann auch bei
Java:
class Graph {
    int getNumVertices() { ... }
    Vertex getVertex(int i) { ... }
}
daraus eine "List<Vertex>" machen: AbstractList erweitern, und die 1,2 abstrakten Methoden auf die beiden vorhandenen umbiegen und gut. Aber ich hätte einen Vorteil der allgemeinen Beschreibung (GROB im Sinne von
Java:
class Graph {
    Collection<Vertex> getVertices(){ ... }
}
oder gar das schon angedeutete Tuple<Collection<Vertex>, ...>, an dem man gar nicht mehr sieht, dass es ein Graph ist!) eben darin gesehen, dass man dieses Einwickeln und Umbiegen gar nicht mehr braucht.


Weißt Du was, irgendwie habe ich das Gefühl, als ob du ... geistig schon in einer funktionalen Sprache denken, aber physisch noch in Java coden würdest ;)
Genau das Gefühl habe ich auch :( Ich will und brauche mehr funktionale Aspekte, und merke selbst, dass ich tendenziell immer häufiger anonyme Implementierungen einzelner Interfaces für solche "Umbiege-Operationen" verwende. Eigentlich hatte ich gehofft, dass das bei Scala weniger notwendig sein könnte, aber deinen Ausführungen nach scheint es lediglich besser... "antizipiert" zu sein :bahnhof: Ich finde den Einstieg in Scala trotzdem schwer... (mal schauen, was in http://www.java-forum.org/plauderecke/123807-yes-scala-hard.html so neues steht :D ). Hatte auch schonmal Goovy ins Auge gefasst. Dazu kommt , dass ich gerne von anfang an etwas sinnvolles machen würde, man aber vermutlich sicher sein kann, dass man von allem, was man anfangs in Scala schreibt, wenige Monate später erkennt, was für ein crap es eigentlich war :autsch: Die zur Verfügung stehenden Mittel gut zu nutzen erfordert so viel Übung, dass vorher erstmal viel Zeit vermeintlich verbrannt wird...
 
N

nillehammer

Gast
Marco13 hat gesagt.:
Dazu kommt , dass ich gerne von anfang an etwas sinnvolles machen würde, man aber vermutlich sicher sein kann, dass man von allem, was man anfangs in Scala schreibt, wenige Monate später erkennt, was für ein crap es eigentlich war Die zur Verfügung stehenden Mittel gut zu nutzen erfordert so viel Übung, dass vorher erstmal viel Zeit vermeintlich verbrannt wird...
Das Gefühl hab ich auch, wenn ich mir meinen Java-Code von früher anschaue... was sag ich, das Gefühl hab ich, wenn ich mir meinen Java-Code von vor ein paar Monaten anschaue! Man sollte meinen, dass ich es nach 7 Jahren endlich kann. Ist aber nicht so... Egal, ich bin trotzdem ein super Programmierer! :toll:
 
G

guest2512

Gast
Als Ausgangspunkt soll einmal dieses Zitat dienen:



Auf den Scala-Aspekt will ich hier nicht eingehen, das wurde schon im dortigen Thread gut beantwortet. Es ist aber auffallend, dass ausgerechnet ein Beispiel ausgewählt wurde, das den "furchteinflößenden" Begriff "Monoid" verwendet.

Das es gut ist, in seinem Code Strukturen zu entdecken, die sich verallgemeinern lassen, sollte eigentlich klar sein: Jede "entdeckte" Abstraktion ist eine Chance, wiederverwendbaren Code entweder zu schreiben oder zu nutzen.

Ein Monoid ist ein sehr einfaches Konzept: Wir haben eine assoziative Operation # (also [c]a # (b # c) == (a # b) # c)[/c]) mit einem neutralen Element e (dass bei Veknüpfung mit einem anderen Element dessen Wert nicht ändert). Monoide sind z.B. die natürlichen Zahlen bezüglich der Addition und Multiplikation, aber auch die Verkettung von Strings oder Listen(*). Hat man diese Abstraktion einmal erkannt, kann man z.B. eine [c]List<T>[/c] zu einem einzelnen Wert [c]T[/c] "eindampfen" (oder "falten"), egal welche Art von Monoid über [c]T[/c] man benutzt. Eigentlich eine praktische Sache.

Also: Wieso lassen sich Programmierer so leicht von mathematisch angehauchten Namen abschrecken? Irgendwie muss man die Strukturen ja nennen, und es wäre Blödsinn, eine eigene Nomenklatur zu verwenden, wenn es schon eine allgemein akzeptierte, konsistente Benennung gibt. Wieso werden so schnell die Scheuklappen aufgesetzt, wenn ein Wort wie "Monoid", "Funktor", "Arrow" oder dreikreuzederherrseibeiuns "Monade" fällt?

(*) Mehr Beispiele: Monoid (Functional Java 3.0)
OK, jetzt du hast den Monoid erklärt. Aber das geht am Haupkritikpunkt vorbei, denn der Rest dieser Zeile ist immer noch mehr als kryptisch.
Also noch mal kurz zur Erklärung:
Code:
implicit def wrapToOps[T](x: T)(implicit monoid: AdditiveMonoid[T]): AdditiveMonoid[T]#Ops = new monoid.Ops(x)
Das hinterlässt für mich als Java-Programmierer nichts als WTFs:
1. WTF will diese Methode mit 2 verschiedenen Parameterlisten? Wird sie dann so aufgerufen, oder wie?
Code:
wrapToOps(x)(m);
Warum wird nicht die klassische Form
Code:
wrapTpOps(x, m);
verwendet?
2. WTF ist "implicit"? Nach 3 Seiten Google bin ich noch kein bisschen schlauer. Irgendwas mit Default-Werten für Parameter. Wenn dem so ist, dann halte ich das von der Lesbarkeit her ziemlich krank. Hätte man stattdessen nicht eher sowas hier machen können?
Code:
wrapToOps[T](x: T,  @Default{new Person("John  Doe")} p: Person)
Und eine API-Dokumentation wie bei Java scheint für Scala wohl in dieser Form überhaupt nicht zu existieren?
3. WTF gibt diese Methode zurück?
Code:
...): AdditiveMonoid[T]#Ops = new monoid.Ops(x)
ein Object vom Typ AdditiveMonoid[T] gekreuzt mit ... häh?
Also gibt diese Methode irgendwas anderes zurück, als das, was im Methodenkörper returned wird?
Warum wird der Rückgabewert nicht im Methodenkörper "gekreuzt"?

Ich finde, es sind doch genau solche unverständlichen Spielereien sind ein Hauptgrund, warum im Unternehmseinsatz niemand Scala so wirklich ernst nehmen kann.

Denn mal ehrlich: oben schreibst du was von Abstraktion und Wiederverwendbarkeit, und dann präsentierst du Code, der vor "Coolheit" (Übersetzung: Ich sch*** auf Nachvollziehbarkeit und Lesbarkeit, hauptsache ich zeige allen, was für coole Sachen ich drauf habe, und dass ich mit Scala 10 verschiedene Sachen in eine Zeile quetschen kann) nur so strotzt.
 

Marco13

Top Contributor
Egal, ich bin trotzdem ein super Programmierer! :toll:

Es gibt Leute, die genau das nicht zuletzt daran festmachen würden ;) Nichts ist schlimmer, als jemand, der bei prozeduralem for,if+arrays hängengeblieben ist, und das dann ewig weiterführt und nichts dazulernt (außer vielleicht jemand, der bei OO hängengeblieben ist, und damit eine funktionale Sprache nachbaue... ooops :oops: ). Oder ganz provokant: Wenn man sich etwas ansieht, was man vor 5 Jahren gemacht hat, und nicht weiß, was man damals hätte besser machen können, dann hatte man es entweder schon damals richtig drauf (und kaum noch Luft nach oben) oder... man hat etwas ganz, ganz falsch gemacht :D
 

0x7F800000

Top Contributor
Das hinterlässt für mich als Java-Programmierer nichts als WTFs
Es ist eine praktisch völlig andere Sprache als Java, es ist von der Gesamtphilosophie bis ins kleinste Detail von Java verschieden. Wieso erwartest du, dass du auch nur eine Zeile davon verstehst? Glaubst du etwa, dass es auf dieser Welt nichts außer C, C++, C-Derivaten, kastrierten C-inspirierten Skriptsprachen und "C mit bisschen Garbage Collection" gibt? Da irrst Du dich leider gewaltig. Ich habe paar tausend Zeilen Code in Prolog geschrieben, ich habe meine Hausaufgaben in Lisp und Scheme gemacht, ich habe mir die ML-Spec durchgelesen, ich habe ein wenig mit Haskell rumgespielt, und ich habe praktisch von Anfang bis Ende das Buch von Odersky durchgelesen: und jetzt versteh ich es größtenteils. Wenn Du das nicht gemacht hast: dein Problem, ich wünsche dir vom ganzen Herzen, dass du das nachholen kannst. Aber erwarte bitte nicht, dass du nach "3 Seiten Google" Scala verstehst, oder Chinesisch sprichst, oder Differentialgleichungen im Kopf lösen kannst.

1. WTF will diese Methode mit 2 verschiedenen Parameterlisten? Wird sie dann so aufgerufen, oder wie?
Code:
wrapToOps(x)(m);
Korrekt.
Warum wird nicht die klassische Form
Code:
wrapTpOps(x, m);
verwendet?
implizite parameter werden in einer separaten parameterliste übergeben, damit man in der ersten Liste mit expliziten parametern beim letzten Argument eine "vararg"-Liste übergeben kann. Für implizite Parameter ist diese letzte Stelle zu schade, weil man die meistens eh nicht sieht.

2. WTF ist "implicit"?
implicit ist nicht für default-parameter. Die Default-parameter werden einfach wie folgt spezifiziert:
Code:
def myFunction(x: Int = 123, s: String = "ein default-String") = {...}
1) Mit implicit werden einfach diejenigen funktionen, variablen oder objekte markiert, die vom Kompiler stillschweigend in die implicit-parameterlisten eingefügt werden können. Dies ist oftmals praktisch, wenn man irgendeine "Struktur" oder "Algorithmus" nicht bei jedem Funktionsaufruf explizit übergeben will. Man sehe sich zum Beispiel folgendes Beispiel an, in dem man irgendwelche Punkte mit zwei Int-Koordinaten zuerst nach der ersten Komponente, danach nach der zweiten Komponente vergleichen möchte:
Java:
Comparator<Pair<Int>> intPairCmp = new Comparator<Pair<Int>>(){
  @Override public int compare(Pair<Int> a, Pair<Int> b){
     // irgendwie vergleichen
  }
}

// Anwendungsbeispiel: sortieren
List<Pair<Int>> listeMitPaaren = //irgendwie füllen
MeineSuperschnellenSortieralgos.sort(listeMitPaaren, intPairCmp) // comparator explizit übergeben
bei Scala würde man dagegen Ordering[(Int, Int)] (analogon von Comparator<Pair<Int>>) eher als implicit definieren und dann vom compiler in die 2. parameterliste von [c]sort[/c] einfügen lassen:
Code:
implicit val intPairOrd = new Ordering[(Int,  Int)](){
  override def compare //usw...
}

val listeMitPaaren = //irgendwie füllen
MeineSuperschnellenSortieralgos.sort(listeMitPaaren) // ord wird vom compiler Automatisch eingefügt
Bei sowas wie Sortierung fällt's weniger ins Gewicht, bei kurzen Methoden wie [c]<[/c] oder [c]>[/c] aber schon eher.

2) implicit wird benutzt, um benutzerdefinierte implicite casts durchzuführen. Beispiel:
Code:
class Complex(re: Double, im:Double) {...viele lustige Rechenoperationen mit komplexen zahlen...}

// diese methode castet einfache doubles zu complex
implicit def doubleToComplex(d: Double) = Complex(d, 0)

// anwendung
val I = Complex(0, 1)
val a = 1.3 + 5.6 * I 
val b = a * 34.5
// statt 
// val b = a * Complex(34.5, 0)
Anmerkung: bei Complex würde man sich eher die mühe geben, und alle rechenoperationen für doubles nochmal separat implementieren, dieses Beispiel ist unrealistisch, aber kurz.

Code:
 gekreuzt mit ... häh?
Das ist Zugriff auf innere Klassen, bei java wird stattdessen einfach Punkt gschrieben:
Java:
new Rectangle2D.Double(...)
Die Syntax [c]Outer.Inner[/c] vs [c]Outer#Inner[/c] bedeutet in Scala in etwa dasselbe, wie
Java:
class Outer{
  class Inner{}
}
vs
Java:
class Outer{
  static class Inner{}
}
in Java: man legt damit also fest, ob der Typ durch die Instanz oder durch die Klasse (also statisch) parametrisiert wird.
Also gibt diese Methode irgendwas anderes zurück, als das, was im Methodenkörper returned wird?
Nein. Die Methode
Code:
implicit def wrapToOps[T](x: T)(implicit monoid: AdditiveMonoid[T]): AdditiveMonoid[T]#Ops = new monoid.Ops(x)
garantiert, dass sie einen (nur von der monoid-klasse abhängigen) [c]AdditiveMonoid[T]#Ops[/c] liefern wird, schnappt sich den implizit übergebenen Monoiden, und instantiiert mit seiner Hilfe eine Ops-Instanz. Wenn es dich nur verwirrt, dann schreib die Methode so hin:
Code:
implicit def wrapToOps[T](x: T)(implicit monoid: AdditiveMonoid[T]) = new monoid.Ops(x)
Der Typ wird doch eh inferiert...

Ich finde, es sind doch genau solche unverständlichen Spielereien sind ein Hauptgrund, warum im Unternehmseinsatz niemand Scala so wirklich ernst nehmen kann.
"Spielereien" habe ich bei Scala noch keine Entdeckt: kenne keinen Professor, der seinen Job ernster nehmen würde. Diese Sprache ist nicht irgendsoein Weihnachtsunfall oder planlose Anhäufung von Features, sondern die klarste und intrinsisch mathematischste Sprache, die ich kenne. Implizite parameter sind keine "Spielereien", sondern ein fundamental neuartiges Werkzeug, mit dem man zum einen viele hässliche details verbergen, und [wie im letzten Beitrag unter Punkt 3) beschrieben], Algorithmen zur Compile-Zeit automatisch zusammenbauen lassen kann.

Denn mal ehrlich: oben schreibst du was von Abstraktion und Wiederverwendbarkeit, und dann präsentierst du Code, der vor "Coolheit" (Übersetzung: Ich sch*** auf Nachvollziehbarkeit und Lesbarkeit, hauptsache ich zeige allen, was für coole Sachen ich drauf habe, und dass ich mit Scala 10 verschiedene Sachen in eine Zeile quetschen kann) nur so strotzt.

Die Code-Zeile ist praktisch 1:1 aus der Scala-API übernommen: Scala Standard Library 2.9.1.final (siehe unter Numeric die Numeric.scala source-file, Zeile 23). ich habe lediglich den Großen Numeric-Trait auf ein kleines Additive-Monoid heruntergebrüht, um's kurz zu halten. Es handelt sich bei dieser Zeile um ein Scala-Spezifisches, sehr oft benutztes Design. Wenn man sich paar Dutzend von solchen impliciten Casts angeschaut hat, erscheint diese Zeile auf Anhieb kurz, effektiv und kristallklar. Daher habe ich keine negativen Emotionen verspüren können, von den Du berichtest.

Wie ich neulich herausgefunden habe, kann ich mich auf genau dieses Konstrukt verlassen, um viele algorithmen für floats, doubles, ints, BigDecimals, Rational[Int], rational[Long], Rational[BigInt] usw. in generischer Art und weise zu implementieren: das ist eine enorme Arbeitsersparnis, ansonsten hätte ich vieles zehnmal separat schreiben müssen (aus Performancegründen wohlbemerkt, nicht weil es die Syntax nicht erlaubt: dass JVM das alles so gut wegoptimiert fand ich erst gestern heraus). Wenn das nicht der Wiederverwendbarkeit des Codes dient, dann weiß ich auch nicht...
 
Zuletzt bearbeitet:

spaghetti

Mitglied
Darf man mal fragen, wo ihr sowas in der Praxis verwendet? Rein fürs Studium? Ist garnicht böse gemeint, ich bin noch einigermaßen frisch auf dem Gebiet und ich kann euch hier nicht im Ansatz folgen...
 
G

gu816est

Gast
Wenn du mit Praxis die Anwendung in industriellen Software-Projekten meinst: so gut wie gar nicht. Scala ist recht "cool", um zu zeigen was theoretisch möglich ist. Ist aber ansonsten eine rein akademische Geschichte. Denn:

1. Es existiert so gut wie keine vernünftige IDE-Unterstützung. Die Plugins für die großen IDEs sind ein Krampf.
2. Besteht ein Softwareprojekt aus weit mehr als nur Code einhämmern. Was Scala bei der Programmierung erleichtert, macht es in den anderen Disziplinen und Tätigkeiten wieder kaputt, z.B. dem Testing, Refactorings (siehe IDE-Support), ...
3. Bestehen große Projekte aus TEAMS und mehreren hundert Entwicklern. Team bedeutet Kommunikation, u.a. auch mittels für alle verständlichen Code-Artefakten. Und nicht eine Masse von Einzelkämpfern, in der jeder sich durch möglichst kryptischen und "coolen" Code von den anderen abheben will.
4. Versucht Scala krampfhaft "die" eierlegende Wollmilchsau zu sein. Java ist u.a. so erfolgreich geworden, weil es eben eine klare und gut lesbare Grundsyntax hat, und nicht 100 verschiedene Konzepte auf engstem Raum komprimieren will.
 

Landei

Top Contributor
Wenn du mit Praxis die Anwendung in industriellen Software-Projekten meinst: so gut wie gar nicht. Scala ist recht "cool", um zu zeigen was theoretisch möglich ist. Ist aber ansonsten eine rein akademische Geschichte. Denn:

1. Es existiert so gut wie keine vernünftige IDE-Unterstützung. Die Plugins für die großen IDEs sind ein Krampf.
2. Besteht ein Softwareprojekt aus weit mehr als nur Code einhämmern. Was Scala bei der Programmierung erleichtert, macht es in den anderen Disziplinen und Tätigkeiten wieder kaputt, z.B. dem Testing, Refactorings (siehe IDE-Support), ...
3. Bestehen große Projekte aus TEAMS und mehreren hundert Entwicklern. Team bedeutet Kommunikation, u.a. auch mittels für alle verständlichen Code-Artefakten. Und nicht eine Masse von Einzelkämpfern, in der jeder sich durch möglichst kryptischen und "coolen" Code von den anderen abheben will.
4. Versucht Scala krampfhaft "die" eierlegende Wollmilchsau zu sein. Java ist u.a. so erfolgreich geworden, weil es eben eine klare und gut lesbare Grundsyntax hat, und nicht 100 verschiedene Konzepte auf engstem Raum komprimieren will.

1. An der IDE-Unterstützung wird intensiv gearbeitet. Mit dem Netbeans-Plugin bin ich inzwischen einigermaßen zufrieden, das Eclipse-Plugin hat große Fortschritte gemacht.
2. Völliger Blödsinn. Scala-Testframeworks sind besser als z.B. JUnit. Weniger Code lässt sich leichter Refactorn. Class-Datei-basierte Tools (wie z.B. FindBugs) arbeiten problemlos mit Scala zusammen
3. Organisatorische Probleme lassen sich nicht mit Sprachen lösen. Scala bietet aber die Möglichkeit, z.B. Frameworks als DSLs wesentlich freundlicher zu gestalten und so auch weniger erfahrene Programmierer "mitzunehmen". Cooler und kryptischer Code ist kein Problem, wenn er hinter einer guten API einer Bibliothek versteckt ist.
4. Wie "erfolgreich" Java's Beschränkung ist, sieht man an der riesigen Wolke an Bibliotheken, die versuchen, dessen Unzulänglichkeiten auszugleichen: Apache Commons, Spring, Guice, diverse XML-Bibliotheken, OSGi und so weiter, aber wohl am deutlichsten an den ganzen Ansätzen, die den Sprachumfang erweitern und/oder automatisch Code generieren (AspectJ, ASM, EMF..).

Der Vorwurf der "akademischen" Sprache ist so dumm, dass ich eigentlich nicht darauf antworten möchte. Ich verweise nur auf die Kompromisse, die zwecks Kompatibilität zur JVM eingegangen wurden, und welche Anstrengungen unternommen wurden, um eine gute Performance zu sichern. Und etwas abzulehnen, nur weil es aus dem akademischen Umfeld stammt, ist ziemlich kurzsichtig.

Ich finde es übrigens schade, dass wir schon wieder beim Scala-Bashing angelangt sind, auch wenn ich das ausdrücklich in meinem Eingangspost verhindern wollte. Solche Threads haben wir schon genug, und ich finde, die Frage nach dem Umgang mit Abstraktionen ist interessanter, als schon wieder die immer gleichen Schallplatten der "Seid doch mit Java zufrieden"-Fraktion anhören zu müssen.
 
Zuletzt bearbeitet:
B

bygones

Gast
1. An der IDE-Unterstützung wird intensiv gearbeitet. Mit dem Netbeans-Plugin bin ich inzwischen einigermaßen zufrieden, das Eclipse-Plugin hat große Fortschritte gemacht.
man kann auch nicht erwarten, dass eine junge Sprache von anfang eine vollstaendige und komplette Unterstuetztung durch eine IDE bekommt.

3. Organisatorische Probleme lassen sich nicht mit Sprachen lösen. Scala bietet aber die Möglichkeit, z.B. Frameworks als DSLs wesentlich freundlicher zu gestalten und so auch weniger erfahrene Programmierer "mitzunehmen". Cooler und kryptischer Code ist kein Problem, wenn er hinter einer guten API einer Bibliothek versteckt ist.
Cooler und kryptischer Code kann auch mit Java erstellt werden, auch wenn (wahr. fuer den unbekannten betrachter) Scala mehr dazu einlaedt. Und richtig, die kommunikation in einem Team, welche groesse es auch hat, hat nix mit einer Sprache zu tun.

4. Wie "erfolgreich" Java's Beschränkung ist, sieht man an der riesigen Wolke an Bibliotheken, die versuchen, dessen Unzulänglichkeiten auszugleichen: Apache Commons, Spring, Guice, diverse XML-Bibliotheken, OSGi und so weiter, aber wohl am deutlichsten an den ganzen Ansätzen, die den Sprachumfang erweitern und/oder automatisch Code generieren (AspectJ, ASM, EMF..).
da uebertreibst du nun meiner Ansicht nach. Die Anzahl von Erweiterungen eines Systems hat nicht per se mit seiner Unzulaenglichkeit zu tun, sondern eher mit der Moeglichkeit das zu tun. Ebenso ist es ein falscher Ansatz, dass ein "Team" die Probleme der Welt loest. Java hat definitiv seine Schwaechen und manche Sachen sind schlecht realisiert, aber Erweiterungen als Unzulaenglichkeiten bzw Beschraenkheit hier aufzuzaehlen ist falsch.
Allein weil du keinen unterschied machst zwischen Java und vom JDK ?

Der Vorwurf der "akademischen" Sprache ist so dumm, dass ich eigentlich nicht darauf antworten möchte.
Es ist noch eine akademische Sprache und ich bin auch der Meinung die Entwicklung ist akademisch getrieben. Das ist per se nichts schlimmes, der Fokus liegt einfach bei einem anderen. Ob Scala daraus noch waechst wird die Zeit zeigen - ich bin gespannt

Ich finde es übrigens schade, dass wir schon wieder beim Scala-Bashing angelangt sind, auch wenn ich das ausdrücklich in meinem Eingangspost verhindern wollte.
und jeder wusste wieviel das bringen wird ;-)

zu deinem eigentlichen Thema:
ich meine du hast aus voellig falschem Verstaendnis das Thema hier aufgemacht. Derjenige der sich ueber diese unleserliche Codezeile mockiert hat tat das nicht, weil ihm das Abstraktionsvermoegen fehlte, sondern weil er der Meinung war die Zeile ist einfach nicht leserlich.

Und da hat er vollkommen recht, sie ist in keiner Weise lesbar. Viele Codezeilen aus alten Sprachen sind, auch wenn man sie nicht kann, doch meist etwas zu entziffern. Ich denke ich scheitern einfach die meisten. Ob das nun am Betrachter, an dieser bestimmten Codezeile, oder eben an Scala an sich weiss ich nicht.
 

0x7F800000

Top Contributor
Darf man mal fragen, wo ihr sowas in der Praxis verwendet? Rein fürs Studium? Ist garnicht böse gemeint, ich bin noch einigermaßen frisch auf dem Gebiet und ich kann euch hier nicht im Ansatz folgen...
Ist "Rein für's Studium" irgendwie "wenig"? Es gibt dutzende Sprachen, die "rein für's Studium" bzw. für Forschung gedacht sind, gug dir mal die ganzen Matlabs und R's an. Ich studiere Mathe, hauptsächlich Numerik und Stochastik, ich brauche irgendeine Sprache, in der ich die Algorithmen implementieren kann, weil ich sie ansonsten nicht verstehe. Matlab's und R's sind imho für nichts zu gebrauchen, weil sie keine einzige minimale Anforderung für's "not sucking" erfüllen: es sind keine "Programmiersprachen", sondern einfach nur extrem aufgeblähte Riesentaschenrechner aus den 80'er Jahren. Java ist dagegen eine Programmiersprache, die diesen Namen auch verdient, ist aber aufgrund der geschwätzigen Syntax für meine Probleme ungeeignet. Warum mir Scala geeigneter erscheint, habe ich oben schon beschrieben.

1. Es existiert so gut wie keine vernünftige IDE-Unterstützung. Die Plugins für die großen IDEs sind ein Krampf.
2. Besteht ein Softwareprojekt aus weit mehr als nur Code einhämmern. Was Scala bei der Programmierung erleichtert, macht es in den anderen Disziplinen und Tätigkeiten wieder kaputt, z.B. dem Testing, Refactorings (siehe IDE-Support), ...
Das schon wieder... Du verwechselst Sprachen und IDE's. Ich rede nicht von IDE's. Ich weiß, wie geil Eclipse ist, und ich weiß auch, warum ich meinen Scala-Code in einem Text-Editor eintippe: Das beste an Java ist Eclipse. Das schlimmste an Scala ist auch Eclipse.

Ich rede aber von Sprachen. Und ich stelle fest: mit Scala & Text-Editor komme ich seltsamerweise besser voran, als mit Java & Eclipse, als ob die Entwicklung asymptotisch besser laufen würde, trotz schlechter konstanten (verursacht durch fehlende IDE's). Was Testing angeht, ist dieses ganze Predigt übrigens unbegründet: für Scala gibt's genügend Test-Frameworks.
3. Bestehen große Projekte aus TEAMS und mehreren hundert Entwicklern. Team bedeutet Kommunikation, u.a. auch mittels für alle verständlichen Code-Artefakten. Und nicht eine Masse von Einzelkämpfern, in der jeder sich durch möglichst kryptischen und "coolen" Code von den anderen abheben will.
Warum beschweren sich immer nur die Leute über "kryptischen und coolen" Code, die diesen Code eigentlich nie gesehen haben, und die Syntax nicht kennen? :autsch:
Ich finde es übrigens schade, dass wir schon wieder beim Scala-Bashing angelangt sind, auch wenn ich das ausdrücklich in meinem Eingangspost verhindern wollte. Solche Threads haben wir schon genug, und ich finde, die Frage nach dem Umgang mit Abstraktionen ist interessanter
Naja, da hast du schlecht geplant: wenn wir beide im selben Thread mit einem anonymen Java-Patriot landen, und der Thread auch noch mit einem Scala-Zitat anfängt, dann muss es automatisch mit Scala-Bashing enden :D

Und Back to Topic können wir hier nicht vernünftig kommen, weil alle zwei Posts jemand sagt, dass er keinerlei Probleme mit Monoiden und Abstraktionen habe, sondern lediglich die Scala-Syntax nicht kenne ;)
 

0x7F800000

Top Contributor
Und da hat er vollkommen recht, sie ist in keiner Weise lesbar. Viele Codezeilen aus alten Sprachen sind, auch wenn man sie nicht kann, doch meist etwas zu entziffern.
Господа, вы меня уже просто задолбали своей непробиваемой уверенностью в том, что ява-программисты должны понимать все языки на свете... :noe:
 
G

gu816est

Gast
Ist "Rein für's Studium" irgendwie "wenig"? Es gibt dutzende Sprachen, die "rein für's Studium" bzw. für Forschung gedacht sind, gug dir mal die ganzen Matlabs und R's an. Ich studiere Mathe, hauptsächlich Numerik und Stochastik, ich brauche irgendeine Sprache, in der ich die Algorithmen implementieren kann, weil ich sie ansonsten nicht verstehe. Matlab's und R's sind imho für nichts zu gebrauchen, weil sie keine einzige minimale Anforderung für's "not sucking" erfüllen: es sind keine "Programmiersprachen", sondern einfach nur extrem aufgeblähte Riesentaschenrechner aus den 80'er Jahren. Java ist dagegen eine Programmiersprache, die diesen Namen auch verdient, ist aber aufgrund der geschwätzigen Syntax für meine Probleme ungeeignet. Warum mir Scala geeigneter erscheint, habe ich oben schon beschrieben.
Also gibst du selbst zu, dass Scala eigentlich nur im akademischen Umfeld von Relevanz ist.
Ich rede aber von Sprachen. Und ich stelle fest: mit Scala & Text-Editor komme ich seltsamerweise besser voran, als mit Java & Eclipse, als ob die Entwicklung asymptotisch besser laufen würde, trotz schlechter konstanten (verursacht durch fehlende IDE's).
Herrje, ich rede von großen, kommerziellen Projekten, und nicht, was sich ein Mathe-Student in seinem Wohnheim nachts so zusammenfrickelt, oder was Professor XYZ und seine 3 Mitarbeiter grad so für die Auswertung von 20 Messdaten verwenden.
Was Testing angeht, ist dieses ganze Predigt übrigens unbegründet: für Scala gibt's genügend Test-Frameworks.
Ist deren Unterstützung und Integration in den bestehenden Entwicklungszyklus dann ähnlich gut gelöst wie die IDE-Plugins?
Warum beschweren sich immer nur die Leute über "kryptischen und coolen" Code, die diesen Code eigentlich nie gesehen haben, und die Syntax nicht kennen? :autsch:
Nun, dieser Thread fing damit an, dass uns "Landei" eine für einen aus einem kommerziellen Umfeld kommenden Programmierer vollkommen kryptische Codezeile als wundervolle und gut lesbare Abstraktion verkauft hat.

Übrigens sieht man an eurem Verhalten wunderbar, warum Scala wohl *nie* außerhalb eines kleinen elitären Zirkels aus Akademikern Fuß fassen wird: Eure Überheblichkeit und Arroganz statt sich konstruktiv mit richtigen Entwicklern zusammenzusetzen Ihr haltet mathematische Abstraktionen für das höchste der Gefühle und schaut voller Überheblichkeit auf die Objektorientierung, ihre Entwickler und die Fortschritte die diese Technologie erreicht hat. Dabei war und ist die Objektorientierung weit näher an der menschlichen Denkweise und somit für die Abstraktion komplexer Probleme geeignet als die funktionale Programmierung. Komplexe Systeme bestehen aus Akteuren, welche Zustand und Verhalten haben und Interaktionen eingehen, um ein höheres Verhalten zu erzeugen. Und jetzt schaut euch mal euer ganzes abgehobenes Gerede von "Monoiden", "Funktoren", "Arrows" und "Monaden" an und erzählt mir noch allen Ernstes, dass sowas eine wunderbar nachvollziehbare Abstraktion von realen Problemen von realen Geschäftskunden ist. Wie man sowas ohne obskure Übersetzungen und krassen Methodenbruch in eine allgemeinverständliche Systemarchitektur einbringen will, müsstet ihr schon näher erläutern. Stattdessen scheint ihr die Meinung zu vertreten, dass das Domänenmodell und die Systemarchitektur die Nutzer und Kunden nichts anzugehen habe. Wollt ihr euren Kunden dann erstmal Nachhilfe in höherer Mathematik geben, damit sie eure supertoll nachvollziehbaren Abstraktionen überhaupt verstehen können? Wahrlich tolle Abstraktionen. Zur Not könnt ihr sie ja noch als Idioten beschimpfen, so wie ihr es ja hier auch indirekt mit den Leuten macht, die "nur" diese ganzen "niederen Sprachen" können.
 

Marco13

Top Contributor
Ja tosche dumaju dass man скала njet so leicht panimajut wie ява, schon allein weil ява eher nach "Prosa" aussieht und weniger "Magische Snaki" enthält: +,-,[], schön aufgeräumt mit {}ern drum.

Ich stimme bygones dahingehend zu, dass die ursprünglich zitierte Kritik nicht an dem Konstrukt "Monoid" hing, sondern an der Syntax (was einen ja aber nicht davon abhalten muss, leeeiiicht abdriftend über beides und darüber hinaus zu plaudern ;) ). Das nicht-wohlwollende Zerpflücken der Zeile durch guest2512 ist für mich nachvollziehbar, auch wenn du dann erklärt hast, dass das von einer anderen Sprachphilosophie abhängt (bevor du Stück für Stück erklärt hast, dass damit ja fast das gleiche gemacht wird, wie man in Java mit einer anderen Syntax auch machen könnte :bae: ;) )

Oder anders: Wenn der ursprüngliche Code
Code:
implicit def wrapToOps[T](x: T)(implicit monoid: AdditiveMonoid[T]): AdditiveMonoid[T]#Ops = new monoid.Ops(x)
so ausgesehen hätte
Code:
Operations createOperations(AdditiveMonoid m)
{
    return new Operations(m);
}
hätte es die ursprüngliche Kritik sicher nicht gegeben, und was ein "Additiver Monoid" ist, hätte derjenige auf Wikipedia nachgeschaut.

Dass "das eben alles nicht so einfach ist", und die kryptische Zeile damit gerechtfertigt ist, dass (es eine andere Sprachphilosophie ist und) die vermeintlich implizierte Annahme, man könnte "alles, was man so in Scala machen kann" genausogut mit einem einfachen "wrappen" wie im angedeuteten Java-Code erledigen schon von an Naivität grenzenden Scheuklappen zeugt, ist wohl auch vielen derjenigen klar, die sich kritisch äußern.

Ich bin mit den Möglichkeiten, die Java in bezug auf Abstraktion bietet, auch nicht zufrieden (GERADE in bezug auf das Typsystem - wie oft muss man als Krücke irgendwelche "Class"-Objekte rumreichen :autsch: ). Aber objektiv betrachtet muss man sagen, dass die Syntax von Scala sehr viel kryptischer ist, als die von Java und vielen anderen Sprachen. Das soll keine unfundierte Kritik sein, sondern nur eine Feststellung, wissend, dass diese Syntax einer höheren Kompaktheit, Ausdruchsstärke und um Längen größerer Mächtigkeit geschuldet ist.

Oder anders: Man hätte Scala vielleicht (!) überproportional viel leichter machen können, wenn man auf ein wenig von diesen Punkten verzichtet hätte. Eben auf Dinge, die man erst verwendet, wenn man sehr versiert in der Sprache ist (und ich weiß, dass man dann, wenn diese Dinge nicht möglich wären, auch wieder an (dann!) ärgerliche Grenzen stoßen würde - ich will also nicht sagen, dass das "besser" gewesen wäre, sondern nur andeuten, dass man diese Möglichkeit vielleicht gehabt hätte, wenn man Angst vor der in diesem Thread angeklungenen Kritik gehabt hätte)

BTW: Weil du das explizit erwähnt hattest: Ich habe auch mal ein bißchen mit Prolog rumgespielt. Die Syntax lernt man an einem Nachmittag, oder an zwei wenn man in bezug auf die Syntax ein Experte werden will. Bei Scala ist das genaue Gegenteil der Fall: Typinferenz, Implicits, Case-Classes, viele magische Zeichen die in verschiedenen Zusammenhängen verschiedene Bedeutungen haben ... das kann schon erschlagend sein. Wenn man es drauf hat, ist es sicher toll, wie eine Erlösung, kann gut sein. Aber wer kann schon neutral und objektiv beurteilen, ob es nicht vielleicht doch komplizierter ist, als es sein müßte...
 

Landei

Top Contributor
...Überheblichkeit und Arroganz...
Man sollte nicht gleich zum allerletzten Kunstgriff Nr. 38 greifen, wenn es mit der Argumentation nicht klappt, es gibt wesentlich subtilere. Nun gut, den "richtige Entwickler" könnte man vielleicht noch zu einem wahren Schotten aufbauen.

Aber im Ernst: Exakt die gleichen Diskussionen gab es schon, als die von dir so verehrte objektorientierte Programmierung Einzug halten sollte, was den imperativen und strukturierten Programmierern wenig behagt hat.

Bei den Abstraktionen irrst du dich gewaltig. Die von dir erwähnten Arrows sind z.B. eingeführt worden, weil Entwickler bei der Implementierung eines Parsers über Monaden hinausgehende Fähigkeiten benötigten, und dabei feststellten, dass dieselbe Abstraktion auch auf anderen Gebieten nützlich ist. Das von ihnen entwickelte Konzept des Arrows kann jetzt überall eingesetzt werden, wo es passt, man kann sich also ins "fertig gemachte Nest" setzen, ohne das Rad neu erfinden zu müssen.

Ein anderer, bisher nicht erwähnter Punkt ist sind Automatisierung, Optimierung und Korrektheitsbeweise, die erst durch entsprechende Abstraktionen möglich werden. Ich denke, auch Geschäftskunden freuen sich, wenn man ihnen sagt, dass der gelieferte Code bewiesenermaßen das tut, was er tun soll, oder wenn der Code durch einen Supercompiler wie durch Zauberhand doppelt so schnell läuft. Ich denke, das ist bisher nur die Spitze des Eisbergs.

...Dabei war und ist die Objektorientierung weit näher an der menschlichen Denkweise und somit für die Abstraktion komplexer Probleme geeignet als die funktionale Programmierung. Komplexe Systeme bestehen aus Akteuren, welche Zustand und Verhalten haben und Interaktionen eingehen, um ein höheres Verhalten zu erzeugen...

Wie kommst du darauf, dass ausgerechnet Objekte diese "Akteure" sind? Erlang und Scala haben Aktoren, die viel deutlicher das von dir beschriebene Verhalten zeigen.
 
Zuletzt bearbeitet:
G

gu816est

Gast
Aber im Ernst: Exakt die gleichen Diskussionen gab es schon, als die von dir so verehrte objektorientierte Programmierung Einzug halten sollte, was den imperativen und strukturierten Programmierern wenig behagt hat.
Du vergleichst Äpfel und Birnen:
1. Funktionale Programmierung gibt es nicht erst seit gestern und wohl noch ein Weilchen länger als OOP. Auch hat FP ihre Wurzeln rein in der Mathematik und nicht im menschlichen Verständnis der realen Welt. Von daher wäre es Unsinn, die FP als Nachfolger der OOP darzustellen, der irgendwann noch besser verständliche Abstraktionen ermöglicht.
2. Ist mein Kritikpunkt, dass sich ausgeklügelte, aber für Nicht-Mathematiker einfach nur kryptische, objektfunktionale Konstrukte, bzw. die funktionale Dekomposition allgemein, reichlich schlecht als Abstraktion auf den oberen Ebenen eignen, und erst recht nicht, wenn man eine enge Zusammenarbeit mit dem Kunden pflegt, um mit diesem ein Domänenmodell und die grobe Systemstruktur zu besprechen. Und um diesen Kritikpunkt hast du dich ja wunderbar gedrückt.

Wie kommst du darauf, dass ausgerechnet Objekte diese "Akteure" sind? Erlang und Scala haben Aktoren, die viel deutlicher das von dir beschriebene Verhalten zeigen.
Ach Gottchen, war diese spontane Kurzbeschreibung dem Herrn Mathematiker jetzt nicht exakt genug? Man möge mir diesen Lapsus verzeihen.
 

Landei

Top Contributor
Du vergleichst Äpfel und Birnen:
1. Funktionale Programmierung gibt es nicht erst seit gestern und wohl noch ein Weilchen länger als OOP. Auch hat FP ihre Wurzeln rein in der Mathematik und nicht im menschlichen Verständnis der realen Welt. Von daher wäre es Unsinn, die FP als Nachfolger der OOP darzustellen, der irgendwann noch besser verständliche Abstraktionen ermöglicht.

Von "Nachfolger" war nirgendwo die Rede. Trotzdem sickern immer mehr funktionale Konstrukte in den Mainstream ein, und angesichts der Herausforderungen paralleler Programmierung beginnen sich auch Ideen wie Aktoren und immutable Objekte durchzusetzen. Scala ist ja bewußt keine funktionale Sprache, sondern ein Hybrid - und soweit ich das beurteilen kann, führt das durchaus zu beachtlichen Synergien.

2. Ist mein Kritikpunkt, dass sich ausgeklügelte, aber für Nicht-Mathematiker einfach nur kryptische, objektfunktionale Konstrukte, bzw. die funktionale Dekomposition allgemein, reichlich schlecht als Abstraktion auf den oberen Ebenen eignen, und erst recht nicht, wenn man eine enge Zusammenarbeit mit dem Kunden pflegt, um mit diesem ein Domänenmodell und die grobe Systemstruktur zu besprechen. Und um diesen Kritikpunkt hast du dich ja wunderbar gedrückt.
Sicher werden Sachen wie Domänenmodelle immer den Großteil der Programmierarbeit ausmachen. Aber auch diese Arbeit wird mit Scala einfacher, z.B. durch Closures, Mixin-Vererbung und DSLs. Die komplexeren Sachen braucht man seltener, aber wenn man sie braucht, können sie extrem wichtig sein. Man lässt den Feuerlöscher in einem Gebäude ja auch nicht weg, weil man ihn nicht so oft braucht, oder?

Ach Gottchen, war diese spontane Kurzbeschreibung dem Herrn Mathematiker jetzt nicht exakt genug? Man möge mir diesen Lapsus verzeihen.

Ich bin kein Mathematiker. Ich habe Wirtschaftsinformatik studiert, und arbeite zur Zeit als Anwendungsprogrammierer in einem Glaswerk.

Insgesamt muss ich sagen, dass mir dein Diskussionsstil wenig gefällt. Alle anderen Diskussionsteilnehmer waren - auch wenn sie nicht meiner Meinung sind - konstruktiv und haben sehr sachlich argumentiert. Ich habe bei dir den Eindruck, dass du die Existenz der Sprache Scala irgendwie als persönliche Beleidigung ansiehst. In diesem Fall solltest du professionelle Hilfe in Anspruch nehmen. Ansonsten lass einfach die Finger davon, und warte ab, ob etwas daraus wird. Vielleicht werden eines Tages Java-Programmierer so gesucht wie heute COBOL-Programmierer - das könnte deine Chance sein...
 
Zuletzt bearbeitet:

0x7F800000

Top Contributor
Also gibst du selbst zu, dass Scala eigentlich nur im akademischen Umfeld von Relevanz ist.
Ich bin ein einzelner Mensch, ich kann keine Gedanken des gesamten Planeten lesen. Ich habe lediglich gesagt, warum es für mich von Relevanz ist. Davon, was da in dem "Enterprise-Millieu" los ist, weiß ich gar nichts, und es interessiert mich nicht sonderlich, ich kenn mich dort nicht aus. Dafür kenne ich haufenweise Mathematiker, Informatiker und Physiker, und ich kann Dir garantieren, dass keiner von den jemals PHP oder Java benutzen wird, unabhängig davon, wie viele Millionen "normale Programmierer" es täglich einsetzen. Wenn Du wissen willst, warum ich angefressen bin, und warum ich mich für Scala begeistere, dann versuch mal wenigstens irgendeinen kleinen Mergesort- oder Dijkstra-Algorithmus in Matlab zu schreiben, geschweige denn dort irgendetwas zu testen oder zu dokumentiren.
Herrje, ich rede von großen, kommerziellen Projekten, und nicht, was sich ein Mathe-Student in seinem Wohnheim nachts so zusammenfrickelt, oder was Professor XYZ und seine 3 Mitarbeiter grad so für die Auswertung von 20 Messdaten verwenden.
Was die großen kommerziellen Projekte angeht, fragst du in meinem Fall den falschen: wenn es dich so sehr interessiert, frag doch die Leute von Twitter oder was weiß ich...
Professoren laufen in der Tat nicht in Horden herum, sollen die jetzt etwa ihre Arbeit einstellen, nur weil sie "normalen Programmierern" zahlenmäßig unterlegen sind, oder was? :autsch:
Ist deren Unterstützung und Integration in den bestehenden Entwicklungszyklus dann ähnlich gut gelöst wie die IDE-Plugins?
Wieso? Auf Test-Frameworks hat die Syntax keinen Einfluss: da laufen dieselben Werkzeuge wie bei Java auch.
Nun, dieser Thread fing damit an, dass uns "Landei" eine für einen aus einem kommerziellen Umfeld kommenden Programmierer vollkommen kryptische Codezeile als wundervolle und gut lesbare Abstraktion verkauft hat.
Jup, auf diesen Fehler haben ihn schon bygones und ich zweimal hingewiesen. Das Zitat war unglücklich.
Übrigens sieht man an eurem Verhalten wunderbar, warum Scala wohl *nie* außerhalb eines kleinen elitären Zirkels aus Akademikern Fuß fassen wird: Eure Überheblichkeit und Arroganz statt sich konstruktiv mit richtigen Entwicklern zusammenzusetzen
Was genau kommt dir an der verwendung von mathematischen Fachbegriffen so überheblich und arrogant vor? Es gibt sehr viele Leute, die mit sehr seltsamen Sachen arbeiten, von den weder Du noch ich irgendeinen Plan haben. Aber wenn zu mir ein Physiker kommt, und irgendwas über eine C++-Simulationsbibliothek fragt, dann werfe ich dem doch nicht gleich "Arroganz und Überheblichkeit" vor, nur weil ich von Bosonen, Gluonen und Spins nichts verstehe: ich akzeptiere einfach, dass es mich nichts angeht.
Ihr haltet mathematische Abstraktionen für das höchste der Gefühle und schaut voller Überheblichkeit auf die Objektorientierung, ihre Entwickler und die Fortschritte die diese Technologie erreicht hat.
Ich habe mich in diesem Thread etwa vor fünf Beiträgen ausführlichst darüber ausgebreitet, warum viele mathematische Abstraktionen für die Programmierung ungeeignet sind. Und wer hat hier bitte schön irgendetwas gegen Objektorientierung oder leugnet die Fortschritte dieser Technologie? Fehlende OOP ist der Hauptgrund, warum ich altertümliche Programmiersprachen wie Matlab und R nicht leiden kann, und ich habe mich für Scala entschieden, weil es das fortschrittlichste typsystem weit und breit hat.
Dabei war und ist die Objektorientierung weit näher an der menschlichen Denkweise und somit für die Abstraktion komplexer Probleme geeignet als die funktionale Programmierung.
Sehr merkwürdiger Vergleich von Birnen mit Äpfeln. Scala, Ruby, Python, Closure, Kotlin oder wie die alle heißen, diese ganze Palette von Sprachen kombiniert wunderbar OOP und FP. Du verwendest die begriffe dagegen so, als ob sie sich gegenseitig ausschließen oder irgendwie stören würden.

Und jetzt schaut euch mal euer ganzes abgehobenes Gerede von "Monoiden", "Funktoren", "Arrows" und "Monaden" an und erzählt mir noch allen Ernstes, dass sowas eine wunderbar nachvollziehbare Abstraktion von realen Problemen von realen Geschäftskunden ist.
Was vermischst du denn da irgendwelche kategorientheoretische Grundbegriffe mit Geschäftskunden?
Wenn deine Geschäftskunden in deinem Online-Shop nach einer Tüte Äpfel suchen, kommst Du zu den doch auch nicht mit relationaler Algebra, warum unterstellst du das denn bitte Programmierern, die in funktionalen Sprachen arbeiten? Begriffe wie "Monoiden", "Funktoren", "Arrows" und "Monaden" sind (wie die ganze Kathegorientheorie) dermaßen extrem allgemein, dass man sie auf jedes Lebensbereich anwenden kannst (oh wunder, auch auf java), wenn man denn unbedingt will. Ich habe mir neulich ein Buch über computational cathegory theory durchgelesen, und ich kann sagen: ne, nicht wirklich praxistauglich, weil eben das Verhältnis zwischen den Vorschriften in den Interfaces und der gelieferten Funktionalität nicht wirklich befriedigend ist.

Wie man sowas ohne obskure Übersetzungen und krassen Methodenbruch in eine allgemeinverständliche Systemarchitektur einbringen will, müsstet ihr schon näher erläutern.
Das wird beim Odersky im Kapitel "For expressions revisited" erläutert: dort wird auch am Rande der Begriff "Monad" erwähnt, der den ganzen Spuk anscheinend losgetreten hat. Wenn man hinter so etwas wie
Code:
val matrix = Array(Array(1, 2), Array(3, 5), Array(7, 3))
val listOfSquaredEntries = for (row <- matrix; entry <- row) yield (entry * entry)
einen Monad erkennt: okay, soll mir recht sein. Obskurer wird der code dadurch nicht.

Stattdessen scheint ihr die Meinung zu vertreten, dass das Domänenmodell und die Systemarchitektur die Nutzer und Kunden nichts anzugehen habe.
Ich vertrete die Meinung nicht, ich sehe es jeden Tag: haufen Leute nutzen fröhlich irgendwelche Mathematischen pakete, und sie juckt überhaupt nicht, wie es im inneren funktioniert.

Wollt ihr euren Kunden dann erstmal Nachhilfe in höherer Mathematik geben damit sie eure supertoll nachvollziehbaren Abstraktionen überhaupt verstehen können? Wahrlich tolle Abstraktionen. Zur Not könnt ihr sie ja noch als Idioten beschimpfen, so wie ihr es ja hier auch indirekt mit den Leuten macht, die "nur" diese ganzen "niederen Sprachen" können.
Sag mal, was ist dein Punkt, worüber regst du dich so auf? Java wird in den nächsten 20 jahren nirgendwohin verschwinden, mach dir um deinen Arbeitsplatz keine sorgen. Räum deine Fackel also bitte weg, wenn dir Scala nicht gefällt: ok, dann spiel halt mit was anderem herum, vielleicht irgendein Rennspiel oder Tennis. Ich würde dir aber nach wie vor wärmstens empfehlen, einfach mal die Sprache zu lernen, weil's lustig ist.
 
S

SlaterB

Gast
Insgesamt muss ich sagen, dass mir dein Diskussionsstil wenig gefällt. Alle anderen Diskussionsteilnehmer waren - auch wenn sie nicht meiner Meinung sind - konstruktiv und haben sehr sachlich argumentiert. Ich habe bei dir den Eindruck, dass du die Existenz der Sprache Scala irgendwie als persönliche Beleidigung ansiehst. In diesem Fall solltest du professionelle Hilfe in Anspruch nehmen.
die letzten Sätze sind vielleicht sauber formuliert, aber noch unkonstruktiver als das Zitierte
 

0x7F800000

Top Contributor
die letzten Sätze sind vielleicht sauber formuliert, aber noch unkonstruktiver als das Zitierte
Das stimmt! Landei, reiß dich mal zusammen :noe:
Ich möchte in diesem Zusammenhang ausdrücklich darauf hinweisen, dass die Bemerkung mit tennis keineswegs abwertend oder sarkastisch gemeint war: ich selbst kann kein tennis, ich jogge lieber. Aber ich verstehe nicht, warum die Leute mit solcher Verbissenheit versuchen mir zu beweisen, dass Scala oder FP schlecht sind, als ob ihre Existenz davon abhängen würde. Ich selbst betrachte das ganze nämlich hauptsächlich als Hobby, weil's eben wirklich lustig ist: an der Uni muss ich nach wie vor Java-Code korrigieren bzw. Lösungen in Java erstellen, und meckere darüber auch nicht zu laut. Bleibt mal locker :)

Marco13 hat gesagt.:
Ja tosche dumaju dass man скала njet so leicht panimajut wie ява, schon allein weil ява eher nach "Prosa" aussieht und weniger "Magische Snaki" enthält: +,-,[], schön aufgeräumt mit {}ern drum.
Omfg, die java-Programmierer können es also doch :eek:
:D
 
Zuletzt bearbeitet:

darekkay

Bekanntes Mitglied
Ich dachte, dass wenn in diesem Thread sowieso nicht mehr viel Konstruktives zu erwarten ist, wenigstens vor negativen mentalen Auswirkungen gewarnt werden sollte.

Gegenmaßnahmen: Schopenhauer rät dazu, ruhig zu bleiben und auf Sachargumenten zu bestehen. Dennoch gesteht er ein, dass es nicht ausreicht, selbst höflich zu bleiben, da selbst eine Widerlegung ad rem oft als Kränkung der Eitelkeit verstanden wird und so ein ad personam des Gegners nach sich zieht. Einzige Gegenmaßnahme ist, sich unberührt zu zeigen und klarzustellen, dass die Beleidigung nicht Thema des Streites ist.

Oder auf deutsch: lass dich einfach nicht auf sein Niveau herab ;)
 

0x7F800000

Top Contributor
Passend zu dem Thema sagt man: offensichtlich wäre es in diesem Fall schöner, wenn Klassen X1, X2, Xn, die semantisch ähnliche Methoden x1(), x2(), ... , xn() aufweisen, alle dasselbe interface X mit der methode x() implementieren würden. Dann würden alle x1(), x2(), ... , xn() einfach nur x() heißen. Wenn man dann gleichzeitig mit mehreren Klassen Y1, Y2, ..., Ym mit der Semantik von Y arbeitet, dann muss man lediglich darauf auchten, dass die Syntax von X und Y verschieden ist, anstatt auf die unterschiedliche Syntax von allen X1, .... Xn's und Y1, ..., Ym's gleichzeitig zu achten.

Man vergleiche diese zwei fiktive Listen von Klassen, einmal ohne gemeinsame Abstraktion, und einmal mit gemeinsamer Abstraktion:
Code:
[Semantik: Collection-Größe] [Semantik: Graphische Abmessungen]
X1 syntax: length()                 Y1 syntax: size()
X2 syntax: getLength()              Y2 syntax: length()
X3 syntax: getSize()                Y3 syntax: size()
X4 syntax: getLength()              Y4 syntax: getSize()
X5 syntax: size()                   Y5 syntax: getDimension()
...                                 ...



[Semantik: Collection-Größe] [Semantik: Graphische Abmessungen]
X interface syntax: size()          Y interface syntax: boundingBoxDimension()
X1 <: X syntax: size()              Y1 <: Y  syntax: boundingBoxDimension()
X2 <: X syntax: size()              Y2 <: Y  syntax: boundingBoxDimension()
X3 <: X syntax: size()              Y3 <: Y  syntax: boundingBoxDimension()
X4 <: X syntax: size()              Y4 <: Y  syntax: boundingBoxDimension()
X5 <: X syntax: size()              Y5 <: Y  syntax: boundingBoxDimension()
...                                 ...
Du darfst drei mal raten, was übersichtlicher ist ;)

Nochmal: damit schließt man zwar nicht aus, dass man irgendwann interfaces erfindet, die dieselbe methodennamen heißen, aber so muss man nur auf die interfaces, statt auf jede einzelne Klasse aufpassen.
 

schalentier

Gesperrter Benutzer
Was hat das mit Java oder Scala zu tun? Der Effekt kann mit jeder Sprache eintreten, sobald der Umfang waechst. Je mehr verschiedene Teams oder einzelne Leute an unterschiedlichen Libraries und Frameworks arbeiten, desto hoeher die Wahrscheinlichkeit, dass Inkonsistenzen in der Benennung von Variablen, Klassen- oder Methodennamen auftreten.

Schoen geloest ist das uebrigens bei Ruby, da kann man "aliase" vergeben und es gibt eigentlich immer size() und length(), die das selbe machen ;-)
 

Neue Themen


Oben