Der Wert main
bezeichnet ist ein unendliches Programm:
main = do
line <- getLine
putStrLn line
line <- getLine
putStrLn line
line <- getLine
putStrLn line
line <- getLine
putStrLn line
line <- getLine
putStrLn line
line <- getLine
putStrLn line
...
Aber es ist als rekursive Struktur im Speicher dargestellt, die selbst verweist. Diese Darstellung ist endlich, es sei denn, jemand versucht, das Ganze zu entfalten, um eine nicht-rekursive Darstellung des gesamten Programms zu erhalten - , die niemals beenden würde.
Aber genauso wie Sie wahrscheinlich herausfinden können, wie Sie das oben beschriebene Infinite-Programm ausführen, ohne darauf zu warten, dass ich Ihnen "alles" sage, kann Haskells Laufzeitsystem herausfinden, wie man main
ausführt, ohne die Rekursion zu entwickeln im Voraus.
Haskells lazy evaluation ist eigentlich mit dem Runtime-System der Ausführung des main
IO Programm verschachtelt, so funktioniert dies auch für eine Funktion, die eine IO
Aktion zurück, die rekursiv die Funktion aufruft, wie:
main = foo 1
foo :: Integer -> IO()
foo x = do
print x
foo (x + 1)
Hier foo 1
ist kein rekursiver Wert (enthält foo 2
, nicht foo 1
), aber es ist immer noch ein unendliches Programm.Dies funktioniert jedoch gut, da das mit foo 1
gekennzeichnete Programm nur auf Abruf erzeugt wird; Es kann erzeugt werden, wie die Ausführung des Laufzeitsystems von main
geht.
Standardmäßig bedeutet Haskells Faulheit, dass nichts ausgewertet wird, bis es benötigt wird, und dann nur "gerade genug", um über den aktuellen Block hinauszukommen. Letztendlich kommt die Quelle aller "Bedürfnisse" in "bis es gebraucht wird" vom Laufzeitsystem, das wissen muss, was der nächste Schritt im main
Programm ist, damit es es ausführen kann. Aber es ist immer nur die nächste Schritt; Der Rest des Programms kann danach unbewertet bleiben, bis der nächste Schritt vollständig ausgeführt wurde. So können unendlich viele Programme ausgeführt werden und nützliche Arbeit leisten, solange es immer nur eine begrenzte Menge an Arbeit ist, um "einen weiteren Schritt" zu erzeugen.
Wie alles andere in Haskell, ist der IO-Wert nicht streng und wird daher nur nach Bedarf ausgewertet. – Rufflewind
Alle Typen - einschließlich 'IO()' - haben einen zusätzlichen Wert namens [bottom] (https://wiki.haskell.org/Bottom). Also "main" ist technisch "zurück" ein "IO()"; es ist nur, dass es die langweiligste 'IO()' zurückgibt: ⊥. Haskell kann niemals das "ganze" "Haupt" bewerten, aber aufgrund der Nicht-Strenge kann es versuchen. –
Wenn Sie denken, dass Sie jetzt verwirrt sind, warten Sie einfach, bis Sie etwas über 'fixIO: :(a-> IO a) -> IO a' erfahren. – dfeuer