Reasoning about code

Ein kurzer, eloquenter Artikel darüber, warum funktionale Programmierung das Leben leichter macht:

Reasoning About Code | LispCast

interessantes langes Thema (mit allen eh schon bekannten Argumenten (*)) im anderen Forum
Java 8 - Lambdaexpression: flatMap | Java: Java-Forum.org

darunter Beispiel Problefrage

grps.stream().flatMap(grp ->
            grp.getMeasures().stream().forEach(m -> {
             //TODO
            });
        );

von jemanden der von Java 8-Fan-Vorgesetzten dazu gezwungen wurde…,

Fehlermeldung ‚Type mismatch: cannot convert from Stream to ‘ wenig hilfreich

richtiger lautet der Code dann

grps.stream().flatMap(grp ->
            grp.getMeasures().stream()).forEach(m -> {
             //TODO
            } );

Zitat „Habe es jetzt mit Hilfe hinbekommen, es war eine Klammer falsch:“…

solche Probleme soll man mal direkt im Code erkennen können…, schon jetzt mit beiden Versionen nicht ganz leicht Unterschied zu erkennen,
einfach unlesbares Blahblah


im Forum hätte dazu wohl keiner helfen können, zu einen beachtlichen Anteil dadurch dass mit TODO viel Code gestrichen,
aber womöglich auch wenn vorhanden, schlimm hier allein schon dass für m der Datentyp fehlt

die spät erwähnte normale Variante zeigt die Lücke

  for(Group g : grps) {
    for(??? m : g.getMeasures()) {
      //TODO
    }
  }

klar von jedem lesbarer,

im TODO könnte man freilich auch Variablenname m zu g vertippen, vielleicht kleine Analogie zum Klammerfehler,
aber das fällt doch recht schnell auf, selbst falls m auch vom Typ Group sein sollte


(*) interessant noch recht frisch unter den Standardargumenten finde ich

Es wird einfach viel zu viel an Code versteckt. Bei den Collectors genauso. Und das ist nicht nur von Nachteil für Programmierer, die sich nicht damit auskennen. Kommt es zu Bugs o.ä. kann erstmal gesucht werden warum die Map falsche Werte beinhaltet. Es kann nicht „rein- gedubuggt“ werden, da es nirgens ein map.put() mehr gibt.

aber das könnte man natürlich zum Vorteil umdrehen, dass sich bei Stream-Verwendung einfach nie mehr entsprechende Fragen stellen :wink:

in der freilich schönen Java 8-Variante von (auch aus Thread dort geklaut)

public int[] getEvenNumbers(int[] arr) {
  int[] result = new int[arr.length];
  int pos = 0;
  for(int i : arr) {
    if(i % 2 == 0) {
      result[pos++] = i;
    }
  }
  return Arrays.copyOf(result, pos);
}

public int[] getEvenNumbers(int[] arr) {
  return Arrays.stream(arr)./*parallel().*/filter(i -> i % 2 == 0).toArray();
}

wäre es mit den Streams reichlich schwer zu debuggen, was denn an bestimmten Positionen im Array oder einen gesuchten Wert exakt passiert ± 3 Positionen,
wieviel bis dahin im Ergebnis-Array steht usw.,
in dem einfachen Beispiel auch gar nicht nötig, in komplizierten Programmen oft wünschenswert

[quote=Landei]Ein kurzer, eloquenter Artikel darüber, warum funktionale Programmierung das Leben leichter macht:[/quote]Der Artikel erklärt, was Code leichter verständlich macht, zeigt aber nicht auf, wieso Funktionale Programmierung hier Vorteile haben sollte. Dieser Zusammenhang wird lediglich behauptet.

bye
TT

Wesentliche Punkte die genannt werden sind Pure Functions und Immutable Values.

Beispiele bei denen Java dies missachtet hat und dafür einiges an Kritik bekam sind Calendar (Mutability), sowie SimpleDateFormat. (Thread-Safety, not pure)

Aber, sie haben es repariert mit LocalDate und DateTimeFormatter, die in Java 8 hinzugekommen sind.

[quote=ionutbaiu]Wesentliche Punkte die genannt werden sind Pure Functions und Immutable Values.

Beispiele bei denen Java dies missachtet hat[/quote]Gut, in den Standardklassen von Java sind ein paar Stolpersteine drin.
Aber dass erklärt noch nicht, warum das bei Funktionalen Sprachen inhärent besser sein soll…

bye
TT

[QUOTE=Timothy_Truckle;130117]Gut, in den Standardklassen von Java sind ein paar Stolpersteine drin.
Aber dass erklärt noch nicht, warum das bei Funktionalen Sprachen inhärent besser sein soll…

bye
TT[/QUOTE]

Immutable Values sind hier meiner Meinung nach der Kern. Wenn ich per Getter ein Java Objekt herausgebe, garantiert mir niemand, dass es nicht von außen irgendwo manipuliert wird. Bei Date oder bei Collections muss ich eine Kopie herausgeben, wenn ich das sicherstellen will. Reference-Leaking ist ein Unsicherheitsfaktor, den ich bei funktionalen Sprachen die sich strikt an immutable values halten nicht habe - ich muss mir darüber keine Gedanken machen, kann lokal denken (um im Wortlaut des Artikels zu bleiben).

Das Thema wird in diesem Talk von Rich Hickey m.M.n. sehr gut dargestellt: https://youtu.be/VSdnJDO-xdg?t=32m32s - als Disclaimer, er nennt den Part selbst “Ranty”, aber im Kern gebe ich ihm absolut Recht. Verlinkt ab 32:32, aber der gesamte Talk ist sehenswert.

[quote=inv_zim;130120]Immutable Values sind hier meiner Meinung nach der Kern.[/quote]Immutables sind tatsächlich überdurchschnittlich oft extrem sinnvoll, aber eben nicht immer, gerade, wenn es um das Verständnis des Codes geht.

Für mich ist im Hinblick auf “lokales Denken” Polymorphie viel hilfreicher als die Gewissheit, dass Werte unveränderlich sind.

Und welcher Ansatz den geringeren WTF-Faktor hat zeigt ja das Post von @SlaterB recht deutlich…

bye
TT

Immutable ist eine feine Sache, funktionale Syntax eine andere…

String zB. war schon immer immutable in Java, ein grosser Vorteil.
Die “Denkarbeit” die man sich dank immutable Collections sparen wuerde wiegt IMHO nicht auf wieviel man ueber streams, implicits und andere Sprachkonstrukte nachdenken muss, aber zum Glueck dauert das kompilieren von Scala ja lange genug dass man wirklich alle Zeit der Welt zum Nachdenken hat…
Nebenbei, immutable Collections haben ihren Preis zur Laufzeit.

Wir sind gerade dabei eine Anwendung zu refactoren, nein umzuschreiben, aehh., nein, sozusagen neuzuschreiben, von Scala nach Java, man spart sich erstaunlich viel Boilerplate Code in normalem Java, konvertieren von Exceptions, Left, Either, Option etc. faellt alles weg, das versprechen das Scala gab wurde IMHO nicht gehalten, jetzt muss nur noch Fugue raus, dann kann man wieder klaren, verstaendlichen Code schreiben.

Hab persoenlich nix gegen die Java 8 Lambdas, Stream API usw., kann zB. mit einem Lambdas manchmal Code besser weil klarer schreiben, aber eben nicht immer, in massen ok.
Java ist nunmal alt, und es ist alt und verbreitet nicht weil es immer den letzten Schrei mitmacht, sondern weil der Code einfach zu verstehen war, das zu ignorieren waere einen der Hauptgruende fuer Javas Erfolg zu ignorieren.

Auch auf die Gefahr hin, dass das jetzt von dem abweicht, was mit dem Thread und “Reasoning about code” eigentlich ausgesagt werden sollte:

[QUOTE=SlaterB]
in der freilich schönen Java 8-Variante von (auch aus Thread dort geklaut)

public int[] getEvenNumbers(int[] arr) {
  int[] result = new int[arr.length];
  int pos = 0;
  for(int i : arr) {
    if(i % 2 == 0) {
      result[pos++] = i;
    }
  }
  return Arrays.copyOf(result, pos);
}

public int[] getEvenNumbers(int[] arr) {
  return Arrays.stream(arr)./*parallel().*/filter(i -> i % 2 == 0).toArray();
}

wäre es mit den Streams reichlich schwer zu debuggen, was denn an bestimmten Positionen im Array oder einen gesuchten Wert exakt passiert ± 3 Positionen,
wieviel bis dahin im Ergebnis-Array steht usw.,
in dem einfachen Beispiel auch gar nicht nötig, in komplizierten Programmen oft wünschenswert[/QUOTE]

Das Beispiel ist “zu” einfach, um die wichtigsten Argumente daran zu verdeutlichen. In bezug auf das eigentliche Thema (und das Thema, zu dem es abgedriftet ist) könnte man es trotzdem versuchen, auch wenn es schon fast polemisch suggestiv wirkt: Bei code wie

public int[] getEvenNumbers(int[] arr) {
  int[] result = new int[arr.length];
  int pos = 0;
  for(int i=0; i<arr.length; i++) {
    if(arr** % 2 == 0) {
      result[pos++] = arr**++;
    }
  }
  return Arrays.copyOf(result, pos);
}

könnte man z.B. folgendes machen:

int array[] = { 0,1,2,3,4,5 };

int evenA[] = getEvenNumbers(array);
int evenB[] = Arrays.stream(array).filter(i -> i % 2 == 0).toArray();

System.out.println(Arrays.toString(evenA)); // Ausgabe: [0, 2, 4]
System.out.println(Arrays.toString(evenB)); // Ausgabe: []

Oops.


Ich denke, die Argumentation hat indirekt (nicht “nicht”, aber eben nur indirekt) mit mutability und immutability zu tun. Ich denke, es hängt mit einer allgemeinen Frage zusammen, die z.B. auch im von inv_zim verlinkten Video ein entscheidender Punkt ist (auch wenn er nicht direkt als dieser angesprochen wird) :

Was ist der Zustandsraum eines Objektes?

Ich denke, viele (vielleicht sogar die meisten) “Software”-Fehler hängen genau damit zusammen. Tatsächliche Implementierungsfehler (in einer Methode) werden relativ leicht erkannt und aufgedeckt, und können leicht durch unit-Tests abgedeckt werden. Die richtig ekligen Fehler sind ja die, die sich “in komplexer Software verstecken” - und die Komplexität bezieht sich praktisch immer auf den Zustandsraum. Viele Operationen verändern den Zustand vieler Objekte, und irgendwann, nach einer “unbekannten” (d.h. im einfachsten Fall: Vom Benutzer durch Klicks ausgeführten) Abfolge von Operationen wird eine Operation ausgeführt, die eine ungültige Annahme über den Zustand macht.

Man kann sich leicht die dazu passenden Bug-Reports vorstellen: “Wenn man diese-und-jene Datei lädt, und dann dies-und-das macht, und dann hier klickt und dann da, und dann dreimal dort, und dann eine neue Datei lädt und noch zweimal da-und-da klickt, erscheint eine NullPointerException”. würg

Der Charme den funktionale Konstrukte da haben ist eben die “Abgeschlossenheit” (zu der natürlich auch Immutability gehört). Am trivialsten ist ein Unit-Test für eine static methode, die nur immutable Parameter bekommt. (Und im Gegensatz zu Arrays SIND Streams eben immutable - man kann sich also denken, wo bei obigem Beispiel der Fehler eigentlich herkam ;-)).

[quote=Marco13]Am trivialsten ist ein Unit-Test für eine static methode, die nur immutable Parameter bekommt. (Und im Gegensatz zu Arrays SIND Streams eben immutable - man kann sich also denken, wo bei obigem Beispiel der Fehler eigentlich herkam ).[/quote]Also ich sag mal so: wer Arrays benutzt um die schwächen einer OO-Sprache aufzuzeigen muss schon ziemlich verzweifelt sein… :smiley:

Aber ernsthaft: natürlich wäre das Beispiel mit einer Collection wohl genau so schief gegangen. Der Unterschied liegt aber nicht darin, dass getEvenNumbers() nicht im FP-Style implementiert wurde, sondern dass die Implementierung schlicht kaputt ist und entgegen der Erwartung (auf Grund des Namens und der Tatsache, dass es einen Rückgabewert gibt) das übergebene Array verändert.
wobei:

Das übrgebene Array nach der Änderung zu kopieren und dann die Kopie zurückzugeben ist schon vorsätzlich…

Nebenbei gibt es auch abseits von Streams schon länger libs, die Collections filtern können. Das Problem reduziert sich also eigentlich auf die alte Frage „Lib nutzen oder selber programmieren“ mit den zwei dazugehörigen Teilfragen: „Kenne ich die relevanten Libs?“ und „Kann ich das besser als die Lib-Programmierer?“. Da sind dann die Antworten leider viel zu oft : „nein, -“ oder „ja, ja!“.

Übrigens hätte man die Erwartung, dass das übergebene Array nicht verändert werden darf auch in einem UnitTest fixieren können. Denn letztlich gilt die Weisheit von Martin Fowler:

Ohne Spezifikation gibt es keine Fehler, nur Überraschungen.

bye
TT

Mein erster Gedanke bei der falschen Version war dann auch: forEach gibt doch nichts zurück, was hat das in einem flatMap verloren? Und das wäre schon die halbe Miete bei der Fehlersuche gewesen.

Dazu ist auch anzumerken, dass der Code bescheiden eingerückt ist.

in der freilich schönen Java 8-Variante von (auch aus Thread dort geklaut) … wäre es mit den Streams reichlich schwer zu debuggen, was denn an bestimmten Positionen im Array oder einen gesuchten Wert exakt passiert ± 3 Positionen

Es gibt genau dafür die peek-Methode, falls man gar nicht mehr weiß, was los ist. Nebenbei werden die IDEs auch im Lambda-Debuggen besser.

Aber eigentlich will ich auf dieses blöde geflameware gar nicht mehr eingehen, es wird langweilig. Ich habe hier den Artikel gebracht, weil er jenseits von Sprache und Syntax über die grundsätzlichen Ideen und Konzepte funktionaler Programmierung spricht, und als erstes werden wieder die guten alten Streams rausgekramt. Sicher, Streams sind funktional, man kann damit arbeiten, aber sie sind bestimmt nicht das gelungenste Beispiel für funktionale Programmierung in Java. Schon das Problem, dass man immer stream() aufrufen muss, weil grundlegende Methoden wie map und flatMap nicht an den Collections selbst verfügbar sind, ist ziemlich nervig. Aber es ist wahnsinnig schwer, sowas im ersten Anlauf hinzubekommen. In Scala wird z.B. demnächst die gesamte hochgelobte Collections-Bibliothek überarbeitet. Und Java mit seinem Kompatibilitäts-Fetisch hat es da noch schwerer.

Aber zu sagen, funktionale Programmierung ist blöd, weil das Beispiel Streams ein paar Ecken und Kanten hat, geht genauso am Thema vorbei, wie wenn man statische Typisierung ablehnt, weil sie Java so langatmig macht. Wer ein bisschen besser verstehen will, worum es eigentlich geht, kann den Artikel lesen. Wer das Haar in der Suppe finden will, hat es jetzt gefunden - Streams, yeah! EOD.

Eigentlich hatte ich gehofft, das klar würde, dass dieses Beispiel nicht direkt geeignet ist, um den entscheidenden Punkt klarzumachen (sondern höchstens, indirekt, den der Immutability). Das entscheidende war ja: Wenn man eine Funktion hat (im funktion-alen Sinn), dann hat die keine Seiteneffekte. (Und nochmal: Die Änderung am Parameter war „gestelzt“). Man muss sich nicht fragen, welche Funktion vorher oder nachher aufgerufen wurde oder wird, oder ob irgendeine Referenz in irgendeinem Objekt gerade „null“ ist, oder nicht. Eine Funktion als solche ist ein in sich abgeschlossener Building Block, wo man (wenn’s sein muss) ganz streng formal die Vor- und Nachbediungungen hinschreiben kann.

Für mich persönlich bedeutet das (etwas, was für andere haarsträubend wirkt? Weiß nicht…) : Wenn ich eine Instanzfunktion habe, die z.B. nur „wenige“ Fields einer Klasse verwendet, dann konvertiere ich so eine Funktion häufiger mal in eine static-Funktion, die intern dann aufgerufen wird, indem sie genau diese Fields übergeben bekommt. Damit ist klar: Ein Aufruf dieser Funktion ändert den Zustand des Objektes nicht. Parameter rein. Ergebnis raus. Ganz lokal und klar abgegrenzt. Der allgemeine Versuch sollte ja (grob gesagt) sein, „möglichst wenige Fields“ zu haben. Und Methoden, die (viele) „Fields brauchen“, sind extrem selten, und Methoden die Fields verändern auch. Solche Muster wie

class Foo {
    int i;
    float f;

    float compute() {
        return compute(i, f);
    }

    private static float compute(int i, float f) { ... }
}

sind IMHO nicht das schlechteste. (Den Aussagen, die in die Richtung gehen, dass eine Methode möglichst wenige Parameter haben sollte, habe ich hoffentlich genug vorgegriffen).

Bin heute zufällig auf diesen Blog-Post gekommen, vielleicht für den einen oder anderen interessant:
Functional Programming Is Not Popular Because It Is Weird

Und ich muss zugeben, ich könnte mir auch nicht vorstellen eine komplette Applikation in Haskell zu programmieren - eben weil sich manche Teile wie ein Puzzle anfühlen die in Java, Ruby oder was auch immer einfach runter geschrieben werden können. (UI, Webservice-Definitionen, etc.)

Hm. Die Fragestellung, um die es hier ging, mit C++ Templates zu vermischen ist etwas schräg. Templates sind zwar eine (Turing-vollständige) Sprache, aber sie sind nicht als solche gedacht. (Als was sie gedacht sind, weiß ich nicht genau. Als eine Art Textersetzungsengine, vielleicht. Wenn man mit C++ zu tun hat, hat man also immer mit 3 Sprachen auf einmal zu tun). Sicher, die Argumentation ist davon teilweise unabhängig. Aber sie ließe sich (ohne den bezug zu templates) auch einfacher und klarer darstellen.

(Das Problem ist vielleicht, dass bei C++ Templates teilweise das Problem auftriftt, das bei einigen anderen Dingen (speziell, aber nicht nur funktionaler Programmierung) auch auftritt: Showing off. Es werden absurde, spezifische, am Rand des überhaupt ausdrückbaren liegende, kryptische Konstrukte verwendet (unabhängig von der Frage, ob das jeweilge Konstrukt für die Lösung des Problems das geeignetste ist), weil das irgendwie fancy ist, und eine Mischung aus “Kompaktheit” und “Kryptischkeit” von den Betreffenden als “Eleganz” (miß)verstanden wird, und diese Konstrukte, wenn sie funktionieren, zu etwas führen was man als “Geekgasmus” bezeichnen könnte).

[QUOTE=schlingel]Bin heute zufällig auf diesen Blog-Post gekommen, vielleicht für den einen oder anderen interessant:
Functional Programming Is Not Popular Because It Is Weird

Und ich muss zugeben, ich könnte mir auch nicht vorstellen eine komplette Applikation in Haskell zu programmieren - eben weil sich manche Teile wie ein Puzzle anfühlen die in Java, Ruby oder was auch immer einfach runter geschrieben werden können. (UI, Webservice-Definitionen, etc.)[/QUOTE]

Nan-in, a Japanese master during the Meiji era (1868-1912), received a university professor who came to inquire about Zen.
Nan-in served tea. He poured his visitor’s cup full, and then kept on pouring.
The professor watched the overflow until he no longer could restrain himself. „It is overfull. No more will go in!“
„Like this cup,“ Nan-in said, „you are full of your own opinions and speculations. How can I show you Zen unless you first empty your cup?“

Ich glaube für jemanden mit einem „imperativen“ Programmier-Hintergrund ist es schwerer, funktionale Programmierung zu „grokken“, als jemand, der gar keinen hat. Man muss sozusagen erst einmal sein imperatives Wissen „verlernen“, bevor man wirklich loslegen kann - und ja, das ist erst einmal „weird“. Aber dass eine Programmiertechnik „weird“ ist, heißt ja noch lange nicht, dass sie nicht nützlich sein kann. Viel verrückter als funktionale Programmierung finde ich z.B. Continuations, bei denen sich mein Gehirn im vierdimensionalen Raum umstülpt (so kommt es mit jedenfalls vor) - und trotzdem kann dieses Konzept an einigen Stellen äußerst nützlich sein.

Es ein riesiger Unterschied, ob ich wirklich eine ganze Applikation in Haskell schreiben will, oder ob ich einfach funktionale Stückchen, die sich gut nach Java übertragen lassen, verwende. Wenn alles was man hat, eine (mehr oder weniger) objektorientierte Sprache ist, fängt alles an, wie ein Objekt auszusehen, aber das ist nur eine mögliche Sichtweise, und eben nicht die ganze Wahrheit. Die Vorteile, die funktionale Programmierung bietet, sind real - selbst unter den Einschränkungen von Java. Sie als „weird“, zu kompliziert oder bloße Spielerei abzutun ist einfach eine unnötige Beschränkung der eigenen Ausdrucksmöglichkeiten.

gibt es eigentlich Anfänger in Scala (oder Java-funktionaler Programmierung), gibt es ein (bevorzugt deutsch-sprachiges) Forum,
in welchem ganz normale Dinge wie die allseits beliebten Übungsprogramme mit Eingabe von Konsole, oder Swing-Layout oder sonstiges normales besprochen wird?

Scala wird ja wohl nicht so gut sein, dass es dazu einfach keine Probleme mehr gibt :wink:

nicht dass ich Scala oder das alte Forum besonders pushen möchte, aber ich komme nicht umhin auf mögliche Aktualisierung von
Scala - Deutsches Scala-Tutorial | Java: Java-Forum.org
oder auch

hinzuweisen…, sonst könnte es zu sehr nach toter alter Sprache aussehen
(edit: auch die verlinkten Blogs sind weitgehend nicht mehr erreichbar oder mit letzten Postings 2013 und kleiner…, wie ja auch https://scalatutorial.wordpress.com/ )

insbesondere scalatutorial.de scalatutorial.de gibt es nicht mehr,
war das nicht ein deutsches Forum von dir, Landei? gibt es das noch woanders?

unter
https://groups.google.com/forum/#!forum/scala-user
Newest ‚scala‘ Questions - Stack Overflow
sehe ich viel zu sehr Fragen a la ‚Create a function that takes objects which declare an implicit writes‘ die sich mit der Sprache selber beschäftigen statt mit fachlichen Aufgaben,


imperative Sprachen sind das A + O, schon seit 50 Jahren, lange vor Java oder Scala,

vor Java gab es bei mir Turbo Pascal, und das war programmiertechnisch auch schon ausreichend, nie käme eine funktionale Sprache da ran,
Java ist lediglich ein Super-Duper-Upgrade mit ganz anderer Dimension IDE (Eclipse, Livecompiler),
mit Klassen und Objekten für weniger Parameter und nicht mehr globale Menge an (statischen) Funktionen, die allein nach Namen zu unterscheiden sind,
nebenbei die ganzen netten Effekte wie Polymorphie

all das ist schön, aber auch ein simples Turbo Pascal wäre jeder funktionalen Sprache überlegen, einfach weil verständlicher Code statt Wirrwarr,
das gilt für damalige funktionale Sprachen wie aktuelle, all die normalen Fortschritte nebenher (für alle Sprachen gleich) ändern an dieser grundsätzlichen Frage seit 50 Jahren nichts

die funktionalen Anteile könnten in Java eine gute Ergänzung für 5% des Codes komplexer Programme sein,
Kontrollfluss von Methoden mit Fachlogik, welche selber auch noch normal geschrieben sind,
Kontrollfluss, den man sonst müßig in eigenen Iteratoren usw. selber schreibt,

aber wenn dagegen Schleifen & Co. komplett rausschmissen werden, jede einzelne Methode zwanghaft sonderbar geschrieben wird, dann hat man kein Java mehr,
sondern einen neuen Dialekt,
solche Programme wie auch das angefangene Projekt Java-Forum fjorum - Java based Forum - Byte-Welt - Die Welt des Programmierens stehen außerhalb der Programmiersprache Java,
und abgesehen von anderen Funktional-Fans kann dann niemand mehr mitarbeiten…, besser gleich in Scala, dann geklärt

In den letzten 10 Jahren, haben sich aber die Rechner massivst verändert. Heute sind auf einem Kleinstrechner, wie dem Raspberry PI schon 4 Rechenkerne verfügbar. Multithreading war davor nur ein Rescheduling, wohingegen man heute mit mehreren Rechenkernen von wirklich Parallelem Rechnen sprechen kann.

Um daraus die Vorteile ziehen zu können, also alle Ressourcen nutzen zu können, muss man eben anderen Code schreiben, der eben, sofern sinnvoll, alles in ein oder mehrer Threads auslagert. Dies kann auf verschiedene Arten geschehen. Compiler können oder könnten dies teilweise, indem sie verschiedene Analysen machen oder irgendwelche Aspekte eingearbeitet werden. Man kann das ganze auch von Hand machen, was aber sehr viel wissen voraussetzt und eine gewisse Fehleranfälligkeit mit sich bringt. Oder man benutzt die Dinge, die in Java hinzugekommen sind.

Eine For-Schleife ist die schlechteste Art für eine Map-Operation. Gehört von https://de.wikipedia.org/wiki/Guy_Lewis_Steele_junior

Eine For-Schleife legt fest, dass die Ausführung sequentiell ist und nur auf einem Kern läuft. Will man mehrer Kerne auslasten, dann hat man wirklich viel Spaß mit Threads.
Geht man über die Map-Operation von Stream, dann muss man nur die Parallele Verarbeitung anschalten, bei verhältnismäßig einfachem Code.

Es mag gut sein, dass das einfachere auf Single-Threading basierende Programmiermodel für viele, wenn nicht gar die meisten Anforderungen reicht, wenn man aber schnellere Programme haben möchte dann wird man mehrer Kerne einbinden müssen. Mehrer Kerne, mehr Threads, mehr Komplexität ist eben der Trade-off für schnellere Programme, den man eben eingeht oder nicht.

Nein, das ist nicht von mir.

Stimmt, ich müsste die Links mal updaten - habe mich in letzter Zeit deutlich mehr um Java 8 gekümmert, und Scala nur aus den Augenwinkeln verfolgt.

imperative Sprachen sind das A + O, schon seit 50 Jahren, lange vor Java oder Scala,

Sowohl das Konzept (Lambda-Kalkül, SKI-Kalkül) wie auch die Umsetzung (Lisp) funktionaler Programmierung sind jeweils ungefähr so alt wie ihre imperativen Gegenstücke (Turing-Maschine bzw. Algol, Fortran u.s.w.).

solche Programme wie auch das angefangene Projekt Java-Forum https://forum.byte-welt.net/byte-wel...a-based-forum/ stehen außerhalb der Programmiersprache Java, und abgesehen von anderen Funktional-Fans kann dann niemand mehr mitarbeiten

Ich weiß gar nicht, was du dich so aufregst. In einem Projekt von dir würde ich aufgrund des Stils wahrscheinlich auch nicht mitarbeiten wollen - so what?

ja, ich meinte dass weder das eine noch das andere neu ist, weder Java noch Scala spielt eine besondere Rolle dabei, auch OO oder nicht nur nebensächlich,
beides ist seit 50 Jahren bekannt, zu jeder Zeit immer imperativ vorne

keine Notwendigkeiten von Fortschritt oder so zu reden, gabs schon immer, war schon immer unterlegen, nix neues

*** Edit ***

[quote=ionutbaiu;130271]In den letzten 10 Jahren, haben sich aber die Rechner massivst verändert. Heute sind auf einem Kleinstrechner, wie dem Raspberry PI schon 4 Rechenkerne verfügbar. Multithreading war davor nur ein Rescheduling, wohingegen man heute mit mehreren Rechenkernen von wirklich Parallelem Rechnen sprechen kann.
[…]
Eine For-Schleife legt fest, dass die Ausführung sequentiell ist und nur auf einem Kern läuft. Will man mehrer Kerne auslasten, dann hat man wirklich viel Spaß mit Threads.
Geht man über die Map-Operation von Stream, dann muss man nur die Parallele Verarbeitung anschalten, bei verhältnismäßig einfachem Code.[/quote]
parallel() war im Thread im anderen Forum ja auch eines der Themen,
das ist auf so vielen Ebenen fragwürdig…

  • Rechner werden schneller, nicht langsamer,
    die Wahrscheinlichkeit, dass auf irgendein festes klein-programmiertes Problem X zu warten ist, ist um Faktor 1000 gesunken

  • dass eine einzelne Code-Zeile, ein kleiner Block normaler Befehle, sequentiell arbeiten, ist kein Nachteil
    sondern ein wichtiges (erwartetes) Feature wiederum für einfachen verständlichen verläßlichen Code,
    statt alle 5 Min. eine unbekannte Blackbox vorsichtig analysieren zu müssen

  • die so wichtige Entscheidung, dass Code von mehreren Threads() auszuführen ist,
    gehört in übergeordnete Kontrollstrukturen, nicht mitten in Fachlogik hineingesetzt (oder fehlend),

schlecht, Fachlogik allein deswegen zu modifizieren, nicht in verschiedenen Kontexten je nach Bedarf verwenden zu können

  • es ist unrealistisch, es mit einem eingeworfenen Aufruf Java zu überlassen wieviele der begrenzen wichtigen Systemressourcen auf welche Weise genutzt werden,

Nebenläufigkeit braucht es an wenigen wichtigen Programmstellen wie in Webprogrammen (wie auch ein solches Forum hier) die Bearbeitung eines User-Requests,
da sollen nicht 10 User warten müssen, weil ein einziger seine 10 sec-Suche in 10 Threads beschleunigen will (!)

man kann und sollte natürlich zum Ziel haben, diese Suche besser hinzubekommen,
aber kontrolliert mit Blick auf Auslastung von einer komplexen programmierten Komponente, nicht generisch ohne Rücksicht

  • von den überschaubar vielen bzw. wenigen Problemen, die nebenläufige Ausführung erfordern, was immer entsprechenden Programmieraufwand auch lohnt,
    sind die wenigsten so simpel vollkommen disjunkt problemlos automatisch aufzuteilen wie ein einfaches parallel() auf einen Stream,

Programmierbeispiele zu Threads kümmern sich zurecht um interagierende Komponenten wie Producer/ Consumer,
nicht darum String-Operationen in mehreren Threads statt einem durchzuführen…,
auch die Beispiele User-Requests/ Suche müssen sich zumindest um DB-Ressourcen kümmern

Der Vergleich bzw. die Nachfrage nach „Swing mit Scala“ hinkt auf mehreren Ebenen - ganz vordergründig schon auf der, dass Swing eben Objektorientiert ist, und einige (selbst in dieser Hinsicht fragwürdige) Konzepte aus den 90ern mitschleift. Andererseits „enthält“ gerade Swing ein Paradebeispiel für das, was Funktionale Programmierung im Kern ausmacht: Funktionen sind first-class-citizens, und neuerdings
button.addActionListener(e -> someMethod());
schreiben zu können, ist schon praktisch :wink:

Das Argument der Parallelisierung einfach so wegzuwischen greift auch etwas zu kurz. Gerade weil man Funktionale Elemente (oder sogar direkt Scala) weniger im GUI unterbringen würde, sondern eher im „backend“, wo irgendwelche Datentransformationen gemacht werden. Sicher besteht die Gefahr, dass das falsch verwendet wird. Die Befürchtung, jeder könnte unbedacht und überall jede furz-for-Schleife durch stream().parallel() ersetzen ist nicht ganz ungerechtfertigt. Aber die Möglichkeiten, die sich durch den (schon mehrfach an anderen Stellen angesprochenen) Übergang von externer zu interner Iteration ergeben, sind gewaltig. Angenommen, du hast eine for-Schleife wie

List<Result> results = new ArrayList<Result>();
for (int i=0; i<largeList.size(); i++)
{
    Element element = largeList.get(i);
    if (isImportant(element))
    {
        Result result = process(element);
        results.add(result);
    }
}

Und nun stellst du fest, dass das zeitkritisch ist. Der Benutzer wartet 5 Minuten, bis die Liste abgearbeitet ist, und in dieser Zeit glüht ein CPU-Kern, und die anderen langweilen sich.

Nun könnte man überlegen, das zu parallelisieren. Hmja, die Liste in 4 Teile teilen, … aber erstmal die „important“ Elemente in eine eigene Liste kopieren. Hui, das sind viele. Da wird der Speicher eng. Und dann… einen ThreadPoolExecutor anwerfen. Nicht vergessen auf die Ergebnisse zu warten, und „shutdown“ aufzurufen, sonst idlet der so rum. Joa, oder einfach

List<Result> results = largeList.stream().parallel()
    .filter(e -> isImportant())
    .map(e -> process(e))
    .collect(toList());

und schon dauert das ganze nur noch 2 Minuten. Und auf dem nächsten Rechner nurnoch 1 Minute. Und mit Java 10 und GPU-Unterstützung nurnoch 10 Sekunden. Ohne eine Zeile Code zu ändern.

Sicher, oft geht es bei Parallelisierung nicht um Numbercrunching, sondern daum Latenzen zu verstecken, aber da jetzt ins Detail zu gehen, würde wohl zu weit führen - das ist ja auch einer der Punkte, um die es in diesem Thread ursprünglich wohl gar nicht so sehr gehen sollte: Das reasoning sehe ich in mancher Hinsicht ähnlich sowohl wie der Threadersteller, als auch wie SlaterB: Theoretisch-formal ist es schön, zu wissen, dass eine Funktion eine Funktion ist, ohne Zustand, sie macht irgendwas und liefert das Ergebnis, EOD. Andererseits KANN es „unangenehm“ sein, wenn man eben mal NICHT an einem der (imperativen) Schritte einen Breakpoint setzen und den Debugger anwerfen kann, um dann 3 der (imperativen) Schritte schrittweise (sic) ablaufen zu lassen, um zu sehen, was da schiefgeht.

Oder ganz platt: Funktionale Konzepte sind wie Salz. Kein Grundnahrungsmittel, aber ein bißchen davon kann, an der richtigen Stelle vernünftig eingesetzt, das Mahl etwas besser machen.