Haskell



  • 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?



  • 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").

    Kurz zum Maybe (Ptr a) (aus meiner Sicht).

    Maybe ist folgendermaßen definiert:
    data Maybe a = Just a | Nothing

    D.h. Maybe (Ptr a) ist entweder Just (Ptr a) oder Nothing , d.h. falls eine C-Funktion einen ungültigen Pointer zurückgibt ( Ptr a ist teil des Foreign Function Interface) wird in Haskell daraus Nothing . Jetzt könntest du eine Funktion f schreiben, die folgendermaßen aussieht:

    f :: Maybe (Ptr a) -> a 
    f Just (Ptr a) = -- Version für einen gütligen Zeiger
    f Nothing = -- Version für einen ungültigen Zeiger
    

    Du kannst also gar nicht mit ungültigen Zeigern hantieren.

    Das könnte man auch in C++ mit Klassen und Überladung irgendwie erreichen, das macht aber niemand, weil das unnötig ineffizient ist. Stattdessen macht man eben manuelles Pattern-Matching. Mir gefällt die Haskell-Version allerdings besser.

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

    Du verlangst von Haskell, dass es alle Fehler zur Compilezeit verhindert? Bleib realistisch.
    Es verhindert einige (siehe Maybe (Ptr a) ), aber wie soll es denn alle verhindern?



  • was gibt eigentlich Haskell aus, wenn du den Definitionsbereich einer partiell definierten Funktion verlässt? (d.h. wenn ich für den "Nothing"-Teil nichts sinnvolles machen kann und den Teil offen lasse)

    Irgendwer schrieb:

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

    Du verlangst von Haskell, dass es alle Fehler zur Compilezeit verhindert? Bleib realistisch.
    Es verhindert einige (siehe Maybe (Ptr a) ), aber wie soll es denn alle verhindern?

    Ich verlange von Haskell gar nichts. Aber von den Haskell-Vertretern kam die Aussage, daß compilierte Programme fehlerfrei sind. In dem Artikel wurde gezeigt, wie man durch eine Umstellung der Semantik einige Fehler vermeiden kann - aber deswegen kann man trotzdem immer noch genug Mist bauen.



  • was gibt eigentlich Haskell aus, wenn du den Definitionsbereich einer partiell definierten Funktion verlässt? (d.h. wenn ich für den "Nothing"-Teil nichts sinnvolles machen kann und den Teil offen lasse)

    Entweder gibt's einen Pattern-Matching Fehler, d.h. du hast den Nothing Teil komplett weggelassen, oder du wirfst eine Exception oder benutzt error - genauso wie du es in C++ auch machen wirst. Ganz schön kann auch die Maybe-Monade sein, bei der nur weitergemacht wird, solange eine Funktion Just a zurückgibt, ansonsten wird direkt abgebrochen - und das alles ohne ein direktes "if" zu schreiben, da sich das alles im Bind-Operator >>= abspielt.

    Du kannst auch am immer gegen ein beliebiges Pattern matchen:

    data Bar = Foo | Bar | Baz
    f :: Bar -> Int
    f Foo = 1
    f Bar = 2
    f _   = error "Invalid value"
    

    Aber natürlich werden partiell definierte Funktionen vermieden wo es nur geht.

    Aber von den Haskell-Vertretern kam die Aussage, daß compilierte Programme fehlerfrei sind.

    Ja, mit der Aussage hat ertes wohl die Gemüter hier ziemlich in Wallungen gebracht. Ich bin kein erfahrener Haskell-Programmierer, habe aber von erfahrenen Haskellern schon häufig die Aussage gehört und gelesen, dass Programme häufig beim ersten kompilieren laufen.

    Aber vielleicht sind das auch einfach alles schlechte imperative Programmierer und Lüger noch obendrauf. 😉



  • ertes schrieb:

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

    Durch äquivalente Umformungen wird die Liste vollständig wegoptimiert

    und in Forth kann man - bei laufender Compilierung eines Wortes (!) - den Compiler ausschalten, den Interpreter anschalten, die fragliche Zahl (min x mit 2|x und x^2>=110) vom Interpreter berechnen lassen, das Ergebnis festverdrahtet in den Maschinencode einfügen, den Compiler wieder anschalten und den Rest des Programms compilieren.

    Da wird aber nicht so ein Buhei drum gemacht wie in der funktionalen Programmierung.



  • Das Konzept "Beispiel" hast du auch nicht verstanden, oder?

    In Haskell hindert dich niemand daran obige Funktion in GHCi auszuführen und das Ergebnis direkt in den Quelltext zu schreiben. Genauso wie dich daran in C++ oder Forth keiner hindert. Den großen Vorteil in der Unterbrechbarkeit des Kompiliervorgangs sehe ich nicht...



  • Irgendwer schrieb:

    Den großen Vorteil in der Unterbrechbarkeit des Kompiliervorgangs sehe ich nicht...

    Das ist mal wieder das Blub-Paradox 🙂 Oder du siehst den Wald vor Bäumen nicht, das ist nämlich nichts anderes als Metaprogrammierung.



  • !rr!rr_. schrieb:

    und in Forth kann man - bei laufender Compilierung eines Wortes (!) - den Compiler ausschalten, den Interpreter anschalten, die fragliche Zahl (min x mit 2|x und x^2>=110) vom Interpreter berechnen lassen, das Ergebnis festverdrahtet in den Maschinencode einfügen, den Compiler wieder anschalten und den Rest des Programms compilieren.

    Zeigst Du auch hier bitte den Code, der genau das schafft?



  • Oder du siehst den Wald vor Bäumen nicht, das ist nämlich nichts anderes als Metaprogrammierung.

    So kann man das natürlich auch sehen. Ich kenne mich mit Forth nicht aus, weswegen die Frage bestehen bleibt, ob der Compiler das selber macht - also Werte (auch komplexere wie diese Funktion) vorab berechnet - oder ob ich da selber die Kompilation unterbrechen muss (so wie es !rr!rr_. beschrieben hat).



  • volkard schrieb:

    Zeigst Du auch hier bitte den Code, der genau das schafft?

    : foo [ bar ] literal ... ;
    

    "[" schaltet den Compiler aus und den Interpreter an, "]" bewirkt das Umgekehrte, und "literal" baut das vom Interpreter errechnete Ergebnis von bar festverdrahtet in das Compilat ein.

    "bar" ist ein Wort, das das gefragte min x berechnet und ist dem geneigten Leser als Übung überlassen 😃



  • komplettes Beispiel für die wo kein Forth können:

    : bar 110 1 do i dup * 110 >= i 2 mod 0= and if i unloop exit then loop ;
    : foo [ bar ] literal ;
    foo . cr  ( "12" )
    


  • Danke. Habe anscheinend viel Forth vergessen.


Anmelden zum Antworten