¿Por qué `print <$> (print“ hello ”)` print “hello”?

14

Al calcular IO (IO ()), ambos (IO ())y ()se calculan, entonces ¿por qué?

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

impresión

"Hello, World!"

no

IO "Hello, World!" -- ??
"Hello, World!"
qexy
fuente
3
Básicamente fmap print (print "Hello World")aplica su primer parámetro, la printfunción, al resultado de print "Hello World". Eso es simplemente el equivalente a invocar print ()después de realizar una print "Hello World"acción.
Reducido el
@Redu Eso es correcto, pero tenga en cuenta que la invocación de print ()nunca se evalúa, ni se realiza su acción (que se imprimiría ()en stdout). Entonces, "invocar print ()después de ..." es un poco engañoso (IMO).
chi

Respuestas:

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

es equivalente, gracias a las leyes de mónada, a

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

Ahora, printsiempre se devuelve ()como resultado, por lo que todo el código es equivalente a

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

Finalmente, el resultado de mainsimplemente se descarta. Es decir, la última línea podría ser return (putStrLn "this is ignored")y tener el mismo efecto.

Por lo tanto, el código solo ejecutará el primero print "Hello, World!".

Yo recomendaría que siempre definas main :: IO (). Haskell nos permite declarar main :: IO AnyTypeHere, pero esto es (IMO) confuso.

También recomendaría que use putStrLn, y no printpara imprimir cadenas, ya que este último citará y escapará de toda la cadena.

chi
fuente
55
Agregaría que f <$> a ≡ a >>= \r -> return $ f rno es solo una cosa específica de esta situación, sino que en realidad vale para cualquier mónada.
Leftaroundabout