Vor kurzem habe ich angefangen, Haskell zu lernen, weil ich mein Wissen über funktionale Programmierung erweitern wollte und ich muss sagen, dass ich es bis jetzt wirklich liebe. Die Ressource, die ich derzeit verwende, ist der Kurs 'Haskell Fundamentals Part 1' über Pluralsight. Leider habe ich einige Schwierigkeiten, ein bestimmtes Zitat des Dozenten über den folgenden Code zu verstehen, und ich hatte gehofft, dass Sie etwas Licht in das Thema bringen können.
Begleitcode
helloWorld :: IO ()
helloWorld = putStrLn "Hello World"
main :: IO ()
main = do
helloWorld
helloWorld
helloWorld
Das Zitat
Wenn Sie dieselbe E / A-Aktion mehrmals in einem Do-Block haben, wird sie mehrmals ausgeführt. Dieses Programm druckt also die Zeichenfolge 'Hello World' dreimal aus. Dieses Beispiel zeigt, dass putStrLn
es sich nicht um eine Funktion mit Nebenwirkungen handelt. Wir rufen die putStrLn
Funktion einmal auf, um die helloWorld
Variable zu definieren . Wenn putStrLn
das Drucken der Zeichenfolge einen Nebeneffekt hätte, würde sie nur einmal gedruckt, und die helloWorld
im Haupt-Do-Block wiederholte Variable hätte keine Auswirkung.
In den meisten anderen Programmiersprachen würde ein Programm wie dieses 'Hello World' nur einmal drucken, da das Drucken beim Aufrufen der putStrLn
Funktion erfolgen würde . Diese subtile Unterscheidung stolpert oft über Anfänger. Denken Sie also ein wenig darüber nach und stellen Sie sicher, dass Sie verstehen, warum dieses Programm 'Hello World' dreimal druckt und warum es nur einmal druckt, wenn die putStrLn
Funktion den Druck als Nebeneffekt ausführt.
Was ich nicht verstehe
Für mich ist es fast selbstverständlich, dass die Zeichenfolge 'Hello World' dreimal gedruckt wird. Ich nehme die helloWorld
Variable (oder Funktion?) Als eine Art Rückruf wahr, der später aufgerufen wird. Was ich nicht verstehe ist, wie wenn putStrLn
ein Nebeneffekt dazu führen würde, dass die Zeichenfolge nur einmal gedruckt wird. Oder warum es nur einmal in anderen Programmiersprachen gedruckt wird.
Nehmen wir im C # -Code an, ich würde annehmen, dass es so aussehen würde:
C # (Geige)
using System;
public class Program
{
public static void HelloWorld()
{
Console.WriteLine("Hello World");
}
public static void Main()
{
HelloWorld();
HelloWorld();
HelloWorld();
}
}
Ich bin sicher, ich übersehen etwas ganz Einfaches oder interpretiere seine Terminologie falsch. Jede Hilfe wäre sehr dankbar.
BEARBEITEN:
Vielen Dank für Ihre Antworten! Ihre Antworten haben mir geholfen, diese Konzepte besser zu verstehen. Ich denke, es hat noch nicht vollständig geklickt, aber ich werde das Thema in Zukunft noch einmal aufgreifen, danke!
helloWorld
eine Konstante wie ein Feld oder eine Variable in C # vor. Es gibt keinen Parameter, auf den angewendet wirdhelloWorld
.putStrLn
hat keine Nebenwirkung; Es wird einfach eine E / A-Aktion zurückgegeben, dieselbe E / A-Aktion für das Argument,"Hello World"
unabhängig davon, wie oft Sie aufrufenputStrLn
.helloworld
der Fall wäre , wäre dies keine Aktion, die gedruckt wirdHello world
. es würde durch den Wert zurückgeführt werden ,putStrLn
nachdem es gedrucktHello World
(nämlich()
).helloWorld = Console.WriteLine("Hello World");
. Sie enthalten nurConsole.WriteLine("Hello World");
dieHelloWorld
Funktion, die bei jedemHelloWorld
Aufruf ausgeführt werden soll. Denken Sie jetzt darüber nach, washelloWorld = putStrLn "Hello World"
machthelloWorld
. Es wird einer E / A-Monade zugewiesen, die enthält()
. Sobald Sie es daran gebunden haben, führt>>=
es erst dann seine Aktivität aus (etwas drucken) und zeigt Sie()
auf der rechten Seite des Bindungsoperators an.Antworten:
Es wäre wahrscheinlich einfacher zu verstehen, was der Autor bedeutet, wenn wir
helloWorld
als lokale Variable definieren:was Sie mit diesem C # -ähnlichen Pseudocode vergleichen könnten:
Dh in C #
WriteLine
ist eine Prozedur, die ihr Argument druckt und nichts zurückgibt. In HaskellputStrLn
ist dies eine Funktion, die eine Zeichenfolge verwendet und Ihnen eine Aktion gibt, die diese Zeichenfolge druckt, wenn sie ausgeführt wird. Es bedeutet, dass es absolut keinen Unterschied zwischen dem Schreiben gibtund
Abgesehen davon ist der Unterschied in diesem Beispiel nicht besonders tiefgreifend. Es ist also in Ordnung, wenn Sie nicht genau verstehen, worauf der Autor in diesem Abschnitt abzielt, und erst einmal weitermachen.
Es funktioniert ein bisschen besser, wenn Sie es mit Python vergleichen
Der Punkt hier ist , dass IO Aktionen in Haskell sind „echte“ Werte , die in weiteren „Callbacks“ oder etwas dergleichen eingewickelt nicht brauchen werden , um sie daran zu hindern, die Ausführung - besser gesagt, der einzige Weg zu tun , sie zu bekommen , ist auszuführen , um sie an einem bestimmten Ort zu platzieren (dh irgendwo drinnen
main
oder ein Faden ist entstandenmain
).Dies ist nicht nur ein Salon-Trick, sondern hat auch einige interessante Auswirkungen auf das Schreiben von Code (zum Beispiel ist dies ein Teil des Grundes, warum Haskell keine der üblichen Kontrollstrukturen benötigt, die Sie kennen mit aus imperativen Sprachen und kann damit davonkommen, stattdessen alles in Bezug auf Funktionen zu tun), aber auch hier würde ich mir keine Sorgen machen (Analogien wie diese klicken nicht immer sofort)
quelle
Es ist möglicherweise einfacher, den beschriebenen Unterschied zu erkennen, wenn Sie eine Funktion verwenden, die tatsächlich etwas tut, anstatt
helloWorld
. Denken Sie an Folgendes:Dadurch wird dreimal "Ich füge 2 und 3 hinzu" ausgedruckt.
In C # können Sie Folgendes schreiben:
Welches würde nur einmal drucken.
quelle
Wenn die Bewertung von
putStrLn "Hello World"
Nebenwirkungen hätte, würde die Nachricht nur einmal gedruckt.Wir können dieses Szenario mit dem folgenden Code approximieren:
unsafePerformIO
nimmt eineIO
Handlung vor und "vergisst", dass es sich um eineIO
Handlung handelt, die sich von der üblichen Reihenfolge löst, die durch die Zusammensetzung derIO
Handlungen auferlegt wird, und die Wirkung gemäß den Launen der faulen Bewertung stattfinden lässt (oder nicht).evaluate
nimmt einen reinen Wert und stellt sicher, dass der Wert immer dann ausgewertet wird, wenn die resultierendeIO
Aktion ausgewertet wird - was für uns der Fall sein wird, weil er auf dem Weg von liegtmain
. Wir verwenden es hier, um die Auswertung einiger Werte mit der Ausführung des Programms zu verbinden.Dieser Code gibt "Hello World" nur einmal aus. Wir behandeln
helloWorld
als reinen Wert. Dies bedeutet jedoch, dass alleevaluate helloWorld
Anrufe gemeinsam genutzt werden. Und warum nicht? Es ist schließlich ein reiner Wert, warum sollte er unnötig neu berechnet werden? Die ersteevaluate
Aktion "knallt" den "versteckten" Effekt und die späteren Aktionen bewerten nur das Ergebnis()
, was keine weiteren Effekte verursacht.quelle
unsafePerformIO
in dieser Phase des Lernens von Haskell absolut nicht verwenden sollten . Der Name enthält aus einem bestimmten Grund "unsicher", und Sie sollten ihn nicht verwenden, es sei denn, Sie können (und haben) die Auswirkungen seiner Verwendung im Kontext sorgfältig abwägen. Der Code, den Danidiaz in die Antwort eingefügt hat, erfasst perfekt die Art von unintuitivem Verhalten, das daraus resultieren kannunsafePerformIO
.Es gibt ein Detail zu beachten: Sie rufen die
putStrLn
Funktion beim Definieren nur einmal aufhelloWorld
. In dermain
Funktion verwenden Sie den Rückgabewert nurputStrLn "Hello, World"
dreimal.Der Vortragende sagt, dass
putStrLn
Anruf keine Nebenwirkungen hat und es wahr ist. Aber schauen Sie sich die Art von anhelloWorld
- es ist eine E / A-Aktion.putStrLn
schafft es einfach für dich. Später verketten Sie 3 davon mit demdo
Block, um eine weitere E / A-Aktion zu erstellenmain
. Später, wenn Sie Ihr Programm ausführen, wird diese Aktion ausgeführt. Dort liegen die Nebenwirkungen.Der Mechanismus, der dieser Basis zugrunde liegt - Monaden . Mit diesem leistungsstarken Konzept können Sie einige Nebenwirkungen wie das Drucken in einer Sprache verwenden, die Nebenwirkungen nicht direkt unterstützt. Sie verketten nur einige Aktionen und diese Kette wird beim Start Ihres Programms ausgeführt. Sie müssen dieses Konzept genau verstehen, wenn Sie Haskell ernsthaft einsetzen möchten.
quelle