Warum druckt `print <$> (drucke" Hallo ")` drucke "Hallo"?

14

Bei der Berechnung IO (IO ())werden beide (IO ())und ()berechnet, warum also?

main :: IO (IO ())
main = print <$> (print "Hello, World!")

drucken

"Hello, World!"

nicht

IO "Hello, World!" -- ??
"Hello, World!"
qexy
quelle
3
Grundsätzlich fmap print (print "Hello World")wird der erste Parameter, die printFunktion, auf das Ergebnis von angewendet print "Hello World". Dies entspricht einfach dem Aufrufen print ()nach dem print "Hello World"Ausführen einer Aktion.
Redu
@Redu Das ist richtig, aber beachten Sie, dass der Aufruf von print ()nie ausgewertet oder seine Aktion ausgeführt wird (was ()auf stdout gedruckt würde ). "Aufrufen print ()nach ..." ist also etwas irreführend (IMO).
Chi

Antworten:

21
main :: IO (IO ())
main = print <$> (print "Hello, World!")

ist dank der Monadengesetze gleichbedeutend mit

main :: IO (IO ())
main = do 
   result <- print "Hello, World!"
   return (print result)

Gibt jetzt printimmer ()als Ergebnis zurück, sodass der gesamte Code äquivalent zu ist

main :: IO (IO ())
main = do 
   _ <- print "Hello, World!"
   return (print ())

Schließlich wird das Ergebnis von maineinfach verworfen. Das heißt, die letzte Zeile könnte return (putStrLn "this is ignored")den gleichen Effekt haben und haben.

Daher führt der Code nur den ersten aus print "Hello, World!".

Ich würde empfehlen, dass Sie immer definieren main :: IO (). Haskell erlaubt uns zu deklarieren main :: IO AnyTypeHere, aber das ist (IMO) verwirrend.

Ich würde Ihnen auch empfehlen putStrLn, printZeichenfolgen zu verwenden und nicht zu drucken, da letztere die gesamte Zeichenfolge zitieren und maskieren.

Chi
quelle
5
Ich würde hinzufügen, dass dies f <$> a ≡ a >>= \r -> return $ f rnicht nur eine spezifische Sache in dieser Situation ist, sondern tatsächlich für jede Monade gilt.
links um den