Dlaczego `print <$> (print„ hello ”)„ print ”hello?

14

Przy obliczaniu IO (IO ())zarówno (IO ())i ()jest obliczane, więc dlaczego

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

wydrukować

"Hello, World!"

nie

IO "Hello, World!" -- ??
"Hello, World!"
qexy
źródło
3
Zasadniczo fmap print (print "Hello World")stosuje swój pierwszy parametr, printfunkcję, do wyniku print "Hello World". Jest to po prostu odpowiednik wywoływania print ()po wykonaniu print "Hello World"akcji.
Redu
@Redu To prawda, ale pamiętaj, że wywołanie print ()nigdy nie jest oceniane, ani wykonywane jest jego działanie (które wypisuje się ()na standardowe wyjście). Tak więc „wywoływanie print ()po ...” jest nieco mylące (IMO).
chi

Odpowiedzi:

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

jest równoznaczny, dzięki prawom monady, z

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

Teraz printzawsze zwraca ()wynik, więc cały kod jest równoważny

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

Ostatecznie wynik mainjest po prostu odrzucany. Oznacza to, że ostatnia linia może być return (putStrLn "this is ignored")i mieć ten sam efekt.

Dlatego kod wykona tylko pierwszy print "Hello, World!".

Polecam, abyś zawsze określał main :: IO (). Haskell pozwala nam deklarować main :: IO AnyTypeHere, ale jest to (IMO) mylące.

Poleciłbym również, abyś używał putStrLn, a nie printdrukował napisów, ponieważ ten ostatni zacytuje i ucieknie cały napis.

chi
źródło
5
Dodam, że f <$> a ≡ a >>= \r -> return $ f rnie jest to tylko konkretna sytuacja w tej sytuacji, ale w rzeczywistości dotyczy każdej monady.
leftaroundabout