Почему `print <$> (print" hello ")` печатает "hello"?

14

При расчете IO (IO ())как (IO ())и так ()рассчитывается, так почему

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

Распечатать

"Hello, World!"

не

IO "Hello, World!" -- ??
"Hello, World!"
qexy
источник
3
В основном fmap print (print "Hello World")применяет свой первый параметр, printфункцию, к результату print "Hello World". Это просто эквивалент вызова print ()после выполнения print "Hello World"действия.
Redu
@Redu Это правильно, но обратите внимание, что вызов print ()никогда не оценивается, и не выполняется его действие (которое будет выводиться ()на стандартный вывод). Таким образом, «вызов print ()после ...» немного вводит в заблуждение (ИМО).
Чи

Ответы:

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это не просто специфическая вещь в этой ситуации, а фактически для любой монады.
оставил около