Por que `print <$> (print" hello ")` imprime "hello"?

14

Ao calcular IO (IO ()), ambos (IO ())e ()é calculado, então por que

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

impressão

"Hello, World!"

não

IO "Hello, World!" -- ??
"Hello, World!"
qexy
fonte
3
Basicamente, fmap print (print "Hello World")aplica seu primeiro parâmetro, a printfunção, ao resultado de print "Hello World". Isso é simplesmente o equivalente a invocar print ()após a print "Hello World"ação ser executada.
Redu
@ Redu Isso está correto, mas observe que a invocação de print ()nunca é avaliada, nem sua ação é executada (que seria impressa ()em stdout). Portanto, "invocar print ()após ..." é um pouco enganador (IMO).
chi

Respostas:

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

é equivalente, graças às leis da mônada, a

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

Agora, printsempre retorna ()como resultado, portanto todo o código é equivalente a

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

Finalmente, o resultado de mainé simplesmente descartado. Ou seja, a última linha pode ser return (putStrLn "this is ignored")e ter o mesmo efeito.

Portanto, o código executará apenas o primeiro print "Hello, World!" .

Eu recomendaria que você sempre definisse main :: IO (). Haskell nos permite declararmain :: IO AnyTypeHere , mas isso é confuso (IMO).

Também recomendo que você use putStrLn, e não printimprima, porque o último citará e escapará de toda a string.

chi
fonte
5
Eu acrescentaria que f <$> a ≡ a >>= \r -> return $ f rnão é apenas uma coisa específica para esta situação, mas vale para qualquer mônada.
precisa saber é o seguinte