Чому `print <$> (надрукувати" привіт ")` друкує "привіт"?


14

При обчисленні IO (IO ())і те, (IO ())і ()обчислюється, так навіщо

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

друк

"Hello, World!"

ні

IO "Hello, World!" -- ??
"Hello, World!"

3
В основному fmap print (print "Hello World")застосовується перший результат, printфункція, до результату print "Hello World". Це просто еквівалент виклику print ()після виконання print "Hello World"дії.
Redu

@Redu Це правильно, але зауважте, що виклик print ()ніколи не оцінюється, ані його дії виконуються (що було б надруковано ()у stdout). Отже, "виклик print ()після ..." трохи вводить в оману (IMO).
чі

Відповіді:


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

завдяки законам монади рівнозначний:

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

Тепер, printзавжди повертається ()як результат, так що весь код еквівалентний

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

Нарешті, результат mainпросто відкидається. Тобто останній рядок міг би бути return (putStrLn "this is ignored")і мати такий же ефект.

Отже, код виконає лише перший print "Hello, World!".

Я б рекомендував вам завжди визначати main :: IO (). Haskell дозволяє нам заявляти main :: IO AnyTypeHere, але це (IMO) заплутано.

Я також рекомендував би вам використовувати putStrLn, а не printдрукувати рядки, оскільки останні будуть цитувати та уникати цілу нитку.


5
Додам, що f <$> a ≡ a >>= \r -> return $ f rце не просто конкретна річ у цій ситуації, але насправді справедлива для будь-якої монади.
leftaroundabout
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.