Haskell



  • CStoll schrieb:

    Die meisten Laufzeitfehler, die mir in letzter Zeit untergekommen sind, basierten auf Rechen- oder Denkfehlern und Daten, die erst zur Laufzeit bekannt sind. Sowas kannst du nicht diagnostizieren indem du es in ein Typ-Korsett presst.

    Es kann sich lohnen, "typische" Fehler durchs Typsystem zu verbieten. Ich stelle mir als Beispiel vor, daß man so erzwingt, daß socket::bind aufgerufen wird. Erspart einem auch den Blick ins Handbuch.
    Das Korsett kann ich allerdings erst basteln, wenn ich das richtige Vorgehen herausgefunden habe.
    Aber kaum der Rede wert. Die Arbeitszeitfreßfehler tauchen dort gar nicht auf.



  • ipsec schrieb:

    Wo du gerade nochmal Agda erwähnst. Du sagst (sinngemäß) Agda ist nicht turingvollständig, weil Endlosschleifen nicht möglich sind, trotzdem sind endlose Programme durch Corekursionen möglich. Für mich ist das ein Widerspruch. Wenn es nicht turingvollständig ist, kann man damit nicht alle Programme schreiben (wie praktisch relevant diese Beschränkung auch immer sein möge). Wenn man solche Programme jetzt doch schreiben kann, ist es doch turingvollständig.

    Das bedeutet nur, dass man dennoch Programme schreiben kann, die unendlich lange laufen. Die Sprache wird deshalb aber nicht turingvollständig. Das Halteproblem etwa existiert in Agda nicht. Du kannst einen Algorithmus schreiben, der sagen kann, wann ein Agda-Programm terminiert. Zwei mögliche Ausgaben des Programms sind etwa, dass das Programm immer terminiert, oder dass es terminiert, sobald der Benutzer die "q"-Taste gedrückt hat. So ein Programm kannst du für eine turingvollständige Sprache nicht schreiben.

    audacia schrieb:

    ertes schrieb:

    Haskell ist besser als C++ für Serverprogramme und Webanwendungen. (Darauf ist bisher keiner richtig eingegangen. Offenbar habe ich da wohl eine echte Schwachstelle von C++ erwischt.)

    Ungefähr niemand macht Webentwicklung in C++; warum sollte man auch. Praktisch jeder benutzt ASP.NET oder PHP, Ausnahmen bestätigen die Regel.

    Aber Server-Programmierung wird doch in C++ gemacht, oder irre ich mich? Und Webentwicklung macht niemand in C++, weil es so ungeheuer schwierig ist. Da fängt es ja schon damit an, dass es kaum brauchbare Bibliotheken dafür gibt, ganz zu schweigen von einem richtigen Framework.

    CStoll schrieb:

    bei den arithmetischen Funktionen hast du schließlich schon wieder einen Rückzieher gemacht.

    Nö, hab ich nicht.

    Wo ist dann die Erkläung, wie man Subtraktion und Wurzel typsicher miteinander kombinieren kann. Du hast zwar behauptet, daß das ohne Laufzeitpüfungen geht, aber den Beweis hast du unterschlagen.

    Ja, weil ich zufällig auch noch ein Leben habe. Außerdem habe ich die Erklärung gegeben, nur eben ohne Beispielcode. Ich habe einfach Besseres zu tun als eine Stunde lang für dich ein Code-Beispiel auszuarbeiten. Ja, es dauert wirklich so lange, weil Haskell (und jetzt sage ich es schon wieder) nicht dafür geeignet ist. Letzter Beitrag zu dem Thema.

    Du bemerkst anscheinend gar nicht, wie du dir selber widerspichst. Auf der einen Seite sagst du, daß Haskell typische C++ Fehler zur Compilezeit abfangen kann, auf der anderen Seite kommt dann

    HASKELL IST DAFÜR NICHT GEEIGNET!

    Ein typischer C++-Fehler ist für dich, wenn sqrt eine negative Zahl bekommt? Wie es aussieht, bist du in C++ genauso fit wie in Haskell. Also meine häufigsten Fehler in C++ lagen an falschen Schleifen, unüberschaubarer Vererbung, Zeigerprobleme, nicht oder zu früh freigegebene Resourcen, etc.

    Das liegt aber nur daran, daß Haskell viele Aspekte hinter den Kulissen abwickelt, die der C++ Entwickler direkte beeinflussen kann.

    Da bin ich ja mal gespannt. Zum Beispiel?

    Außerdem kannst du die falsche Verwendung von Datenypen in C++ genauso unterbinden wie in Haskell (ist nur ein wenig aufwendiger, aber machbar). Die meisten Laufzeitfehler, die mir in letzter Zeit untergekommen sind, basierten auf Rechen- oder Denkfehlern und Daten, die erst zur Laufzeit bekannt sind. Sowas kannst du nicht diagnostizieren indem du es in ein Typ-Korsett presst.

    Datentypen beziehen sich auf Werte. In Haskell fällt aber wesentlich mehr in die Klasse Werte als in C++. Funktionen etwa sind auch Werte und haben einen Typen. Kontrollstrukturen (Schleifen, sofern man sie braucht, Exceptions, sogar Labels und Sprünge) sind normale Werte und haben einen Typen. Die gesamte Programmlogik hat einen Typen. Rechenfehler werden nicht abgefangen, aber Denkfehler, die sich auf die Programmlogik beziehen, sehr wohl. Du hast referentielle Transparenz (Entsprechung in C++ wäre etwa: ist f(x) = 3, dann kannst du überall in deinem Programm den Aufruf f(x) durch das Ergebnis 3 ersetzen, ohne dein Programm zu verändern) und kannst durch lazy evaluation viele Algorithmen auf eine Art schreiben, die den Datenfluss expliziter und deshalb weniger fehleranfällig macht. Wie oft hat man ein falsches Ergebnis, weil man Zuweisungen in der falschen Reihenfolge geschrieben hat?

    Ich hab' den Shootout jetzt nicht im Detail analysiert, aber woher willst du wissen, wie gut die einzelnen Kandidaten tatsächlich optimiert worden sind?

    Indem ich sie mir ansehe?

    Klar, wenn man die Möglichkeiten einer Sprache einschränkt, kann man innerhalb dieser Möglichkeiten irgendwann alles beweisen. Das Problem ist, daß du mit einer solchen Sprache schneller an die Grenzen dessen kommst, was du darstellen kannst.

    Also ich weiß nicht. Ein komplettes HTTP-Framework gibt es bereits in Agda. Dieses Framework demonstriert auch schön, wie man in Agda all die schönen Sachen machen kann, die du willst, und die in Haskell nur sehr aufwendig zu realisieren sind.

    Du wolltest doch Fehlerfreiheit durch Typsicherheit ausdrücken (oder war das einer der anderen Haskell-Anhänger - jedenfalls kam von einem von euch die Aussage "wenn es sich compilieren lässt, ist es fehlerfrei"?).

    Meine Aussage war, dass ein Programm, das kompiliert, in den meisten Fällen auch funktioniert. Ich schreibe meine Programme so, dass ich nur selten Fehler zur Laufzeit finden muss. Und besonders viel Numerik kommt in meinen Programmen nicht vor, daher habe ich auch selten arithmetische Bugs. Die statisch zu prüfen würde aus einem effizienten 1000-Zeilen-Programm ein sehr ineffizientes, kaum zu überblickendes 100000-Zeilen-Programm machen. Das ist auch der Grund, warum ich dir keine Code-Beispiele gebe. Ich nehme mir nicht ein ganzes Wochenende Zeit, nur um dir zu beweisen, dass es möglich ist, auch solche Beweise zu führen.

    Außerdem habe ich dir nicht widersprochen, als du sagtest, dass Werte, die von außen reinkommen, auch zur Laufzeit geprüft werden müssen. Diese Prüfung besteht darin, etwa den hereinkommenden String richtig in die Konstruktoren des Datentyps zu verpacken, der den Wert in das statisch geprüfte System nimmt. Dafür brauchst du einen Algorithmus. Wenn dieser Algorithmus etwa eine Benutzereingabe in das x in sqrt(x - 1) einsetzt, dann muss er so geschrieben sein, dass statisch garantiert werden kann, dass x ≥ 1 gilt, ansonsten kompiliert er nicht. Den Aufwand dafür schätze ich auf 500-1000 Zeilen Code mit allem drum und dran. Vielleicht verstehst du jetzt, warum ich dir kein Code-Beispiel gebe, und warum ich ständig immer wieder betone, dass du dir lieber Agda als Haskell anschauen solltest, wenn du sowas willst.

    volkard schrieb:

    ertes schrieb:

    Haskell ist typensicher und Programme, die kompilieren, funktionieren meistens auch. Viele Bugs, die in C++ erst zur Laufzeit auftreten würden, fängt in Haskell bereits der Compiler ab. (Eure Interpretation: Ich würde meine Programme nie testen. Ich würde meine Programme so schreiben, dass jede noch so kleine Kleinigkeit statisch garantiert wird.)

    Das rührt aus einem Mißverständnis her.
    Jeder, der "Effektiv C++ programmieren" gelesen und verstanden hat, macht auch in C++ viele Bugs, die in vielen anderen Sprachen erst zur Laufzeit auftreten würden, zu Compilerfehlern. Wir versuchten zu ergründen, was Haskell da an Mehrwert hat. Und Du bliebst für uns enorm schwammig widersprüchlich leer, weil Du einerseits Codebeispiele gezeigt hast, die dann doch zu extrem waren und Du sie selber nicht verwendest, und andererseits Codebeispiele zeigtest, die verwendbar sind und auch in modernem C++ eine Selbstverständlichkeit sind. Es war kein Mehrwert ersichtlich, obwohl Du dauernd den Mehrwert betont hast. Verwirrend. Also fragt man nach. Und es kommt nur weiter *******.
    Wir gingen dummerweise davon aus, daß Du C++ total gut kannst. Weil Du es mehrfach behauptet hast. Ab jetzt spreche ich es auch Dir ab. Es gibt viele mehrjährige C++-Programmierer, die nicht gut sind. Es würde einfach gut erklären, warum Du Dich in Haskell so verknallt hast. Fröhliche Ostern!

    Siehe oben. Nur weil du meine Argumente nicht verstehst, brauchst du nicht mit persönlichen Angriffen loslegen. Das ist übrigens gemeinhin ein Hinweis dafür, dass dir selber die Argumente ausgehen.



  • ertes schrieb:

    Du bemerkst anscheinend gar nicht, wie du dir selber widerspichst. Auf der einen Seite sagst du, daß Haskell typische C++ Fehler zur Compilezeit abfangen kann, auf der anderen Seite kommt dann

    HASKELL IST DAFÜR NICHT GEEIGNET!

    Ein typischer C++-Fehler ist für dich, wenn sqrt eine negative Zahl bekommt? Wie es aussieht, bist du in C++ genauso fit wie in Haskell. Also meine häufigsten Fehler in C++ lagen an falschen Schleifen, unüberschaubarer Vererbung, Zeigerprobleme, nicht oder zu früh freigegebene Resourcen, etc.

    Das liegt aber nur daran, daß Haskell viele Aspekte hinter den Kulissen abwickelt, die der C++ Entwickler direkte beeinflussen kann.

    Da bin ich ja mal gespannt. Zum Beispiel?

    Die Beispiele hast du doch selber gerade genannt: In C++ kann man direkt mit dem Speicher hantieren, muß dann aber mit den Konsequenzen umgehen können. In C++ kann man seine Daten auch in vernünftige Container packen, die einem derartige Probleme dann abnehmen. In Haskell hast du den Vorteil, daß du von Haus aus von der Low-Level Speicherverwaltung abgekoppelt bist und sich schon ein schlauer Compiler-Entwickler um deren Risiken gekümmert hat.

    Um mal weg von der Arithmetik zu kommen (ist dir wohl ohnehin zu abstrakt), ein typischeres Problem in C++ sind ungültige Indizes. Ich kann in beiden Sprachen eine Funktion schreiben, die mir das n-te Element einer Liste liefert - und die werden in beiden Sprachen Probleme bekommen, wenn ich einen Index jenseits der Listen-Größe angebe. Wie sich diese Probleme äußern, mag jetzt unterschiedlich sein, aber auf jeden Fall brauche ich Code, der zur Laufzeit damit umgehen kann.
    Du kannst jetzt gerne einwenden, daß du den drumherumliegenden Algorithmus so anpassen kannst daß der Fall nicht vorkommt - aber ich kann das genausogut in C++ 😉

    Außerdem kannst du die falsche Verwendung von Datenypen in C++ genauso unterbinden wie in Haskell (ist nur ein wenig aufwendiger, aber machbar). Die meisten Laufzeitfehler, die mir in letzter Zeit untergekommen sind, basierten auf Rechen- oder Denkfehlern und Daten, die erst zur Laufzeit bekannt sind. Sowas kannst du nicht diagnostizieren indem du es in ein Typ-Korsett presst.

    Datentypen beziehen sich auf Werte. In Haskell fällt aber wesentlich mehr in die Klasse Werte als in C++. Funktionen etwa sind auch Werte und haben einen Typen. Kontrollstrukturen (Schleifen, sofern man sie braucht, Exceptions, sogar Labels und Sprünge) sind normale Werte und haben einen Typen. Die gesamte Programmlogik hat einen Typen. Rechenfehler werden nicht abgefangen, aber Denkfehler, die sich auf die Programmlogik beziehen, sehr wohl. Du hast referentielle Transparenz (Entsprechung in C++ wäre etwa: ist f(x) = 3, dann kannst du überall in deinem Programm den Aufruf f(x) durch das Ergebnis 3 ersetzen, ohne dein Programm zu verändern) und kannst durch lazy evaluation viele Algorithmen auf eine Art schreiben, die den Datenfluss expliziter und deshalb weniger fehleranfällig macht. Wie oft hat man ein falsches Ergebnis, weil man Zuweisungen in der falschen Reihenfolge geschrieben hat?

    kommt bei mir eher seltener vor.

    Daß man Funktionen auch wieder als Werte betrachten kann, ist vielleicht ein Vorteil (wobei C++ auch Funktionszeiger und Funktoren anbietet), daß man Kontrollstrukturen auch als Werte ansehen kann, liegt vermutlich am Konzept der funktionalen Programmierung. Und referenzielle Transparenz habe ich auch noch nicht wirklich benötigt - wenn es mir zu umständlich ist, den Wert von f(x) an mehreren Stellen im Programm ausrechnen zu lassen, packe ich ihn halt in eine Hilfsvariable.

    Klar, wenn man die Möglichkeiten einer Sprache einschränkt, kann man innerhalb dieser Möglichkeiten irgendwann alles beweisen. Das Problem ist, daß du mit einer solchen Sprache schneller an die Grenzen dessen kommst, was du darstellen kannst.

    Also ich weiß nicht. Ein komplettes HTTP-Framework gibt es bereits in Agda. Dieses Framework demonstriert auch schön, wie man in Agda all die schönen Sachen machen kann, die du willst, und die in Haskell nur sehr aufwendig zu realisieren sind.

    Es ist kein Problem, für einen speziellen Anwendungsbereich eine nicht-turingvollständige Sprache zu verwenden, solange es für das reicht, was du damit vorhast. Und zu deinem Glück fallen mir aus dem Stegreif auch nur akademische Beispiele ein, die die Grenzen der Tuing-Vollständigkeit ausnutzen und mit so einer "verkleinerten" Sprache nicht lösbar wären.

    Du wolltest doch Fehlerfreiheit durch Typsicherheit ausdrücken (oder war das einer der anderen Haskell-Anhänger - jedenfalls kam von einem von euch die Aussage "wenn es sich compilieren lässt, ist es fehlerfrei"?).

    Meine Aussage war, dass ein Programm, das kompiliert, in den meisten Fällen auch funktioniert. Ich schreibe meine Programme so, dass ich nur selten Fehler zur Laufzeit finden muss. Und besonders viel Numerik kommt in meinen Programmen nicht vor, daher habe ich auch selten arithmetische Bugs. Die statisch zu prüfen würde aus einem effizienten 1000-Zeilen-Programm ein sehr ineffizientes, kaum zu überblickendes 100000-Zeilen-Programm machen. Das ist auch der Grund, warum ich dir keine Code-Beispiele gebe. Ich nehme mir nicht ein ganzes Wochenende Zeit, nur um dir zu beweisen, dass es möglich ist, auch solche Beweise zu führen.

    Darf man erfahren, was für Programme du so schreibst, bei denen du ohne Arithmetik auskommst? Ich habe beruflich mit Anwendungen zu tun, die einiges zu rechnen, vergleichen und auszuwerten haben, also beschäftige ich mich naturgemäß ein wenig stärker mit dem arithmetischen Aspekt.

    volkard schrieb:

    ertes schrieb:

    Haskell ist typensicher und Programme, die kompilieren, funktionieren meistens auch. Viele Bugs, die in C++ erst zur Laufzeit auftreten würden, fängt in Haskell bereits der Compiler ab. (Eure Interpretation: Ich würde meine Programme nie testen. Ich würde meine Programme so schreiben, dass jede noch so kleine Kleinigkeit statisch garantiert wird.)

    Das rührt aus einem Mißverständnis her.
    Jeder, der "Effektiv C++ programmieren" gelesen und verstanden hat, macht auch in C++ viele Bugs, die in vielen anderen Sprachen erst zur Laufzeit auftreten würden, zu Compilerfehlern. Wir versuchten zu ergründen, was Haskell da an Mehrwert hat. Und Du bliebst für uns enorm schwammig widersprüchlich leer, weil Du einerseits Codebeispiele gezeigt hast, die dann doch zu extrem waren und Du sie selber nicht verwendest, und andererseits Codebeispiele zeigtest, die verwendbar sind und auch in modernem C++ eine Selbstverständlichkeit sind. Es war kein Mehrwert ersichtlich, obwohl Du dauernd den Mehrwert betont hast. Verwirrend. Also fragt man nach. Und es kommt nur weiter *******.
    Wir gingen dummerweise davon aus, daß Du C++ total gut kannst. Weil Du es mehrfach behauptet hast. Ab jetzt spreche ich es auch Dir ab. Es gibt viele mehrjährige C++-Programmierer, die nicht gut sind. Es würde einfach gut erklären, warum Du Dich in Haskell so verknallt hast. Fröhliche Ostern!

    Siehe oben. Nur weil du meine Argumente nicht verstehst, brauchst du nicht mit persönlichen Angriffen loslegen. Das ist übrigens gemeinhin ein Hinweis dafür, dass dir selber die Argumente ausgehen.

    Die Tatsache, daß du ständig die selben Thesen wiederholst, kann man auch als Hinweis ansehen, daß dir die Argumente ausgehen.



  • ertes schrieb:

    HALLO, KANNST DU LESEN ODER WAS?

    ertes schrieb:

    Entweder du bist wirklich strohdumm

    ertes schrieb:

    Nur weil du meine Argumente nicht verstehst, brauchst du nicht mit persönlichen Angriffen loslegen. Das ist übrigens gemeinhin ein Hinweis dafür, dass dir selber die Argumente ausgehen.

    LOL 🤡



  • CStoll schrieb:

    ertes schrieb:

    Das liegt aber nur daran, daß Haskell viele Aspekte hinter den Kulissen abwickelt, die der C++ Entwickler direkte beeinflussen kann.

    Da bin ich ja mal gespannt. Zum Beispiel?

    Die Beispiele hast du doch selber gerade genannt: In C++ kann man direkt mit dem Speicher hantieren, muß dann aber mit den Konsequenzen umgehen können. In C++ kann man seine Daten auch in vernünftige Container packen, die einem derartige Probleme dann abnehmen. In Haskell hast du den Vorteil, daß du von Haus aus von der Low-Level Speicherverwaltung abgekoppelt bist und sich schon ein schlauer Compiler-Entwickler um deren Risiken gekümmert hat.

    Zeiger, "vernünftige Container" (also effiziente Container durch Allokierung und direkte Manipulation von Speicher) und die damit verbundenen Probleme gibt es in Haskell auch (siehe Foreign.*), wenn man möchte. Die gängigen, schnellen Datenstrukturen verwenden teilweise diese Möglichkeit. Haskell ist eine Programmiersprache, kein Algebrasystem.

    Um mal weg von der Arithmetik zu kommen (ist dir wohl ohnehin zu abstrakt), ein typischeres Problem in C++ sind ungültige Indizes. Ich kann in beiden Sprachen eine Funktion schreiben, die mir das n-te Element einer Liste liefert - und die werden in beiden Sprachen Probleme bekommen, wenn ich einen Index jenseits der Listen-Größe angebe. Wie sich diese Probleme äußern, mag jetzt unterschiedlich sein, aber auf jeden Fall brauche ich Code, der zur Laufzeit damit umgehen kann.
    Du kannst jetzt gerne einwenden, daß du den drumherumliegenden Algorithmus so anpassen kannst daß der Fall nicht vorkommt - aber ich kann das genausogut in C++ 😉

    Sowas kann man statisch prüfen. Was man dafür braucht, sind sog. dependent types. Die gibt es in Haskell zwar nicht, aber man kann mit ein paar Typentricks diese bis zu einem gewissen Grad simulieren. Das bedeutet, man bekommt Arrays mit statischen Längengarantien, sodass man keinen Laufzeitaufwand mehr hat.

    Daß man Funktionen auch wieder als Werte betrachten kann, ist vielleicht ein Vorteil (wobei C++ auch Funktionszeiger und Funktoren anbietet), daß man Kontrollstrukturen auch als Werte ansehen kann, liegt vermutlich am Konzept der funktionalen Programmierung. Und referenzielle Transparenz habe ich auch noch nicht wirklich benötigt - wenn es mir zu umständlich ist, den Wert von f(x) an mehreren Stellen im Programm ausrechnen zu lassen, packe ich ihn halt in eine Hilfsvariable.

    Niemand benötigt referentielle Transparenz, ebensowenig wie irgendjemand Hochsprachen benötigt. Sie hat übrigens auch wenig mit Optimierung zu tun, obwohl sie dafür auch genutzt werden kann. Das (also CSE) macht GHC allerdings bewusst nicht, weil das die Semantik der Sprache beeinflusst. Hintergrund ist, dass es unentscheidbar ist, ob das Programm durch die Optimierung weniger oder mehr Speicher braucht. Wenn man einen berechneten Wert wiederverwenden will, sollte man ihm einen Namen geben (in Haskell).

    Es ist kein Problem, für einen speziellen Anwendungsbereich eine nicht-turingvollständige Sprache zu verwenden, solange es für das reicht, was du damit vorhast. Und zu deinem Glück fallen mir aus dem Stegreif auch nur akademische Beispiele ein, die die Grenzen der Tuing-Vollständigkeit ausnutzen und mit so einer "verkleinerten" Sprache nicht lösbar wären.

    Agda ist allerdings als Allzwecksprache gedacht, mit Schwerpunkt auf Beweisführung.

    Darf man erfahren, was für Programme du so schreibst, bei denen du ohne Arithmetik auskommst? Ich habe beruflich mit Anwendungen zu tun, die einiges zu rechnen, vergleichen und auszuwerten haben, also beschäftige ich mich naturgemäß ein wenig stärker mit dem arithmetischen Aspekt.

    Hauptsächlich verarbeite ich Daten. Entgegennehmen, prüfen, speichern, abrufen, etc. Server und Weboberflächen. Ganz ohne (expliziter) Arithmetik komme ich natürlich nicht aus, aber viel habe ich da nicht. Das ist so die Durchschnittsanwendung, die ich schreibe.

    Die Tatsache, daß du ständig die selben Thesen wiederholst, kann man auch als Hinweis ansehen, daß dir die Argumente ausgehen.

    Diese "Thesen" sind meine Argumente.

    cooky451 schrieb:

    ertes schrieb:

    HALLO, KANNST DU LESEN ODER WAS?

    ertes schrieb:

    Entweder du bist wirklich strohdumm

    ertes schrieb:

    Nur weil du meine Argumente nicht verstehst, brauchst du nicht mit persönlichen Angriffen loslegen. Das ist übrigens gemeinhin ein Hinweis dafür, dass dir selber die Argumente ausgehen.

    LOL 🤡

    Man kann die Tatsachen immer verschleiern, wenn man unvollständig zitiert oder die Bedeutung des Fragezeichens nicht versteht. 😉 Außerdem bleibt meine Aussage davon unbetroffen.



  • ertes schrieb:

    CStoll schrieb:

    ertes schrieb:

    Das liegt aber nur daran, daß Haskell viele Aspekte hinter den Kulissen abwickelt, die der C++ Entwickler direkte beeinflussen kann.

    Da bin ich ja mal gespannt. Zum Beispiel?

    Die Beispiele hast du doch selber gerade genannt: In C++ kann man direkt mit dem Speicher hantieren, muß dann aber mit den Konsequenzen umgehen können. In C++ kann man seine Daten auch in vernünftige Container packen, die einem derartige Probleme dann abnehmen. In Haskell hast du den Vorteil, daß du von Haus aus von der Low-Level Speicherverwaltung abgekoppelt bist und sich schon ein schlauer Compiler-Entwickler um deren Risiken gekümmert hat.

    Zeiger, "vernünftige Container" (also effiziente Container durch Allokierung und direkte Manipulation von Speicher) und die damit verbundenen Probleme gibt es in Haskell auch (siehe Foreign.*), wenn man möchte. Die gängigen, schnellen Datenstrukturen verwenden teilweise diese Möglichkeit. Haskell ist eine Programmiersprache, kein Algebrasystem.

    Liest du überhaupt deine eigenen Beiträge? Ich hab' nämlich langsam das Gefühl, daß wir aneinander vorbeireden.
    Nochmal langsam zum Mitschreiben:
    In C++ kann man viele Dummheiten machen, wenn man tief genug eintaucht (und leider gibt es noch zu viele Programmierer, die nur diesen Stil kennengelernt haben). Aber in C++ gibt es auch genug Möglichkeiten, richtig und fehlersicher zu programmieren (volkard hatte schon "Effektiv C++ programmieren" als Referenz dafür genannt). Aber das erfordert natürlich ein paar Kenntnisse und Disziplin.
    In Haskell sind die High-Level Funktionen vermutlich einfacher anzuwenden und werden deshalb eher genutzt, während Low-Level mit Mehraufwand verbunden ist. Darum gibt es vermutlich mehr "gute" Haskell-Programmierer als "gute" C++ Programmierer.

    Um mal weg von der Arithmetik zu kommen (ist dir wohl ohnehin zu abstrakt), ein typischeres Problem in C++ sind ungültige Indizes. Ich kann in beiden Sprachen eine Funktion schreiben, die mir das n-te Element einer Liste liefert - und die werden in beiden Sprachen Probleme bekommen, wenn ich einen Index jenseits der Listen-Größe angebe. Wie sich diese Probleme äußern, mag jetzt unterschiedlich sein, aber auf jeden Fall brauche ich Code, der zur Laufzeit damit umgehen kann.
    Du kannst jetzt gerne einwenden, daß du den drumherumliegenden Algorithmus so anpassen kannst daß der Fall nicht vorkommt - aber ich kann das genausogut in C++ 😉

    Sowas kann man statisch prüfen. Was man dafür braucht, sind sog. dependent types. Die gibt es in Haskell zwar nicht, aber man kann mit ein paar Typentricks diese bis zu einem gewissen Grad simulieren. Das bedeutet, man bekommt Arrays mit statischen Längengarantien, sodass man keinen Laufzeitaufwand mehr hat.

    Arrays mit statischen Längengarantien kann man auch mit C++ aufbauen, deswegen muß der Nutzer dieses Arrays trotzdem sicherstellen, daß keine ungültigen Indizes übergeben werden. Der einzige Unterschied besteht da in der Art der Fehlerbehandlung durch den Anwender dieses Arrays.

    Daß man Funktionen auch wieder als Werte betrachten kann, ist vielleicht ein Vorteil (wobei C++ auch Funktionszeiger und Funktoren anbietet), daß man Kontrollstrukturen auch als Werte ansehen kann, liegt vermutlich am Konzept der funktionalen Programmierung. Und referenzielle Transparenz habe ich auch noch nicht wirklich benötigt - wenn es mir zu umständlich ist, den Wert von f(x) an mehreren Stellen im Programm ausrechnen zu lassen, packe ich ihn halt in eine Hilfsvariable.

    Niemand benötigt referentielle Transparenz, ebensowenig wie irgendjemand Hochsprachen benötigt. Sie hat übrigens auch wenig mit Optimierung zu tun, obwohl sie dafür auch genutzt werden kann. Das (also CSE) macht GHC allerdings bewusst nicht, weil das die Semantik der Sprache beeinflusst. Hintergrund ist, dass es unentscheidbar ist, ob das Programm durch die Optimierung weniger oder mehr Speicher braucht. Wenn man einen berechneten Wert wiederverwenden will, sollte man ihm einen Namen geben (in Haskell).

    Wenn es niemand benötigt, wieso preist du es dann als großen Vorteil an?

    Es ist kein Problem, für einen speziellen Anwendungsbereich eine nicht-turingvollständige Sprache zu verwenden, solange es für das reicht, was du damit vorhast. Und zu deinem Glück fallen mir aus dem Stegreif auch nur akademische Beispiele ein, die die Grenzen der Tuing-Vollständigkeit ausnutzen und mit so einer "verkleinerten" Sprache nicht lösbar wären.

    Agda ist allerdings als Allzwecksprache gedacht, mit Schwerpunkt auf Beweisführung.

    Und was willst du damit jetzt aussagen?

    Die Tatsache, daß du ständig die selben Thesen wiederholst, kann man auch als Hinweis ansehen, daß dir die Argumente ausgehen.

    Diese "Thesen" sind meine Argumente.

    Das ist noch lange kein Grund, sie immer wieder zu wiederholen. Durch Wiederholung werden deine Aussagen auch nicht besser, wenn du sie nicht weiter untermauern kannst.



  • CStoll schrieb:

    Arrays mit statischen Längengarantien kann man auch mit C++ aufbauen, deswegen muß der Nutzer dieses Arrays trotzdem sicherstellen, daß keine ungültigen Indizes übergeben werden. Der einzige Unterschied besteht da in der Art der Fehlerbehandlung durch den Anwender dieses Arrays.

    Est es auch kein Problem, einen Index-Typ dafür zu bauen, der zur Laufzeit(!) prüft, ob die arithmetischen Operationen erlaubt sind und gültige Indizes für das Array ergeben. Und jetzt kommt verwendete der Trick vom verlinkten Code:
    Die Funktion mitte(a,b){return a+(b-a)/2;} braucht KEINE Laufzeitüberprüfung, das Ergebis ist immer ein sicherer Index, wenn a und b sichere Indizes waren. Damit läßt sich die binäre Suche OHNE Laufzeitprüfung machen.
    Ich kann's mir nicht verkneifen: 🤡

    ps: Die haben (a+b)/2 benutzt und haben keine Angst vor einem Überlauf.



  • volkard schrieb:

    CStoll schrieb:

    Arrays mit statischen Längengarantien kann man auch mit C++ aufbauen, deswegen muß der Nutzer dieses Arrays trotzdem sicherstellen, daß keine ungültigen Indizes übergeben werden. Der einzige Unterschied besteht da in der Art der Fehlerbehandlung durch den Anwender dieses Arrays.

    Wenn die Arraygröße Comilezeitkonstant ist, ist es auch kein Problem, einen Index-Typ dafür zu bauen, der zur Laufzeit(!) prüft, ob die arithmetischen Operationen erlaubt sind. Und jetzt kommt verwendete der Trick vom verlinkten Code:
    Die Funktion mitte(a,b){return a+(b-a)/2;} braucht KEINE Laufzeitüberprüfung, das Ergebis ist immer ein sicherer Index, wenn a und b sichere Indizes waren. Damit läßt sich die binäre Suche OHNE Laufzeitprüfung machen.
    Ich kann's mir nicht verkneifen: 🤡

    Das kann ich auch, ich brauche dafür nur einen eigenen Index-Typ und entsprechende Operator-/Funktions-Überladungen.



  • CStoll schrieb:

    Liest du überhaupt deine eigenen Beiträge? Ich hab' nämlich langsam das Gefühl, daß wir aneinander vorbeireden.

    Ätsch das macht ihr schon die ganze Zeit, deswegen wunder es mich, dass hier dieser Quatsch weitergemacht wird.

    Auf der eine Seite ist CStoll kann auch theoretisch und konzeptionell über Programmiersprachen reden und auf der Andere Ertes, der nur exemplarisch an Haskell Konzepte erläutern kann mit ein Hauch von Validation/Verifikation(welches Wort ist Richtig)?-System-Know-How.

    Das ist mein Eindruck.



  • Validierung oder Verifikation. Nur weil du gefragt hast 🤡



  • Da der Thread grad passt, ich bin grad dabei ein wenig Haskell zu lernen, und gewöhne mich grad an den Pattern-Matching-Mechanismus.
    Jetzt würde ich gerne eine equals-Funktion (bzw. etwas ähnliches) umsetzen. Ich hätte gehofft, dass Folgendes funktioniert:

    eq :: a -> Bool
    eq n n = True
    eq n m = False
    

    Quasi wie man es aus der Mathematik kennt. Funktioniert das irgendwie und ich habs übersehen, oder muss ich das mit

    eq :: a -> Bool
    eq n m = (n == m)
    

    o.ä. machen?

    Schönen Abend noch.



  • Meines Wissen (und meiner flüchtigen Versuche gerade) geht das nicht. Ganz davon abgesehen ist der Typ aber auch falsch.
    Richtig ist:

    eq :: Eq a => a -> a -> Bool
    eq a b = a == b
    


  • CStoll schrieb:

    Liest du überhaupt deine eigenen Beiträge?

    Ja.

    Ich hab' nämlich langsam das Gefühl, daß wir aneinander vorbeireden.

    Endlich hast du es begriffen. Du verlangst ständig von mir, dass ich Haskell-Features verteidige, die ich überhaupt niemals angepriesen habe, und die Features, die ich angepriesen habe, ignorierst du völlig, und zwar bis einschließlich jetzt. C++ ist keinen Deut besser bei den Features, die du diskutierst und um Längen schlechter bei den Features, die ich wirklich anpreise. Kein Wunder, dass du als C++-Jünger nicht darauf eingehst.

    Nochmal langsam zum Mitschreiben:
    In C++ kann man viele Dummheiten machen, wenn man tief genug eintaucht (und leider gibt es noch zu viele Programmierer, die nur diesen Stil kennengelernt haben). Aber in C++ gibt es auch genug Möglichkeiten, richtig und fehlersicher zu programmieren (volkard hatte schon "Effektiv C++ programmieren" als Referenz dafür genannt). Aber das erfordert natürlich ein paar Kenntnisse und Disziplin.

    In C++ muss eine Bibliothek eben die Garantien erbringen, die in Haskell die Sprache selbst erbringen kann.

    In Haskell sind die High-Level Funktionen vermutlich einfacher anzuwenden und werden deshalb eher genutzt, während Low-Level mit Mehraufwand verbunden ist. Darum gibt es vermutlich mehr "gute" Haskell-Programmierer als "gute" C++ Programmierer.

    Falsch. Low-Level-Funktionen sind in Haskell genauso anzuwenden wie in C++ auch, wahrscheinlich sogar noch leichter. Allerdings hat man da eben Funktionen und Kombinatoren mit richtigen Namen statt symbolische Operatoren (die im Endeffekt auch nur Funktionen sind). Das heißt, Haskell bringt auch die guten alten peek und poke wieder zurück. Daher entsteht oft der Eindruck, dass Haskell nicht so gut für Low-Level-Programmierung geeignet sei.

    Niemand benötigt referentielle Transparenz, ebensowenig wie irgendjemand Hochsprachen benötigt. Sie hat übrigens auch wenig mit Optimierung zu tun, obwohl sie dafür auch genutzt werden kann. Das (also CSE) macht GHC allerdings bewusst nicht, weil das die Semantik der Sprache beeinflusst. Hintergrund ist, dass es unentscheidbar ist, ob das Programm durch die Optimierung weniger oder mehr Speicher braucht. Wenn man einen berechneten Wert wiederverwenden will, sollte man ihm einen Namen geben (in Haskell).

    Wenn es niemand benötigt, wieso preist du es dann als großen Vorteil an?

    Also wenn du die Aussage nicht verstanden hast, hast du dir selber ein Armutszeugnis erteilt. Sorry, dass ich jetzt mit dir wie mit einem Drittklässler rede, aber niemand braucht referentielle Transparent. Niemand braucht überhaupt Programmiersprachen. Es geht schließlich auch mit Lochkarten. Mit anderen Worten: Dass du referentielle Transparenz noch nie gebraucht hast ist eine Aussage wie, "der Himmel ist blau". Ich habe sie auch nie gebraucht.

    Agda ist allerdings als Allzwecksprache gedacht, mit Schwerpunkt auf Beweisführung.

    Und was willst du damit jetzt aussagen?

    Dass du mit Agda jede Art von Programm schreiben kannst. Turing-Unvollständigkeit schränkt den Einsatzbereich einer Sprache nicht im Geringsten ein.

    Das ist noch lange kein Grund, sie immer wieder zu wiederholen. Durch Wiederholung werden deine Aussagen auch nicht besser, wenn du sie nicht weiter untermauern kannst.

    Wie soll ich sie denn hier noch weiter untermauern? Meine Aussagen habe ich mit echten Code-Beispielen untermauert. Und damit meine ich nicht Aussagen, die du mir unterstellst, sondern die ich wirklich gemacht habe. Und meine Aussagen stehen unangefochten immer noch im Raum. Bisher ist immer noch keiner auf meine Aussagen eingegangen. Bisher hat mir keiner einen Vorteil von C++ gegenüber Haskell genannt, sondern immer nur: "Kann C++ doch auch!". Es gibt also schon mal ein paar Dinge, die C++ genauso gut kann wie Haskell – streite ich nicht ab.

    Ich habe aber Dinge genannt, die Haskell wesentlich besser kann als C++. Null Reaktion von den C++-Gläubigen. Klar, warum auch? Ohne Gegenbeispiele müsste man ja zugeben, dass C++ schlechter ist. Warum? C++ kann nichts besser als Haskell, aber Haskell kann vieles besser als C++.

    volkard schrieb:

    CStoll schrieb:

    Arrays mit statischen Längengarantien kann man auch mit C++ aufbauen, deswegen muß der Nutzer dieses Arrays trotzdem sicherstellen, daß keine ungültigen Indizes übergeben werden. Der einzige Unterschied besteht da in der Art der Fehlerbehandlung durch den Anwender dieses Arrays.

    Est es auch kein Problem, einen Index-Typ dafür zu bauen, der zur Laufzeit(!) prüft, ob die arithmetischen Operationen erlaubt sind und gültige Indizes für das Array ergeben. Und jetzt kommt verwendete der Trick vom verlinkten Code:
    Die Funktion mitte(a,b){return a+(b-a)/2;} braucht KEINE Laufzeitüberprüfung, das Ergebis ist immer ein sicherer Index, wenn a und b sichere Indizes waren. Damit läßt sich die binäre Suche OHNE Laufzeitprüfung machen.

    Was genau habt ihr an dem Wort "statisch" nicht verstanden? Das ist keine statische Prüfung, sondern eine Optimierung.

    Zeus schrieb:

    Auf der eine Seite ist CStoll kann auch theoretisch und konzeptionell über Programmiersprachen reden und auf der Andere Ertes, der nur exemplarisch an Haskell Konzepte erläutern kann mit ein Hauch von Validation/Verifikation(welches Wort ist Richtig)?-System-Know-How.

    Nein, CStoll kann eben nicht theoretisch und konzeptionell über Programmiersprachen reden. Das ist ja das Problem; deswegen exemplarisch. Wenn ich anfangen würde mit Typentheorie, würde er gar nichts mehr verstehen und mir noch mehr Blödsinn unterstellen. Mir wurde ja schon mal vorgeworfen, ich würde nur mit Fachbegriffen um mich werfen.

    jungerjünger schrieb:

    eq :: a -> Bool
    

    Der Typ ergibt wenig Sinn für eine Vergleichsfunktion. Die einzige Funktion, die du damit schreiben kannst, ist eine konstante Funktion, etwa: eq = const True. Das liegt daran, dass du über den Typen a alleine nichts weißt. Du brauchst mehr Kontext, z.B. so, wie es ipsec beschrieben hat:

    eq :: Eq aaa → Bool
    eq = (==)



  • Das mit dem Typen hat mir der GHC auch gleich um die Ohren geschleudert. Darauf wollte ich aber nicht hinaus, sondern nur, ob man irgendwie durch Parameternamen Gleichheit von Parametern implizieren kann.



  • ertes schrieb:

    Nochmal langsam zum Mitschreiben:
    In C++ kann man viele Dummheiten machen, wenn man tief genug eintaucht (und leider gibt es noch zu viele Programmierer, die nur diesen Stil kennengelernt haben). Aber in C++ gibt es auch genug Möglichkeiten, richtig und fehlersicher zu programmieren (volkard hatte schon "Effektiv C++ programmieren" als Referenz dafür genannt). Aber das erfordert natürlich ein paar Kenntnisse und Disziplin.

    In C++ muss eine Bibliothek eben die Garantien erbringen, die in Haskell die Sprache selbst erbringen kann.

    In Haskell sind die High-Level Funktionen vermutlich einfacher anzuwenden und werden deshalb eher genutzt, während Low-Level mit Mehraufwand verbunden ist. Darum gibt es vermutlich mehr "gute" Haskell-Programmierer als "gute" C++ Programmierer.

    Falsch. Low-Level-Funktionen sind in Haskell genauso anzuwenden wie in C++ auch, wahrscheinlich sogar noch leichter. Allerdings hat man da eben Funktionen und Kombinatoren mit richtigen Namen statt symbolische Operatoren (die im Endeffekt auch nur Funktionen sind). Das heißt, Haskell bringt auch die guten alten peek und poke wieder zurück. Daher entsteht oft der Eindruck, dass Haskell nicht so gut für Low-Level-Programmierung geeignet sei.

    Dann ist die Fehlersicherheit wohl eher eine Frage der Didaktik als der Sprache: Haskell-Anfänger lernen erst die sicheren WEge und dann die Dummheiten, C++-Anfänger (leider) genau umgekehrt (ich gehe hier nicht näher auf JW ein)

    Also wenn du die Aussage nicht verstanden hast, hast du dir selber ein Armutszeugnis erteilt. Sorry, dass ich jetzt mit dir wie mit einem Drittklässler rede, aber niemand braucht referentielle Transparent. Niemand braucht überhaupt Programmiersprachen. Es geht schließlich auch mit Lochkarten. Mit anderen Worten: Dass du referentielle Transparenz noch nie gebraucht hast ist eine Aussage wie, "der Himmel ist blau". Ich habe sie auch nie gebraucht.

    OK, dann erklär mir doch mal, welchen Vorteil ich daraus ziehen kann, daß ich referentielle Transparenz nutzen kann. Wird mein Programm schneller, wenn ich weiß, daß f(x) immer den selben Wert zurückliefert? Du hast selber gesagt, daß der Haskell-Compiler sowas nicht aus eigenem Antrieb optimiert - und eine Hilfsvariable für den Wert von f(x) anlegen zu können ist nicht an die Sprache gebunden.

    Agda ist allerdings als Allzwecksprache gedacht, mit Schwerpunkt auf Beweisführung.

    Und was willst du damit jetzt aussagen?

    Dass du mit Agda jede Art von Programm schreiben kannst. Turing-Unvollständigkeit schränkt den Einsatzbereich einer Sprache nicht im Geringsten ein.

    Wie gesagt, mir fallen gerade keine praktisch relevanten turing-vollständigen Probleme ein, aber das heißt nicht, daß es sie nicht gibt.

    Ich habe aber Dinge genannt, die Haskell wesentlich besser kann als C++. Null Reaktion von den C++-Gläubigen. Klar, warum auch? Ohne Gegenbeispiele müsste man ja zugeben, dass C++ schlechter ist. Warum? C++ kann nichts besser als Haskell, aber Haskell kann vieles besser als C++.

    Ich fürchte, bei deinen ganzen Beiträgen habe ich den Überblick verloren: Welche echten Vorteile von Haskell hast du denn genannt? Nach 23 Seiten blicke ich auch nicht mehr durch, was von deinen Beiträgen jetzt einen Haskell-Vorteil erklärt hat und was nur meine Aussagen als Unsinn hinstellen wollte.
    (wobei ich dazusagen muß, daß ich nicht genug von Haskell weiß, um seine Nachteile gegenüber C++ auf einen Blick zu erfassen)

    OK, die drei Punkte aus deiner letzten Auflistung:
    - typsicher: kann C++ auch
    - wenn es kompiliert, funktioniert es auch: halte ich immr noch für fragwürdig
    - besser für Web-Anwendungen: eine Zange ist auch besser zum Nägel in die Wand schlagen als ein Schraubendreher - trotzdem nimmt man dafür einen Hammer
    - vergleichbar in der Performance: ist für mich kein Kriterium, egal für welche Seite

    Nein, CStoll kann eben nicht theoretisch und konzeptionell über Programmiersprachen reden. Das ist ja das Problem; deswegen exemplarisch. Wenn ich anfangen würde mit Typentheorie, würde er gar nichts mehr verstehen und mir noch mehr Blödsinn unterstellen. Mir wurde ja schon mal vorgeworfen, ich würde nur mit Fachbegriffen um mich werfen.

    Versuch's doch.



  • ertes schrieb:

    volkard schrieb:

    CStoll schrieb:

    Arrays mit statischen Längengarantien kann man auch mit C++ aufbauen, deswegen muß der Nutzer dieses Arrays trotzdem sicherstellen, daß keine ungültigen Indizes übergeben werden. Der einzige Unterschied besteht da in der Art der Fehlerbehandlung durch den Anwender dieses Arrays.

    Est es auch kein Problem, einen Index-Typ dafür zu bauen, der zur Laufzeit(!) prüft, ob die arithmetischen Operationen erlaubt sind und gültige Indizes für das Array ergeben. Und jetzt kommt verwendete der Trick vom verlinkten Code:
    Die Funktion mitte(a,b){return a+(b-a)/2;} braucht KEINE Laufzeitüberprüfung, das Ergebis ist immer ein sicherer Index, wenn a und b sichere Indizes waren. Damit läßt sich die binäre Suche OHNE Laufzeitprüfung machen.

    Was genau habt ihr an dem Wort "statisch" nicht verstanden? Das ist keine statische Prüfung, sondern eine Optimierung.

    Was ich beschrieben habe, ist statisch und entspricht dem Artikel.

    Die andere Kritik kann ich zurückwerfen, Du ignorierst alles, was Du nicht sofort vestehst, und wiederholst längst widerlegte Argumente (hast die Widerlegung ja nicht verstanden). Wenn wir Dir nicht mehr antworten, liegt es nicht daran, daß Du überlegene Argumente wiederholt hättest, sondern an der Zwecklosigkeit einer Diskussion mit Dir.



  • Ich muss sagen, ich hab mich jetzt (auch aufgrund dieses Threads) endlich mal näher mit Haskell beschäftigt. Ich persönlich finde Haskell eine sehr schöne Sprache mit interessanten Konzepten. Von den funktionalen Sprachen hatte ich bisher nur Kontakt mit Lisp, Haskell spricht mich dabei mehr an.

    Eine direkte Konkurrenz von C++ und Haskell sehe ich aber nicht, die Anwendungsgebiete überschneiden sich nur zum Teil. Auch denke ich nicht, dass Haskell die Allheillösung ist, auf die alle Programmierer gewartet haben (wie man das in manche Posts dieses Threads reininterpretieren könnte - Haskell-Jünger gibt es eben genau so wie C++-Jünger).

    Eine direkte Kritik an Haskell habe ich aber. Bei vielen Beispielen, die ich sehe, wird denke ich mit der Eleganz übertrieben.
    Zum Beispiel hatte ich neulich diese Funktion bei Wikipedia gelesen:

    mf = (. map) . (.) . filter
    

    Das ist eine Point-Free-Implementierung von

    mf criteria operator list = filter criteria (map operator list)
    

    Die 2. Version finde ich sehr verständlich, bei der 1. bekomme ich immer noch ne Schraube im Kopf. An die Haskell-Experten: gibt es eine spezielle Technik, wie man solche Definitionen liest, oder ist das wirklich nur eine Sache der Erfahrung? Wer hätte mit der ersten Version spontan etwas anfangen können?
    Auf jeden Fall erschließt sich mir nicht, warum jemand überhaupt auf die Idee kommt, solche Funktionen zu schreiben (also in der 1. Version). Böse betrachtet ist das reine Eleganzgeilheit wer das abstrakteste Programm schreibt.

    Solche extremen Beispiele sind natürlich selten, allerdings muss ich oft mehrmals nachdenken, was denn eine Funktion jetzt eigentlich macht, während sehr oft auch verständlichere Beschreibungen möglich wären. Das ist natürlich nicht direkt Schuld von Haskell, sondern eher der Programmierer. Ich behaupte aber, dass mir sowas in C++ nicht passiert. Es mag an meiner größeren C++-Erfahrung liegen, aber auch weil C++ solche extrem abstrakten Konstrukte nicht so leicht möglich macht wie Haskell.

    Übrigens eine interessante Äußerung in einer Diskussion (indirekt) dazu:

    > You can use flip as a "wildcard" aswell:

    > listeEtagTot = concatMap (listeEtagArm `flip` cfgTypesTringle) listeArmOrd

    ...

    It took me a fair while (I'm talking on the order of half a minute) to
    figure out what that meant

    "Wow, das ist was, was ich nicht gleich verstanden haben, aber auf gar keinen Fall den Verdacht aufkommen lassen, ich würde mich schwer tun, Haskell zu verstehen, bzw. Haskell wäre allgemein unverständlich"

    Ertes Beispiel ist übrigens wieder sowas.

    ertes schrieb:

    eq = (==)

    Natürlich versteht man das auch, die Obfuscation hält sich hier sehr in Grenzen, trotzdem ist eq a b = a == b verständlicher. Warum schreibt man das so kompakt? Doch nicht um am Ende 2 Zeichen zu sparen, oder?



  • Eine direkte Konkurrenz von C++ und Haskell sehe ich aber nicht, die Anwendungsgebiete überschneiden sich nur zum Teil.

    👍
    Sehe ich genauso.

    Womit ich mir bei Haskell noch schwer tue ist State. Es gibt da zwar einige schöne Konzepte, aber es fällt mir deutlich schwieriger damit umzugehen als z.B. in C++. Das liegt natürlich nicht zuletzt an meiner deutlich größeren Erfahrung mit C++ als mit Haskell.

    Eine direkte Kritik an Haskell habe ich aber. Bei vielen Beispielen, die ich sehe, wird denke ich mit der Eleganz übertrieben.
    Zum Beispiel hatte ich neulich diese Funktion bei Wikipedia gelesen:

    Ja, soetwas finde ich absolut Schwachsinnig und habe ich so auch noch nicht in echtem Code gesehen. Ich würde das ähnlich wie du schreiben, nur mit kürzeren Namen:

    mf p f xs = filter p (map f xs)
    

    Und das nichtmal um Platz zu sparen, sondern weil ich es gewohnt bin, dass ein Predicate p heißt oder eine Liste mit xs abgeküzrt wird.

    trotzdem ist eq a b = a == b verständlicher. Warum schreibt man das so kompakt?

    Ist doch geschmackssache. Subjektiv gefällt mir eq = (==) auch besser, weil ich dann unmittelbar sehe, dass eq das gleiche macht wie (==). Bei der etwas expliziteren Version eq a b = a == b muss man etwas mehr nachdenken.



  • jungerjünger schrieb:

    Das mit dem Typen hat mir der GHC auch gleich um die Ohren geschleudert. Darauf wollte ich aber nicht hinaus, sondern nur, ob man irgendwie durch Parameternamen Gleichheit von Parametern implizieren kann.

    Sorry, ich weiß nicht, was du meinst. Also f x x = … ist ein Syntaxfehler.

    CStoll schrieb:

    ertes schrieb:

    Low-Level-Funktionen sind in Haskell genauso anzuwenden wie in C++ auch, wahrscheinlich sogar noch leichter. Allerdings hat man da eben Funktionen und Kombinatoren mit richtigen Namen statt symbolische Operatoren (die im Endeffekt auch nur Funktionen sind). Das heißt, Haskell bringt auch die guten alten peek und poke wieder zurück. Daher entsteht oft der Eindruck, dass Haskell nicht so gut für Low-Level-Programmierung geeignet sei.

    Dann ist die Fehlersicherheit wohl eher eine Frage der Didaktik als der Sprache: Haskell-Anfänger lernen erst die sicheren WEge und dann die Dummheiten, C++-Anfänger (leider) genau umgekehrt (ich gehe hier nicht näher auf JW ein)

    Du hast immer noch das Typensystem, das dir zur Seite steht (am Anfang wohl eher im Weg, bis du lernst, damit umzugehen). Im Low-Level-Bereich wird dir Haskell allerdings nicht verbieten, einen Null-Pointer zu dereferenzieren. Andererseits kommt es wiederum normalerweise nicht vor, dass du Null-Pointer überhaupt bekommst. Nehmen wir als blödes Beispiel an, du willst eine verkettete Liste im klassischen Sinne: Es gibt also eine Variable und einen Zeiger zum nächsten Glied. In C++ hätte das letzte Glied hier einen Null-Pointer. In Haskell würdest du eher Maybe (Ptr a) benutzen und somit kommt ein Null-Pointer gar nicht vor. Der Versuch, vom letzten Glied ins nächste zu springen endet in einem Typfehler zur Kompilierzeit.

    OK, dann erklär mir doch mal, welchen Vorteil ich daraus ziehen kann, daß ich referentielle Transparenz nutzen kann. Wird mein Programm schneller, wenn ich weiß, daß f(x) immer den selben Wert zurückliefert? Du hast selber gesagt, daß der Haskell-Compiler sowas nicht aus eigenem Antrieb optimiert - und eine Hilfsvariable für den Wert von f(x) anlegen zu können ist nicht an die Sprache gebunden.

    So ist es. Allerdings sind viele andere Optimierungen möglich. Beispiel: Du suchst die kleinste gerade quadratische natürliche Zahl (x² mit x ∈ ℕ), die größer als 110 ist, und nehmen wir an, es gäbe keine simple Formel dafür: Du erstellst erst eine unendliche Liste von 0 beginnend aufwärts, quadrierst alle Elemente, filterst die Ergebnisliste, sodass nur gerade Zahlen übrigbleiben, dann verwirfst du so lange den Anfang der Liste, bis das erste Element größer als 110 ist. Dieses Element ist das Ergebnis. Der Code dazu:

    head . dropWhile (<= 110) . filter even . map (^2) $ [0..]

    Durch äquivalente Umformungen wird die Liste vollständig wegoptimiert und es bleibt dieselbe Schleife übrig, die du in C++ von vornherein geschrieben hättest. Das heißt, die referentielle Transparenz wird ein C++-Programm nicht schneller machen, aber sie ermöglicht dir, neue, elegante Design-Patterns zu nutzen, ohne Performance dafür aufzugeben.

    Wie gesagt, mir fallen gerade keine praktisch relevanten turing-vollständigen Probleme ein, aber das heißt nicht, daß es sie nicht gibt.

    Wie sehr mich das wundert, dass dir keine einfallen. Daran haben sich schon andere Leute die Zähne ausgebissen. Wobei – ein solches Problem fällt mir ein, sogar mit Praxisrelevanz: Schreibe ein Agda-Programm, für das unentscheidbar ist, ob es terminiert. 😉

    Ich fürchte, bei deinen ganzen Beiträgen habe ich den Überblick verloren: Welche echten Vorteile von Haskell hast du denn genannt? Nach 23 Seiten blicke ich auch nicht mehr durch, was von deinen Beiträgen jetzt einen Haskell-Vorteil erklärt hat und was nur meine Aussagen als Unsinn hinstellen wollte.
    (wobei ich dazusagen muß, daß ich nicht genug von Haskell weiß, um seine Nachteile gegenüber C++ auf einen Blick zu erfassen)

    Jetzt kommen wir der Sache näher. Du hast bisher noch keine einzige informierte Aussage über Haskell gemacht. Du kannst doch gar nicht beurteilen, wo welche Sprache besser ist.

    OK, die drei Punkte aus deiner letzten Auflistung:
    - typsicher: kann C++ auch

    Nicht in dem Ausmaß.

    - wenn es kompiliert, funktioniert es auch: halte ich immr noch für fragwürdig

    Dazu gibt es sogar einen Wiki-Eintrag.

    - besser für Web-Anwendungen: eine Zange ist auch besser zum Nägel in die Wand schlagen als ein Schraubendreher - trotzdem nimmt man dafür einen Hammer

    Und was ist der Hammer? Etwa PHP? Python? Ruby? Das kannst du doch gar nicht einschätzen, wie du selber indirekt zugegeben hast. Tu doch nicht so, als würdest du alle Web-Frameworks der Welt kennen, denn mindestens eines davon kennst du nicht: Yesod.

    - vergleichbar in der Performance: ist für mich kein Kriterium, egal für welche Seite

    Bis zu einem gewissen Grad hast du hier Recht. Allerdings muss man auch sehen, dass Haskell eine wesentlich höhere Abstraktionsebene besitzt als die meisten anderen Sprachen. Es hat lange gedauert, bis sich das mit so guter Performance kombinieren ließ. In GHC steckt unglaublich viel Erfahrung. Das heißt: Du kannst höhere Abstraktionsgrade nutzen, ohne dafür Performance aufgeben zu müssen. Ich habe oben ein konkretes Beispiel dazu genannt. Wenn du in Haskell C++ programmierst, hast du keinen Vorteil, und wahrscheinlich eher noch einen Nachteil.

    Nein, CStoll kann eben nicht theoretisch und konzeptionell über Programmiersprachen reden. Das ist ja das Problem; deswegen exemplarisch. Wenn ich anfangen würde mit Typentheorie, würde er gar nichts mehr verstehen und mir noch mehr Blödsinn unterstellen. Mir wurde ja schon mal vorgeworfen, ich würde nur mit Fachbegriffen um mich werfen.

    Versuch's doch.

    Hab ich doch schon. Resultat war der Vorwurf, ich sei ein Dummschwätzer, der nur mit Fachbegriffen um sich wirft.

    volkard schrieb:

    ertes schrieb:

    Was genau habt ihr an dem Wort "statisch" nicht verstanden? Das ist keine statische Prüfung, sondern eine Optimierung.

    Was ich beschrieben habe, ist statisch und entspricht dem Artikel.

    "Statisch" und "zur Laufzeit(!)" vertragen sich nicht besonders gut, findest du nicht?

    Die andere Kritik kann ich zurückwerfen, Du ignorierst alles, was Du nicht sofort vestehst, und wiederholst längst widerlegte Argumente (hast die Widerlegung ja nicht verstanden). Wenn wir Dir nicht mehr antworten, liegt es nicht daran, daß Du überlegene Argumente wiederholt hättest, sondern an der Zwecklosigkeit einer Diskussion mit Dir.

    Ich bin bisher auf jedes Argument tief eingegangen. Es gibt aber Argumente von mir, auf die gar nicht eingegangen wurde, bzw. auf die CStoll als einziger erst jetzt ansatzweise eingeht. Du machst es dir nur ziemlich einfach mit deiner Behauptung. "Es ist zwecklos, mit dir zu diskutieren", ist eine billige Ausrede.

    ipsec schrieb:

    Eine direkte Kritik an Haskell habe ich aber. Bei vielen Beispielen, die ich sehe, wird denke ich mit der Eleganz übertrieben.
    Zum Beispiel hatte ich neulich diese Funktion bei Wikipedia gelesen:

    mf = (. map) . (.) . filter
    

    Das ist eine Point-Free-Implementierung von

    mf criteria operator list = filter criteria (map operator list)
    

    Die 2. Version finde ich sehr verständlich, bei der 1. bekomme ich immer noch ne Schraube im Kopf. An die Haskell-Experten: gibt es eine spezielle Technik, wie man solche Definitionen liest, oder ist das wirklich nur eine Sache der Erfahrung? Wer hätte mit der ersten Version spontan etwas anfangen können?

    Ich nicht. Du hast völlig Recht: Das ist übertriebene Abstrahierung. Ich persönlich hätte aber keine der beiden Varianten gewählt, sondern diese:

    mf :: (a -> Bool) -> (b -> a) -> [b] -> [a]
    mf criteria operator = filter criteria . map operator
    

    Zwei Unterschiede: Ich habe eine Typensignatur drin, und durch die allein ist eigentlich völlig klar, was die Funktion tut. Der zweite Unterschied ist: Ich habe nur den letzten Parameter wegabstrahiert. So sieht man relativ deutlich den Datenfluss. Das ist eigentlich Sinn und Zweck der Pointfree-Schreibweise: den Datenfluss expliziter darstellen.

    Auf jeden Fall erschließt sich mir nicht, warum jemand überhaupt auf die Idee kommt, solche Funktionen zu schreiben (also in der 1. Version). Böse betrachtet ist das reine Eleganzgeilheit wer das abstrakteste Programm schreibt.

    So ist es. Interessanterweise kommen solche Codes hauptsächlich von Leuten, die Haskell nicht produktiv einsetzen, sondern nur zum Spaß Code-Schnippsel zusammenbasteln.

    Ertes Beispiel ist übrigens wieder sowas.

    ertes schrieb:

    eq = (==)

    Natürlich versteht man das auch, die Obfuscation hält sich hier sehr in Grenzen, trotzdem ist eq a b = a == b verständlicher. Warum schreibt man das so kompakt? Doch nicht um am Ende 2 Zeichen zu sparen, oder?

    Das ist Haskell-Grundwissen. Diese Definitionen solltest du nach dem Einstieg im Blindflug lesen können. Für einen Haskell-Programmierer gibt es keinen Unterschied zwischen Funktion und Operator, daher liest sich (==) genauso wie jeder andere Name auch. Ich lese das wie Irgendwer: "eq und (==) sind identisch." Das hat mit Sparen nichts zu tun. Es macht eben direkt die Aussage, die deine Version nur indirekt macht. Du schreibst viele Haskell-Funktionen in dieser Art:

    putStrLn :: String -> IO ()
    putStrLn = hPutStrLn stdout
    


  • ertes schrieb:

    CStoll schrieb:

    ertes schrieb:

    Low-Level-Funktionen sind in Haskell genauso anzuwenden wie in C++ auch, wahrscheinlich sogar noch leichter. Allerdings hat man da eben Funktionen und Kombinatoren mit richtigen Namen statt symbolische Operatoren (die im Endeffekt auch nur Funktionen sind). Das heißt, Haskell bringt auch die guten alten peek und poke wieder zurück. Daher entsteht oft der Eindruck, dass Haskell nicht so gut für Low-Level-Programmierung geeignet sei.

    Dann ist die Fehlersicherheit wohl eher eine Frage der Didaktik als der Sprache: Haskell-Anfänger lernen erst die sicheren WEge und dann die Dummheiten, C++-Anfänger (leider) genau umgekehrt (ich gehe hier nicht näher auf JW ein)

    Du hast immer noch das Typensystem, das dir zur Seite steht (am Anfang wohl eher im Weg, bis du lernst, damit umzugehen). Im Low-Level-Bereich wird dir Haskell allerdings nicht verbieten, einen Null-Pointer zu dereferenzieren. Andererseits kommt es wiederum normalerweise nicht vor, dass du Null-Pointer überhaupt bekommst. Nehmen wir als blödes Beispiel an, du willst eine verkettete Liste im klassischen Sinne: Es gibt also eine Variable und einen Zeiger zum nächsten Glied. In C++ hätte das letzte Glied hier einen Null-Pointer. In Haskell würdest du eher Maybe (Ptr a) benutzen und somit kommt ein Null-Pointer gar nicht vor. Der Versuch, vom letzten Glied ins nächste zu springen endet in einem Typfehler zur Kompilierzeit.

    Und jetzt erkläre mir nochmal, wie man zur Kompilierzeit eine Situation verhindenr will, die erst zur Laufzeit (wenn ich tatsächlich eine konkrete Liste in der Hand halte und durch ihre Elemente iteriere) auftreten kann.
    (und komme mir bitte nicht mit "dafür ist Haskell nicht gedacht").

    So ist es. Allerdings sind viele andere Optimierungen möglich. Beispiel: Du suchst die kleinste gerade quadratische natürliche Zahl (x² mit x ∈ ℕ), die größer als 110 ist, und nehmen wir an, es gäbe keine simple Formel dafür: Du erstellst erst eine unendliche Liste von 0 beginnend aufwärts, quadrierst alle Elemente, filterst die Ergebnisliste, sodass nur gerade Zahlen übrigbleiben, dann verwirfst du so lange den Anfang der Liste, bis das erste Element größer als 110 ist. Dieses Element ist das Ergebnis. Der Code dazu:

    head . dropWhile (<= 110) . filter even . map (^2) $ [0..]

    Durch äquivalente Umformungen wird die Liste vollständig wegoptimiert und es bleibt dieselbe Schleife übrig, die du in C++ von vornherein geschrieben hättest. Das heißt, die referentielle Transparenz wird ein C++-Programm nicht schneller machen, aber sie ermöglicht dir, neue, elegante Design-Patterns zu nutzen, ohne Performance dafür aufzugeben.

    Also quasi von hinten durch die Brust ins Auge.

    Wie gesagt, mir fallen gerade keine praktisch relevanten turing-vollständigen Probleme ein, aber das heißt nicht, daß es sie nicht gibt.

    Wie sehr mich das wundert, dass dir keine einfallen. Daran haben sich schon andere Leute die Zähne ausgebissen. Wobei – ein solches Problem fällt mir ein, sogar mit Praxisrelevanz: Schreibe ein Agda-Programm, für das unentscheidbar ist, ob es terminiert. 😉

    Du hast doch selber erklärt, daß Agda nicht mächtig genug ist, um solche unentscheidbaren Programme zu schreiben, also verzichte ich mal darauf.
    (btw, der Grund warum mir kein Problem einfällt ist wohl, daß mein Studium schon eine Weile her ist - und in der Praxis habe ich mit ganz anderen Aufgaben zu tun als mich um berechenbarkeitstheoretische Fragen zu kümmern)

    - wenn es kompiliert, funktioniert es auch: halte ich immr noch für fragwürdig

    Dazu gibt es sogar einen Wiki-Eintrag.

    Selbst der Beitrag sagt nicht aus, daß du mit Haskell alle Fehler zur Compilezeit verhindern kannst.

    - besser für Web-Anwendungen: eine Zange ist auch besser zum Nägel in die Wand schlagen als ein Schraubendreher - trotzdem nimmt man dafür einen Hammer

    Und was ist der Hammer? Etwa PHP? Python? Ruby? Das kannst du doch gar nicht einschätzen, wie du selber indirekt zugegeben hast. Tu doch nicht so, als würdest du alle Web-Frameworks der Welt kennen, denn mindestens eines davon kennst du nicht: Yesod.

    Ich bin kein Web-Entwickler (jedenfalls nicht hauptsächlich, aber ich weiß selber, daß meine Werkzeuge nicht die besten dafür sind), deshalb kann ich dazu nichts sagen.

    Ich nicht. Du hast völlig Recht: Das ist übertriebene Abstrahierung. Ich persönlich hätte aber keine der beiden Varianten gewählt, sondern diese:

    mf :: (a -> Bool) -> (b -> a) -> [b] -> [a]
    mf criteria operator = filter criteria . map operator
    

    Zwei Unterschiede: Ich habe eine Typensignatur drin, und durch die allein ist eigentlich völlig klar, was die Funktion tut. Der zweite Unterschied ist: Ich habe nur den letzten Parameter wegabstrahiert. So sieht man relativ deutlich den Datenfluss. Das ist eigentlich Sinn und Zweck der Pointfree-Schreibweise: den Datenfluss expliziter darstellen.

    Die Signatur gibt zumindest einen Ansatzpunkt, was deine Funktion machen soll, aber da bleiben immer noch zu viele Freiheiten, die damit nicht beantwortet werden. Kommen die Ergebnisse in der selben Reihenfolge vor, die die ursprüngliche Liste vorgegeben hat? Auf welche Weise wird das Filter-Kriterium angewendet (bedeutet true jetzt "übernimm das Argument in die Ergebnisliste" oder "verwirf das Argument")? Für diese Fragen benötigt man doch wieder die genaue Definition.

    Analog können wir als Beispiel diese Signatur verwenden:
    [code}f :: Ordered a => [a] -> [a][/code]
    Kannst du mir ohne die Definition sagen, was beim Aufruf f [5 7 11 3 29] herauskommen wird?


Anmelden zum Antworten