Mengapa `print <$> (print" hello ")` print "hello"?

14

Saat menghitung IO (IO ()), keduanya (IO ())dan ()dihitung, jadi mengapa

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

mencetak

"Hello, World!"

tidak

IO "Hello, World!" -- ??
"Hello, World!"
qexy
sumber
3
Pada dasarnya fmap print (print "Hello World")menerapkan parameter pertama itu, printfungsi, ke hasil print "Hello World". Itu hanya setara dengan memohon print ()setelah print "Hello World"tindakan dilakukan.
Redu
@Redu Itu benar, tetapi perhatikan bahwa permohonan print ()tidak pernah dievaluasi, atau tindakannya dilakukan (yang akan mencetak ()pada stdout). Jadi, "memanggil print ()setelah ..." agak menyesatkan (IMO).
chi

Jawaban:

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

setara, berkat hukum monad, untuk

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

Sekarang, printselalu kembali ()sebagai hasilnya, sehingga seluruh kode setara dengan

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

Akhirnya, hasil maindibuang begitu saja. Artinya, baris terakhir bisa return (putStrLn "this is ignored")dan memiliki efek yang sama.

Karenanya kode hanya akan menjalankan yang pertama print "Hello, World!".

Saya akan merekomendasikan bahwa Anda selalu mendefinisikan main :: IO (). Haskell memungkinkan kita untuk menyatakan main :: IO AnyTypeHere, tetapi ini (IMO) membingungkan.

Saya juga merekomendasikan Anda menggunakan putStrLn, dan tidak printmencetak string, karena yang terakhir akan mengutip dan melarikan diri dari seluruh string.

chi
sumber
5
Saya menambahkan bahwa f <$> a ≡ a >>= \r -> return $ f rini bukan hanya hal yang spesifik untuk situasi ini, tetapi sebenarnya berlaku untuk monad apa pun.
leftaroundabout